From 20413f17bb75905b2604c0a8f5b2489cadf047c3 Mon Sep 17 00:00:00 2001 From: Tom Musgrove Date: Wed, 10 Jan 2007 11:37:19 +0000 Subject: [PATCH] = dxf update = update by the script author - may have killed some changes cambo did to the previous version but the layout of the script is too significantly different to merge them. --- release/scripts/bpymodules/dxfReader.py | 84 +- release/scripts/import_dxf.py | 2313 +++++++++++++++++------ 2 files changed, 1837 insertions(+), 560 deletions(-) diff --git a/release/scripts/bpymodules/dxfReader.py b/release/scripts/bpymodules/dxfReader.py index f0635e00613..d4a39cf63d6 100644 --- a/release/scripts/bpymodules/dxfReader.py +++ b/release/scripts/bpymodules/dxfReader.py @@ -5,7 +5,7 @@ """ # -------------------------------------------------------------------------- -# DXF Reader v0.8 by Ed Blake (AKA Kitsu) +# DXF Reader v0.9 by Ed Blake (AKA Kitsu) # -------------------------------------------------------------------------- # ***** BEGIN GPL LICENSE BLOCK ***** # @@ -26,11 +26,40 @@ # ***** END GPL LICENCE BLOCK ***** # -------------------------------------------------------------------------- -# development -#import dxfImportObjects -#reload(dxfImportObjects) -from dxfImportObjects import * +#from dxfImportObjects import * + +class Object: + """Empty container class for dxf objects""" + + def __init__(self, _type='', block=False): + """_type expects a string value.""" + self.type = _type + self.name = '' + self.data = [] + + def __str__(self): + if self.name: + return self.name + else: + return self.type + + def __repr__(self): + return str(self.data) + + def get_type(self, kind=''): + """Despite the name, this method actually returns all objects of type 'kind' from self.data.""" + if type: + objects = [] + for item in self.data: + if type(item) != list and item.type == kind: + # we want this type of object + objects.append(item) + elif type(item) == list and item[0] == kind: + # we want this type of data + objects.append(item[1]) + return objects + class InitializationError(Exception): pass @@ -72,6 +101,33 @@ class StateMachine: else: handler = newState +def get_name(data): + """Get the name of an object from its object data. + + Returns a pair of (data_item, name) where data_item is the list entry where the name was found + (the data_item can be used to remove the entry from the object data). Be sure to check + name not None before using the returned values! + """ + value = None + for item in data: + if item[0] == 2: + value = item[1] + break + return item, value + +def get_layer(data): + """Expects object data as input. + + Returns (entry, layer_name) where entry is the data item that provided the layer name. + """ + value = None + for item in data: + if item[0] == 8: + value = item[1] + break + return item, value + + def convert(code, value): """Convert a string to the correct Python type based on its dxf code. code types: @@ -144,9 +200,9 @@ def handleObject(infile): def handleTable(table, infile): """Special handler for dealing with nested table objects.""" - item, name, item_index = get_name(table.data) + item, name = get_name(table.data) if name: # We should always find a name - del table.data[item_index] + table.data.remove(item) table.name = name.lower() # This next bit is from handleObject # handleObject should be generalized to work with any section like object @@ -165,10 +221,10 @@ def handleTable(table, infile): def handleBlock(block, infile): """Special handler for dealing with nested table objects.""" - item, name, item_index = get_name(block.data) + item, name = get_name(block.data) if name: # We should always find a name - del block.data[item_index] - block.name = name.lower() + block.data.remove(item) + block.name = name # This next bit is from handleObject # handleObject should be generalized to work with any section like object while 1: @@ -271,7 +327,7 @@ def error(cargo): print err return False -def readDXF(filename): +def readDXF(filename, objectify): """Given a file name try to read it as a dxf file. Output is an object with the following structure @@ -305,12 +361,12 @@ def readDXF(filename): if drawing: drawing.name = filename for obj in drawing.data: - item, name, item_index = get_name(obj.data) + item, name = get_name(obj.data) if name: - del obj.data[item_index] + obj.data.remove(item) obj.name = name.lower() setattr(drawing, name.lower(), obj) - # Call the objectify function from dxfImportObjects to cast + # Call the objectify function to cast # raw objects into the right types of object obj.data = objectify(obj.data) #print obj.name diff --git a/release/scripts/import_dxf.py b/release/scripts/import_dxf.py index 535d762ffaa..a4e4e69b366 100644 --- a/release/scripts/import_dxf.py +++ b/release/scripts/import_dxf.py @@ -7,7 +7,7 @@ # Tooltip: 'Import DXF file.' # """ __author__ = 'Kitsu (Ed Blake)' -__version__ = '0.8 1/2007' +__version__ = '0.9 1/2007' __url__ = ["elysiun.com", "BlenderArtists.org"] __email__ = ["Kitsune_e@yahoo.com"] __bpydoc__ = """\ @@ -48,7 +48,7 @@ Notes:
""" # -------------------------------------------------------------------------- -# DXF Import v0.8 by Ed Blake (AKA Kitsu) +# DXF Import v0.9 by Ed Blake (AKA Kitsu) # -------------------------------------------------------------------------- # ***** BEGIN GPL LICENSE BLOCK ***** # @@ -72,23 +72,12 @@ Notes:
import Blender from Blender import * Sys = sys -try: - from dxfReader import readDXF -except ImportError: - import sys - curdir = Sys.dirname(Blender.Get('filename')) - sys.path.append(curdir) -# development -#import dxfReader -#reload(dxfReader) - -from dxfReader import readDXF +from dxfReader import readDXF, get_name, get_layer +from dxfReader import Object as dxfObject from dxfColorMap import color_map from math import * - - try: import os if os.name:# != 'mac': @@ -104,14 +93,1557 @@ SCENE = Scene.GetCurrent() WORLDX = Mathutils.Vector((1,0,0)) AUTO = BezTriple.HandleTypes.AUTO BYLAYER=256 - -class Layer: - """Dummy layer object.""" - def __init__(self, name, color, frozen): - self.name = name - self.color = color - self.frozen = frozen +"""This module provides wrapper objects for dxf entities. + The wrappers expect a "dxf object" as input. The dxf object is + an object with a type and a data attribute. Type is a lowercase + string matching the 0 code of a dxf entity. Data is a list containing + dxf objects or lists of [code, data] pairs. + + This module is not general, and is only for dxf import. +""" + +# from Stani's dxf writer v1.1 (c)www.stani.be (GPL) +#---color values +BYBLOCK=0 +BYLAYER=256 + +#---block-type flags (bit coded values, may be combined): +ANONYMOUS =1 # This is an anonymous block generated by hatching, associative dimensioning, other internal operations, or an application +NON_CONSTANT_ATTRIBUTES =2 # This block has non-constant attribute definitions (this bit is not set if the block has any attribute definitions that are constant, or has no attribute definitions at all) +XREF =4 # This block is an external reference (xref) +XREF_OVERLAY =8 # This block is an xref overlay +EXTERNAL =16 # This block is externally dependent +RESOLVED =32 # This is a resolved external reference, or dependent of an external reference (ignored on input) +REFERENCED =64 # This definition is a referenced external reference (ignored on input) + +#---mtext flags +#attachment point +TOP_LEFT = 1 +TOP_CENTER = 2 +TOP_RIGHT = 3 +MIDDLE_LEFT = 4 +MIDDLE_CENTER = 5 +MIDDLE_RIGHT = 6 +BOTTOM_LEFT = 7 +BOTTOM_CENTER = 8 +BOTTOM_RIGHT = 9 +#drawing direction +LEFT_RIGHT = 1 +TOP_BOTTOM = 3 +BY_STYLE = 5 #the flow direction is inherited from the associated text style +#line spacing style (optional): +AT_LEAST = 1 #taller characters will override +EXACT = 2 #taller characters will not override + +#---polyline flags +CLOSED =1 # This is a closed polyline (or a polygon mesh closed in the M direction) +CURVE_FIT =2 # Curve-fit vertices have been added +SPLINE_FIT =4 # Spline-fit vertices have been added +POLYLINE_3D =8 # This is a 3D polyline +POLYGON_MESH =16 # This is a 3D polygon mesh +CLOSED_N =32 # The polygon mesh is closed in the N direction +POLYFACE_MESH =64 # The polyline is a polyface mesh +CONTINOUS_LINETYPE_PATTERN =128 # The linetype pattern is generated continuously around the vertices of this polyline + +#---text flags +#horizontal +LEFT = 0 +CENTER = 1 +RIGHT = 2 +ALIGNED = 3 #if vertical alignment = 0 +MIDDLE = 4 #if vertical alignment = 0 +FIT = 5 #if vertical alignment = 0 +#vertical +BASELINE = 0 +BOTTOM = 1 +MIDDLE = 2 +TOP = 3 +class Layer: + """Class for objects representing dxf layers.""" + + def __init__(self, obj, name=None, color=None, frozen=None): + """Expects an entity object of type line as input.""" + self.type = obj.type + self.data = obj.data[:] + + if name: + self.name = name + else: + self.name = obj.get_type(2)[0] + if color: + self.color = color + else: + self.color = obj.get_type(62)[0] + if frozen: + self.frozen = frozen + else: + self.flags = obj.get_type(70)[0] + self.frozen = self.flags&1 + + def __repr__(self): + return "%s: name - %s, color - %s" %(self.__class__.__name__, self.name, self.color) + + + +class Line: + """Class for objects representing dxf lines.""" + + def __init__(self, obj): + """Expects an entity object of type line as input.""" + if not obj.type == 'line': + raise TypeError, "Wrong type %s for line object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + + self.space = obj.get_type(67) + if self.space: + self.space = self.space[0] + else: + self.space = 0 + + self.color_index = obj.get_type(62) + if self.color_index: + self.color_index = self.color_index[0] + else: + self.color_index = BYLAYER + + discard, self.layer = get_layer(obj.data) + obj.data.remove(discard) + self.points = self.get_points(obj.data) + + + + + def get_points(self, data): + """Gets start and end points for a line type object. + + Lines have a fixed number of points (two) and fixed codes for each value. + """ + + # start x, y, z and end x, y, z = 0 + sx, sy, sz, ex, ey, ez = 0, 0, 0, 0, 0, 0 + for item in data: + if item[0] == 10: # 10 = x + sx = item[1] + elif item[0] == 20: # 20 = y + sy = item[1] + elif item[0] == 30: # 30 = z + sz = item[1] + elif item[0] == 11: # 11 = x + ex = item[1] + elif item[0] == 21: # 21 = y + ey = item[1] + elif item[0] == 31: # 31 = z + ez = item[1] + return [[sx, sy, sz], [ex, ey, ez]] + + + + def __repr__(self): + return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points) + + + def draw(self, curves=False): + """Do all the specific things needed to import lines into Blender.""" + # Generate the geometery + points = self.points + edges = [[0, 1]] + + me = Mesh.New('line') # create a new mesh + + me.verts.extend(points) # add vertices to mesh + me.edges.extend(edges) # add edges to the mesh + + # Now Create an object + ob = Object.New('Mesh', 'line') # link mesh to an object + ob.link(me) + + return ob + + + + +class LWpolyline: + """Class for objects representing dxf LWpolylines.""" + + def __init__(self, obj): + """Expects an entity object of type lwpolyline as input.""" + if not obj.type == 'lwpolyline': + raise TypeError, "Wrong type %s for polyline object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + + # required data + self.num_points = obj.get_type(90)[0] + + # optional data (with defaults) + self.space = obj.get_type(67) + if self.space: + self.space = self.space[0] + else: + self.space = 0 + + self.color_index = obj.get_type(62) + if self.color_index: + self.color_index = self.color_index[0] + else: + self.color_index = BYLAYER + + self.elevation = obj.get_type(38) + if self.elevation: + self.elevation = self.elevation[0] + else: + self.elevation = 0 + + self.flags = obj.get_type(70) + if self.flags: + self.flags = self.flags[0] + else: + self.flags = 0 + + self.closed = self.flags&1 # byte coded, 1 = closed, 128 = plinegen + discard, self.layer = get_layer(obj.data) + obj.data.remove(discard) + self.points = self.get_points(obj.data) + self.extrusion = self.get_extrusion(obj.data) + + + + + + + def get_points(self, data): + """Gets points for a polyline type object. + + Polylines have no fixed number of verts, and + each vert can have a number of properties. + Verts should be coded as + 10:xvalue + 20:yvalue + 40:startwidth or 0 + 41:endwidth or 0 + 42:bulge or 0 + for each vert + """ + num = self.num_points + point = None + points = [] + for item in data: + if item[0] == 10: # 10 = x + if point: + points.append(point) + point = Vertex() + point.x = item[1] + elif item[0] == 20: # 20 = y + point.y = item[1] + elif item[0] == 40: # 40 = start width + point.swidth = item[1] + elif item[0] == 41: # 41 = end width + point.ewidth = item[1] + elif item[0] == 42: # 42 = bulge + point.bulge = item[1] + points.append(point) + return points + + + def get_extrusion(self, data): + """Find the axis of extrusion. + + Used to get the objects Object Coordinate System (ocs). + """ + vec = [0,0,1] + for item in data: + if item[0] == 210: # 210 = x + vec[0] = item[1] + elif item[0] == 220: # 220 = y + vec[1] = item[1] + elif item[0] == 230: # 230 = z + vec[2] = item[1] + return vec + + + def __repr__(self): + return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points) + + + def draw(self, curves=False): + """Do all the specific things needed to import plines into Blender.""" + # Generate the geometery + points = [] + for i in range(len(self.points)): + point = self.points[i] + if not point.bulge: + points.append(point.loc) + elif point.bulge and i < len(self.points)-1:# > 0: + center, radius, start, end = solveBulge(point, self.points[i+1]) + #print center, radius, start, end + verts, nosense = drawArc(center, radius, start, end) + verts.pop(0) # remove first + verts.pop() #remove last + if point.bulge >= 0: + verts.reverse() + points.extend(verts) + edges = [[num, num+1] for num in range(len(points)-1)] + if self.closed: + edges.append([len(self.points)-1, 0]) + + me = Mesh.New('lwpline') # create a new mesh + + me.verts.extend(points) # add vertices to mesh + me.edges.extend(edges) # add edges to the mesh + + # Now Create an object + ob = Object.New('Mesh', 'lwpline') # link mesh to an object + ob.link(me) + transform(self.extrusion, ob) + ob.LocZ = self.elevation + + return ob + + + +class Polyline: + """Class for objects representing dxf LWpolylines.""" + + def __init__(self, obj): + """Expects an entity object of type polyline as input.""" + if not obj.type == 'polyline': + raise TypeError, "Wrong type %s for polyline object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + self.points = [] + + # optional data (with defaults) + self.space = obj.get_type(67) + if self.space: + self.space = self.space[0] + else: + self.space = 0 + + self.color_index = obj.get_type(62) + if self.color_index: + self.color_index = self.color_index[0] + else: + self.color_index = BYLAYER + + self.elevation = obj.get_type(30) + if self.elevation: + self.elevation = self.elevation[0] + else: + self.elevation = 0 + + self.flags = obj.get_type(70) + if self.flags: + self.flags = self.flags[0] + else: + self.flags = 0 + + self.closed = self.flags&1 # byte coded, 1 = closed, 128 = plinegen + + discard, self.layer = get_layer(obj.data) + obj.data.remove(discard) + self.extrusion = self.get_extrusion(obj.data) + + + + + + def get_extrusion(self, data): + """Find the axis of extrusion. + + Used to get the objects Object Coordinate System (ocs). + """ + vec = [0,0,1] + for item in data: + if item[0] == 210: # 210 = x + vec[0] = item[1] + elif item[0] == 220: # 220 = y + vec[1] = item[1] + elif item[0] == 230: # 230 = z + vec[2] = item[1] + return vec + + + def __repr__(self): + return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points) + + + def draw(self, curves=False): + """Do all the specific things needed to import plines into Blender.""" + # Generate the geometery + points = [] + for i in range(len(self.points)): + point = self.points[i] + if not point.bulge: + points.append(point.loc) + elif point.bulge and i < len(self.points)-1:# > 0: + center, radius, start, end = solveBulge(point, self.points[i+1]) + #print center, radius, start, end + verts, nosense = drawArc(center, radius, start, end) + verts.pop(0) # remove first + verts.pop() #remove last + if point.bulge >= 0: + verts.reverse() + points.extend(verts) + edges = [[num, num+1] for num in range(len(points)-1)] + if self.closed: + edges.append([len(self.points)-1, 0]) + + me = Mesh.New('pline') # create a new mesh + + me.verts.extend(points) # add vertices to mesh + me.edges.extend(edges) # add edges to the mesh + + # Now Create an object + ob = Object.New('Mesh', 'pline') # link mesh to an object + ob.link(me) + transform(self.extrusion, ob) + ob.LocZ = self.elevation + + return ob + + + + +class Vertex(object): + """Generic vertex object used by polylines (and maybe others).""" + + def __init__(self, obj=None): + """Initializes vertex data. + + The optional obj arg is an entity object of type vertex. + """ + self.loc = [0,0,0] + self.bulge = 0 + self.swidth = 0 + self.ewidth = 0 + self.flags = 0 + + if obj is not None: + if not obj.type == 'vertex': + raise TypeError, "Wrong type %s for vertex object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + + self.get_props(obj.data) + + + def get_props(self, data): + """Gets coords for a vertex type object. + + Each vert can have a number of properties. + Verts should be coded as + 10:xvalue + 20:yvalue + 40:startwidth or 0 + 41:endwidth or 0 + 42:bulge or 0 + """ + for item in data: + if item[0] == 10: # 10 = x + self.x = item[1] + elif item[0] == 20: # 20 = y + self.y = item[1] + elif item[0] == 30: # 30 = z + self.z = item[1] + elif item[0] == 40: # 40 = start width + self.swidth = item[1] + elif item[0] == 41: # 41 = end width + self.ewidth = item[1] + elif item[0] == 42: # 42 = bulge + self.bulge = item[1] + elif item[0] == 70: # 70 = vert flags + self.flags = item[1] + + + def __len__(self): + return 3 + + + def __getitem__(self, key): + return self.loc[key] + + + def __setitem__(self, key, value): + if key in [0,1,2]: + self.loc[key] + + + def __iter__(self): + return self.loc.__iter__() + + + def __str__(self): + return str(self.loc) + + + def __repr__(self): + return "Vertex %s, swidth=%s, ewidth=%s, bulge=%s" %(self.loc, self.swidth, self.ewidth, self.bulge) + + + def getx(self): + return self.loc[0] + + def setx(self, value): + self.loc[0] = value + + x = property(getx, setx) + + + def gety(self): + return self.loc[1] + + def sety(self, value): + self.loc[1] = value + + y = property(gety, sety) + + + def getz(self): + return self.loc[2] + + def setz(self, value): + self.loc[2] = value + + z = property(getz, setz) + + + +class Text: + """Class for objects representing dxf Text.""" + + def __init__(self, obj): + """Expects an entity object of type text as input.""" + if not obj.type == 'text': + raise TypeError, "Wrong type %s for text object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + + # required data + self.height = obj.get_type(40)[0] + self.value = obj.get_type(1)[0] # The text string value + + # optional data (with defaults) + self.space = obj.get_type(67) + if self.space: + self.space = self.space[0] + else: + self.space = 0 + + self.color_index = obj.get_type(62) + if self.color_index: + self.color_index = self.color_index[0] + else: + self.color_index = BYLAYER + + self.rotation = obj.get_type(50) # radians? + if not self.rotation: + self.rotation = 0 + else: + self.rotation = self.rotation[0] + + self.width_factor = obj.get_type(41) # Scaling factor along local x axis + if not self.width_factor: + self.width_factor = 1 + else: + self.width_factor = self.width_factor[0] + + self.oblique = obj.get_type(51) # skew in degrees -90 <= oblique <= 90 + if not self.oblique: + self.oblique = 0 + else: + self.oblique = self.oblique[0] + + self.halignment = obj.get_type(72) # horiz. alignment + if not self.halignment: # 0=left, 1=center, 2=right, 3=aligned, 4=middle, 5=fit + self.halignment = 0 + else: + self.halignment = self.halignment[0] + + self.valignment = obj.get_type(73) # vert. alignment + if not self.valignment: # 0=baseline, 1=bottom, 2=middle, 3=top + self.valignment = 0 + else: + self.valignment = self.valignment[0] + + discard, self.layer = get_layer(obj.data) + obj.data.remove(discard) + self.loc = self.get_loc(obj.data, self.halignment, self.valignment) + self.extrusion = self.get_extrusion(obj.data) + + + + + def get_loc(self, data, halign, valign): + """Gets adjusted location for text type objects. + + If group 72 and/or 73 values are nonzero then the first alignment point values + are ignored and AutoCAD calculates new values based on the second alignment + point and the length and height of the text string itself (after applying the + text style). If the 72 and 73 values are zero or missing, then the second + alignment point is meaningless. + + I don't know how to calc text size... + """ + # bottom left x, y, z and justification x, y, z = 0 + x, y, z, jx, jy, jz = 0, 0, 0, 0, 0, 0 + for item in data: + if item[0] == 10: # 10 = x + x = item[1] + elif item[0] == 20: # 20 = y + y = item[1] + elif item[0] == 30: # 30 = z + z = item[1] + elif item[0] == 11: # 11 = x + jx = item[1] + elif item[0] == 21: # 21 = y + jy = item[1] + elif item[0] == 31: # 31 = z + jz = item[1] + + if halign or valign: + x, y, z = jx, jy, jz + return [x, y, z] + + def get_extrusion(self, data): + """Find the axis of extrusion. + + Used to get the objects Object Coordinate System (ocs). + """ + vec = [0,0,1] + for item in data: + if item[0] == 210: # 210 = x + vec[0] = item[1] + elif item[0] == 220: # 220 = y + vec[1] = item[1] + elif item[0] == 230: # 230 = z + vec[2] = item[1] + return vec + + + def __repr__(self): + return "%s: layer - %s, value - %s" %(self.__class__.__name__, self.layer, self.value) + + + def draw(self, curves=False): + """Do all the specific things needed to import texts into Blender.""" + # Generate the geometery + txt = Text3d.New("text") + txt.setSize(1) + txt.setShear(self.oblique/90) + txt.setExtrudeDepth(0.5) + if self.halignment == 0: + align = Text3d.LEFT + elif self.halignment == 1: + align = Text3d.MIDDLE + elif self.halignment == 2: + align = Text3d.RIGHT + elif self.halignment == 3: + align = Text3d.FLUSH + else: + align = Text3d.MIDDLE + txt.setAlignment(align) + txt.setText(self.value) + + # Now Create an object + ob = Object.New('Text', 'text') # link mesh to an object + ob.link(txt) + + transform(self.extrusion, ob) + + # move the object center to the text location + ob.loc = tuple(self.loc) + # scale it to the text size + ob.SizeX = self.height*self.width_factor + ob.SizeY = self.height + ob.SizeZ = self.height + # and rotate it around z + ob.RotZ = radians(self.rotation) + + return ob + + + + +class Mtext: + """Class for objects representing dxf Mtext.""" + + def __init__(self, obj): + """Expects an entity object of type mtext as input.""" + if not obj.type == 'mtext': + raise TypeError, "Wrong type %s for mtext object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + + # required data + self.height = obj.get_type(40)[0] + self.width = obj.get_type(41)[0] + self.alignment = obj.get_type(71)[0] # alignment 1=TL, 2=TC, 3=TR, 4=ML, 5=MC, 6=MR, 7=BL, 8=BC, 9=BR + self.value = self.get_text(obj.data) # The text string value + + # optional data (with defaults) + self.space = obj.get_type(67) + if self.space: + self.space = self.space[0] + else: + self.space = 0 + + self.color_index = obj.get_type(62) + if self.color_index: + self.color_index = self.color_index[0] + else: + self.color_index = BYLAYER + + self.rotation = obj.get_type(50) # radians + if not self.rotation: + self.rotation = 0 + else: + self.rotation = self.rotation[0] + + self.width_factor = obj.get_type(42) # Scaling factor along local x axis + if not self.width_factor: + self.width_factor = 1 + else: + self.width_factor = self.width_factor[0] + + self.line_space = obj.get_type(44) # percentage of default + if not self.line_space: + self.line_space = 1 + else: + self.line_space = self.line_space[0] + + discard, self.layer = get_layer(obj.data) + obj.data.remove(discard) + self.loc = self.get_loc(obj.data) + self.extrusion = self.get_extrusion(obj.data) + + + + + + def get_text(self, data): + """Reconstructs mtext data from dxf codes.""" + primary = '' + secondary = [] + for item in data: + if item[0] == 1: # There should be only one primary... + primary = item[1] + elif item[0] == 3: # There may be any number of extra strings (in order) + secondary.append(item[1]) + if not primary: + #raise ValueError, "Empty Mtext Object!" + string = "Empty Mtext Object!" + if not secondary: + string = primary.replace(r'\P', '\n') + else: + string = ''.join(secondary)+primary + string = string.replace(r'\P', '\n') + return string + def get_loc(self, data): + """Gets location for a mtext type objects. + + Mtext objects have only one point indicating location. + """ + loc = [0,0,0] + for item in data: + if item[0] == 10: # 10 = x + loc[0] = item[1] + elif item[0] == 20: # 20 = y + loc[1] = item[1] + elif item[0] == 30: # 30 = z + loc[2] = item[1] + return loc + + + + + def get_extrusion(self, data): + """Find the axis of extrusion. + + Used to get the objects Object Coordinate System (ocs). + """ + vec = [0,0,1] + for item in data: + if item[0] == 210: # 210 = x + vec[0] = item[1] + elif item[0] == 220: # 220 = y + vec[1] = item[1] + elif item[0] == 230: # 230 = z + vec[2] = item[1] + return vec + + + def __repr__(self): + return "%s: layer - %s, value - %s" %(self.__class__.__name__, self.layer, self.value) + + + def draw(self, curves=False): + """Do all the specific things needed to import mtexts into Blender.""" + # Generate the geometery + txt = Text3d.New("mtext") + txt.setSize(1) + # Blender doesn't give access to its text object width currently + # only to the text3d's curve width... + #txt.setWidth(text.width/10) + txt.setLineSeparation(self.line_space) + txt.setExtrudeDepth(0.5) + txt.setText(self.value) + + # Now Create an object + ob = Object.New('Text', 'mtext') # link mesh to an object + ob.link(txt) + + transform(self.extrusion, ob) + + # move the object center to the text location + ob.loc = tuple(self.loc) + # scale it to the text size + ob.SizeX = self.height*self.width_factor + ob.SizeY = self.height + ob.SizeZ = self.height + # and rotate it around z + ob.RotZ = radians(self.rotation) + + return ob + + + + + + +class Circle: + """Class for objects representing dxf Circles.""" + + def __init__(self, obj): + """Expects an entity object of type circle as input.""" + if not obj.type == 'circle': + raise TypeError, "Wrong type %s for circle object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + + # required data + self.radius = obj.get_type(40)[0] + + # optional data (with defaults) + self.space = obj.get_type(67) + if self.space: + self.space = self.space[0] + else: + self.space = 0 + + self.color_index = obj.get_type(62) + if self.color_index: + self.color_index = self.color_index[0] + else: + self.color_index = BYLAYER + + discard, self.layer = get_layer(obj.data) + obj.data.remove(discard) + self.loc = self.get_loc(obj.data) + self.extrusion = self.get_extrusion(obj.data) + + + + + + def get_loc(self, data): + """Gets the center location for circle type objects. + + Circles have a single coord location. + """ + loc = [0, 0, 0] + for item in data: + if item[0] == 10: # 10 = x + loc[0] = item[1] + elif item[0] == 20: # 20 = y + loc[1] = item[1] + elif item[0] == 30: # 30 = z + loc[2] = item[1] + return loc + + + + def get_extrusion(self, data): + """Find the axis of extrusion. + + Used to get the objects Object Coordinate System (ocs). + """ + vec = [0,0,1] + for item in data: + if item[0] == 210: # 210 = x + vec[0] = item[1] + elif item[0] == 220: # 220 = y + vec[1] = item[1] + elif item[0] == 230: # 230 = z + vec[2] = item[1] + return vec + + + def __repr__(self): + return "%s: layer - %s, radius - %s" %(self.__class__.__name__, self.layer, self.radius) + + + def draw(self, curves=False): + """Do all the specific things needed to import circles into Blender.""" + # Generate the geometery + # Now Create an object + if curves: + ob = drawCurveCircle(self) + else: + center = self.loc + radius = self.radius + + circ = 2 * pi * radius + if circ < 65: # if circumfrance is too small + verts = 32 # set a fixed number of 32 verts + else: + verts = circ/.5 # figure out how many verts we need + if verts > 100: # Blender only accepts values + verts = 100 # [3:100] + + c = Mesh.Primitives.Circle(int(verts), radius*2) + + ob = Object.New('Mesh', 'circle') + ob.link(c) # link curve data with this object + + ob.loc = tuple(center) + transform(self.extrusion, ob) + + return ob + + + + +class Arc: + """Class for objects representing dxf arcs.""" + + def __init__(self, obj): + """Expects an entity object of type arc as input.""" + if not obj.type == 'arc': + raise TypeError, "Wrong type %s for arc object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + + # required data + self.radius = obj.get_type(40)[0] + self.start_angle = obj.get_type(50)[0] + self.end_angle = obj.get_type(51)[0] + + # optional data (with defaults) + self.space = obj.get_type(67) + if self.space: + self.space = self.space[0] + else: + self.space = 0 + + self.color_index = obj.get_type(62) + if self.color_index: + self.color_index = self.color_index[0] + else: + self.color_index = BYLAYER + + discard, self.layer = get_layer(obj.data) + obj.data.remove(discard) + self.loc = self.get_loc(obj.data) + self.extrusion = self.get_extrusion(obj.data) + + + + + + def get_loc(self, data): + """Gets the center location for arc type objects. + + Arcs have a single coord location. + """ + loc = [0, 0, 0] + for item in data: + if item[0] == 10: # 10 = x + loc[0] = item[1] + elif item[0] == 20: # 20 = y + loc[1] = item[1] + elif item[0] == 30: # 30 = z + loc[2] = item[1] + return loc + + + + def get_extrusion(self, data): + """Find the axis of extrusion. + + Used to get the objects Object Coordinate System (ocs). + """ + vec = [0,0,1] + for item in data: + if item[0] == 210: # 210 = x + vec[0] = item[1] + elif item[0] == 220: # 220 = y + vec[1] = item[1] + elif item[0] == 230: # 230 = z + vec[2] = item[1] + return vec + + + def __repr__(self): + return "%s: layer - %s, radius - %s" %(self.__class__.__name__, self.layer, self.radius) + + + def draw(self, curves=False): + """Do all the specific things needed to import arcs into Blender.""" + # Generate the geometery + # Now Create an object + if curves: + ob = drawCurveArc(self) + else: + center = self.loc + radius = self.radius + start = self.start_angle + end = self.end_angle + verts, edges = drawArc(None, radius, start, end) + + a = Mesh.New('arc') + + a.verts.extend(verts) # add vertices to mesh + a.edges.extend(edges) # add edges to the mesh + + ob = Object.New('Mesh', 'arc') + ob.link(a) # link curve data with this object + ob.loc = tuple(center) + ob.RotX = radians(180) + + transform(self.extrusion, ob) + ob.size = (1,1,1) + + return ob + + +class BlockRecord: + """Class for objects representing dxf block_records.""" + + def __init__(self, obj): + """Expects an entity object of type block_record as input.""" + if not obj.type == 'block_record': + raise TypeError, "Wrong type %s for block_record object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + + # required data + self.name = obj.get_type(2)[0] + + # optional data (with defaults) + self.insertion_units = obj.get_type(70) + if not self.insertion_units: + self.insertion_units = None + else: + self.insertion_units = self.insertion_units[0] + + self.insert_units = obj.get_type(1070) + if not self.insert_units: + self.insert_units = None + else: + self.insert_units = self.insert_units[0] + + + + + + + def __repr__(self): + return "%s: name - %s, insert units - %s" %(self.__class__.__name__, self.name, self.insertion_units) + + + + +class Block: + """Class for objects representing dxf blocks.""" + + def __init__(self, obj): + """Expects an entity object of type block as input.""" + if not obj.type == 'block': + raise TypeError, "Wrong type %s for block object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + self.name = obj.name + + # required data + self.flags = obj.get_type(70)[0] + self.entities = dxfObject('block_contents') + self.entities.data = objectify([ent for ent in obj.data if type(ent) != list]) + + # optional data (with defaults) + self.path = obj.get_type(1) + if self.path: + self.path = self.path[0] + else: + self.path = '' + + self.discription = obj.get_type(4) + if self.discription: + self.discription = self.discription[0] + else: + self.discription = '' + + discard, self.layer = get_layer(obj.data) + obj.data.remove(discard) + self.loc = self.get_loc(obj.data) + + + + + def get_loc(self, data): + """Gets the insert point of the block.""" + loc = [0, 0, 0] + for item in data: + if type(item) != list: + continue + if item[0] == 10: # 10 = x + loc[0] = item[1] + elif item[0] == 20: # 20 = y + loc[1] = item[1] + elif item[0] == 30: # 30 = z + loc[2] = item[1] + return loc + + + + def __repr__(self): + return "%s: name - %s, description - %s, xref-path - %s" %(self.__class__.__name__, self.name, self.discription, self.path) + + + + +class Insert: + """Class for objects representing dxf inserts.""" + + def __init__(self, obj): + """Expects an entity object of type insert as input.""" + if not obj.type == 'insert': + raise TypeError, "Wrong type %s for insert object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + + # required data + self.block = obj.get_type(2)[0] + + # optional data (with defaults) + self.rotation = obj.get_type(50) + if self.rotation: + self.rotation = self.rotation[0] + else: + self.rotation = 0 + + self.space = obj.get_type(67) + if self.space: + self.space = self.space[0] + else: + self.space = 0 + + self.color_index = obj.get_type(62) + if self.color_index: + self.color_index = self.color_index[0] + else: + self.color_index = BYLAYER + + discard, self.layer = get_layer(obj.data) + obj.data.remove(discard) + self.loc = self.get_loc(obj.data) + self.scale = self.get_scale(obj.data) + self.rows, self.columns = self.get_array(obj.data) + self.extrusion = self.get_extrusion(obj.data) + + + + + + def get_loc(self, data): + """Gets the center location for circle type objects. + + Circles have a single coord location. + """ + loc = [0, 0, 0] + for item in data: + if item[0] == 10: # 10 = x + loc[0] = item[1] + elif item[0] == 20: # 20 = y + loc[1] = item[1] + elif item[0] == 30: # 30 = z + loc[2] = item[1] + return loc + + + + def get_scale(self, data): + """Gets the x/y/z scale factor for the block. + """ + scale = [1, 1, 1] + for item in data: + if item[0] == 41: # 41 = x scale + scale[0] = item[1] + elif item[0] == 42: # 42 = y scale + scale[1] = item[1] + elif item[0] == 43: # 43 = z scale + scale[2] = item[1] + return scale + + + + def get_array(self, data): + """Returns the pair (row number, row spacing), (column number, column spacing).""" + columns = 1 + rows = 1 + cspace = 0 + rspace = 0 + for item in data: + if item[0] == 70: # 70 = columns + columns = item[1] + elif item[0] == 71: # 71 = rows + rows = item[1] + if item[0] == 44: # 44 = columns + cspace = item[1] + elif item[0] == 45: # 45 = rows + rspace = item[1] + return (rows, rspace), (columns, cspace) + + + + def get_extrusion(self, data): + """Find the axis of extrusion. + + Used to get the objects Object Coordinate System (ocs). + """ + vec = [0,0,1] + for item in data: + if item[0] == 210: # 210 = x + vec[0] = item[1] + elif item[0] == 220: # 220 = y + vec[1] = item[1] + elif item[0] == 230: # 230 = z + vec[2] = item[1] + return vec + + + def __repr__(self): + return "%s: layer - %s, block - %s" %(self.__class__.__name__, self.layer, self.block) + + + def draw(self, handle, settings): + """Do all the specific things needed to import blocks into Blender. + + Blocks are made of three objects: + the block_record in the tables section + the block in the blocks section + the insert object in the entities section + + block_records give the insert units, blocks provide the objects drawn in the + block, and the insert object gives the location/scale/rotation of the block + instances. To draw a block you must first get a group with all the + blocks entities drawn in it, then scale the entities to match the world + units, then dupligroup that data to an object matching each insert object.""" + + # Create an object + ob = Object.New('Mesh', self.block) + ob.link(handle) # Give the object a handle + + if settings.drawTypes['blocks']: + # get our block group + block = settings.blocks(self.block) + + ob.DupGroup = block + ob.enableDupGroup = True + + ob.loc = tuple(self.loc) + transform(self.extrusion, ob) + ob.RotZ += radians(self.rotation) + ob.size = tuple(self.scale) + + return ob + + + + +class Ellipse: + """Class for objects representing dxf ellipses.""" + + def __init__(self, obj): + """Expects an entity object of type ellipse as input.""" + if not obj.type == 'ellipse': + raise TypeError, "Wrong type %s for ellipse object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + + # required data + self.ratio = obj.get_type(40)[0] + self.start_angle = obj.get_type(41)[0] + self.end_angle = obj.get_type(42)[0] + + # optional data (with defaults) + self.space = obj.get_type(67) + if self.space: + self.space = self.space[0] + else: + self.space = 0 + + self.color_index = obj.get_type(62) + if self.color_index: + self.color_index = self.color_index[0] + else: + self.color_index = BYLAYER + + discard, self.layer = get_layer(obj.data) + obj.data.remove(discard) + self.loc = self.get_loc(obj.data) + self.major = self.get_major(obj.data) + self.extrusion = self.get_extrusion(obj.data) + self.radius = sqrt(self.major[0]**2 + self.major[0]**2 + self.major[0]**2) + + + + + def get_loc(self, data): + """Gets the center location for arc type objects. + + Arcs have a single coord location. + """ + loc = [0, 0, 0] + for item in data: + if item[0] == 10: # 10 = x + loc[0] = item[1] + elif item[0] == 20: # 20 = y + loc[1] = item[1] + elif item[0] == 30: # 30 = z + loc[2] = item[1] + return loc + + + + def get_major(self, data): + """Gets the major axis for ellipse type objects. + + The ellipse major axis defines the rotation of the ellipse and its radius. + """ + loc = [0, 0, 0] + for item in data: + if item[0] == 11: # 11 = x + loc[0] = item[1] + elif item[0] == 21: # 21 = y + loc[1] = item[1] + elif item[0] == 31: # 31 = z + loc[2] = item[1] + return loc + + + + def get_extrusion(self, data): + """Find the axis of extrusion. + + Used to get the objects Object Coordinate System (ocs). + """ + vec = [0,0,1] + for item in data: + if item[0] == 210: # 210 = x + vec[0] = item[1] + elif item[0] == 220: # 220 = y + vec[1] = item[1] + elif item[0] == 230: # 230 = z + vec[2] = item[1] + return vec + + + def __repr__(self): + return "%s: layer - %s, radius - %s" %(self.__class__.__name__, self.layer, self.radius) + + + def draw(self, curves=False): + """Do all the specific things needed to import ellipses into Blender.""" + # Generate the geometery + # Now Create an object + if curves: + ob = drawCurveArc(self) + else: + major = Mathutils.Vector(self.major) + delta = Mathutils.AngleBetweenVecs(major, WORLDX) + center = self.loc + radius = major.length + start = degrees(self.start_angle) + end = degrees(self.end_angle) + verts, edges = drawArc(None, radius, start, end) + + e = Mesh.New('ellipse') + + e.verts.extend(verts) # add vertices to mesh + e.edges.extend(edges) # add edges to the mesh + + + ob = Object.New('Mesh', 'arc') + ob.link(e) # link curve data with this object + ob.loc = tuple(center) + ob.SizeY = self.ratio + #ob.RotZ = radians(delta) + ob.RotX = radians(180) + + + transform(self.extrusion, ob) + ob.RotZ = radians(delta) + + return ob + + + +class Face: + """Class for objects representing dxf 3d faces.""" + + def __init__(self, obj): + """Expects an entity object of type 3dfaceplot as input.""" + if not obj.type == '3dface': + raise TypeError, "Wrong type %s for 3dface object!" %obj.type + self.type = obj.type + self.data = obj.data[:] + + # optional data (with defaults) + self.space = obj.get_type(67) + if self.space: + self.space = self.space[0] + else: + self.space = 0 + + self.color_index = obj.get_type(62) + if self.color_index: + self.color_index = self.color_index[0] + else: + self.color_index = BYLAYER + + discard, self.layer = get_layer(obj.data) + obj.data.remove(discard) + self.points = self.get_points(obj.data) + + + + + def get_points(self, data): + """Gets 3-4 points for a 3d face type object. + + Faces have three or optionally four verts. + """ + + a = [0, 0, 0] + b = [0, 0, 0] + c = [0, 0, 0] + d = False + for item in data: + # ----------- a ------------- + if item[0] == 10: # 10 = x + a[0] = item[1] + elif item[0] == 20: # 20 = y + a[1] = item[1] + elif item[0] == 30: # 30 = z + a[2] = item[1] + # ----------- b ------------- + elif item[0] == 11: # 11 = x + b[0] = item[1] + elif item[0] == 21: # 21 = y + b[1] = item[1] + elif item[0] == 31: # 31 = z + b[2] = item[1] + # ----------- c ------------- + elif item[0] == 12: # 12 = x + c[0] = item[1] + elif item[0] == 22: # 22 = y + c[1] = item[1] + elif item[0] == 32: # 32 = z + c[2] = item[1] + # ----------- d ------------- + elif item[0] == 13: # 13 = x + d = [0, 0, 0] + d[0] = item[1] + elif item[0] == 23: # 23 = y + d[1] = item[1] + elif item[0] == 33: # 33 = z + d[2] = item[1] + out = [a,b,c] + if d: + out.append(d) + return out + + + def __repr__(self): + return "%s: layer - %s, points - %s" %(self.__class__.__name__, self.layer, self.points) + + + def draw(self, curves=False): + """Do all the specific things needed to import 3d faces into Blender.""" + # Generate the geometery + points = self.points + if len(self.points) > 3: + faces = [[0, 1, 2, 3]] + else: + faces = [[0, 1, 2]] + + me = Mesh.New('3dface') # create a new mesh + + me.verts.extend(points) # add vertices to mesh + me.faces.extend(faces) # add faces to the mesh + + # Now Create an object + ob = Object.New('Mesh', '3dface') # link mesh to an object + ob.link(me) + + return ob + + + +# type to object map +type_map = { + 'line':Line, + 'lwpolyline':LWpolyline, + 'text':Text, + 'mtext':Mtext, + 'circle':Circle, + 'arc':Arc, + 'layer':Layer, + 'block_record':BlockRecord, + 'block':Block, + 'insert':Insert, + 'ellipse':Ellipse, + '3dface':Face +} + +def objectify(data): + """Expects a section type object's data as input. + + Maps object data to the correct object type. + """ + objects = [] # colector for finished objects + known_types = type_map.keys() # so we don't have to call foo.keys() every iteration + index = 0 + while index < len(data): + item = data[index] + if type(item) != list and item.type in known_types: + # proccess the object and append the resulting object + objects.append(type_map[item.type](item)) + elif type(item) != list and item.type == 'table': + item.data = objectify(item.data) # tables have sub-objects + objects.append(item) + elif type(item) != list and item.type == 'polyline': + pline = Polyline(item) + while 1: + index += 1 + item = data[index] + if item.type == 'vertex': + v = Vertex(item) + pline.points.append(v) + elif item.type == 'seqend': + break + else: + print "Error: non-vertex found before seqend!" + index -= 1 + break + objects.append(pline) + else: + # we will just let the data pass un-harrased + objects.append(item) + index += 1 + return objects class MatColors: """A smart container for color based materials. @@ -144,7 +1676,7 @@ class MatColors: self.map[color] = layer color = 0 color = abs(color) - if color not in self.colors: # .keys() + if color not in self.colors.keys(): self.add(color) return self.colors[color] @@ -186,7 +1718,7 @@ class Blocks: """ if not name: return self.blocks - if name not in self.blocks: # .keys(): + if name not in self.blocks.keys(): self.add(name) return self.blocks[name] @@ -194,14 +1726,12 @@ class Blocks: def add(self, name): """Create a new block group for the block with name.""" - optimization = self.settings.optimization + write = self.settings.write group = Group.New(name) block = self.map[name] - if optimization <= 1: - print "\nDrawing %s block entities..." %name + write("\nDrawing %s block entities..." %name) drawEntities(block.entities, self.settings, group) - if optimization <= 1: - print "Done!" + write("Done!") self.blocks[name] = group @@ -216,69 +1746,73 @@ class Settings: # Optimization constants MIN = 0 MID = 1 - MAX = 2 + PRO = 2 + MAX = 3 - def __init__(self, drawing, curves): + def __init__(self, drawing, curves, optimization, **kwds): """Given the drawing initialize all the important settings used by the draw functions.""" self.curves = curves + self.optimization = optimization + print "Setting optimization level %s!" %optimization + self.drawTypes = kwds self.layers = True self.blocks = True - self.optimization = self.getOpt() # First sort out all the sections sections = dict([(item.name, item) for item in drawing.data]) # The header section may be omited - if self.optimization <= self.MID: - if 'header' in sections: #.keys(): - print "Found header!" - else: - print "File contains no header!" + if 'header' in sections.keys(): + self.write("Found header!") + else: + self.write("File contains no header!") # The tables section may be partialy or completely missing. - if 'tables' in sections: # .keys(): - if self.optimization <= self.MID: - print "Found tables!" + if 'tables' in sections.keys(): + self.write("Found tables!") tables = dict([(item.name, item) for item in sections["tables"].data]) - if 'layer' in tables: # .keys(): - if self.optimization <= self.MID: - print "Found layers!" + if 'layer' in tables.keys(): + self.write("Found layers!") # Read the layers table and get the layer colors self.colors = getLayers(drawing) else: - if self.optimization <= self.MID: - print "File contains no layers table!" + self.write("File contains no layers table!") self.layers = False self.colors = MatColors({}) else: - if self.optimization <= self.MID: - print "File contains no tables!" - print "File contains no layers table!" + self.write("File contains no tables!") + self.write("File contains no layers table!") self.layers = False self.colors = MatColors({}) # The blocks section may be omited - if 'blocks' in sections: #.keys(): - if self.optimization <= self.MID: - print "Found blocks!" + if 'blocks' in sections.keys(): + self.write("Found blocks!") # Read the block definitions and build our block object - self.blocks = getBlocks(drawing, self) + if self.drawTypes['blocks']: + self.blocks = getBlocks(drawing, self) else: - if self.optimization <= self.MID: - print "File contains no blocks!" - self.blocks = False + self.write("File contains no blocks!") + self.drawTypes['blocks'] = False + def write(self, text, newline=True): + """Wraps the built-in print command in a optimization check.""" + if self.optimization <= self.MID: + if newline: + print text + else: + print text, + + def redraw(self): + """Update Blender if optimization level is low enough.""" + if self.optimization <= self.MIN: + Blender.Redraw() - def getOpt(self): - """Ask the user for update optimization level.""" - Window.WaitCursor(False) + def progress(self, done, text): + """Wrapper for Blender.Window.DrawProgressBar.""" + if self.optimization <= self.PRO: + Window.DrawProgressBar(done, text) - retval = Draw.PupIntInput('optimization: ', 1, 0, 2) - print "Setting optimization level %s!" %retval - - Window.WaitCursor(True) - return retval - def isOff(self, name): """Given a layer name look up the layer object and return its visable status.""" @@ -296,112 +1830,7 @@ class Settings: -class Drawer: - """Super 'function' for all the entitiy drawing functions. - - The code for the drawing functions was very repetitive, each differing - by only a few lines at most. So here is a callable class with methods - for each part of the import proccess. - """ - - def __init__(self, block=False): - self.block = block - - - - def __call__(self, entities, settings, group=None): - """Call with a list of entities and a settings object to generate Blender geometry.""" - if entities and settings.optimization <= settings.MID: - print "Drawing %ss..." %entities[0].type, - - if self.block: - # create one 'handle' data block to use with all blocks - handle = Mesh.New('insert') - handle.verts.extend( - [(-0.01,0,0), - (0.01,0,0), - (0,-0.01,0), - (0,0.01,0), - (0,0,-0.01), - (0,0,0.01)] - ) - handle.edges.extend([(0,1),(2,3),(4,5)]) - - # For now we only want model-space objects - entities = [entity for entity in entities if entity.space == 0] - - if group: - block_def = True - else: - block_def = False - - for entity in entities: - if settings.optimization <= settings.MID: - print '\b.', - # First get the layer group - if not block_def: - group = self.getGroup('layer %s' %entity.layer) # add overhead just to make things a little cleaner - - if not self.block: - ob = self.draw(entity, settings.curves) - else: - ob = self.draw(entity, handle, settings) - - self.setColor(entity, ob, settings) - # Link it to the scene and add it to the correct group - SCENE.link(ob) - self.setGroup(group, ob) - - # Set the visability - if settings.isOff(entity.layer): - ob.Layers = 1<<19 # [20] - elif block_def: - ob.Layers = (1<<18) # [19] - else: - ob.Layers = (1<<20)-1 # [i+1 for i in xrange(20)] # all layers - - # # Set the visability - # if settings.isOff(entity.layer) or block_def: - # ob.restrictDisplay = True - # ob.restrictRender = True - - if settings.optimization == settings.MIN: - # I know it's slow to have Blender redraw after each entity type is drawn - # But is it really slower than the progress bar? - Blender.Redraw() - if entities and settings.optimization <= settings.MID: - print "\nFinished drawing %ss!" %entities[0].type - def getGroup(self, name): - """Returns a Blender group object.""" - try: - group = Group.Get(name) - except: # What is the exception? - group = Group.New(name) - return group - def draw(self, entity): - """Dummy method to be over written in subclasses.""" - pass - - - def setColor(self, entity, ob, settings): - # Set the color - if entity.color_index == BYLAYER: - mat = settings.colors(entity.layer) - else: - mat = settings.colors(entity.color_index) - try: - ob.setMaterials([mat]) - except ValueError: - print "material error - %s!" %mat - ob.colbits = 0x01 # Set OB materials. - def setGroup(self, group, it): - try: - group.objects.link(it) - except: - group.objects.append(it) - - -def main(filename): +def main(filename=None): editmode = Window.EditMode() # are we in edit mode? If so ... if editmode: Window.EditMode(0) # leave edit mode before Window.WaitCursor(True) # Let the user know we are thinking @@ -414,9 +1843,8 @@ def main(filename): except IOError: print "DXF import: error finding default test file, exiting..." return None - if filename: - drawing = readDXF(filename) - drawDrawing(drawing) + drawing = readDXF(filename, objectify) + drawDrawing(drawing) finally: # restore state even if things didn't work Window.WaitCursor(False) @@ -498,121 +1926,183 @@ def getBlocks(drawing, settings): def drawDrawing(drawing): """Given a drawing object recreate the drawing in Blender.""" print "Getting settings..." - # The settings object controls how dxf entities are drawn - settings = Settings(drawing, curves=False) + Window.WaitCursor(False) + #width, height = Window.GetScreenSize() + Window.SetMouseCoords() - if settings.optimization <= settings.MID: - print "Drawings entities..." + # Create a PupBlock to get user preferences + lines = Draw.Create(1) + arcs = Draw.Create(1) + circles = Draw.Create(1) + polylines = Draw.Create(1) + text = Draw.Create(1) + blocks = Draw.Create(1) + faces = Draw.Create(1) + optim = Draw.Create(1) + + block = [] + block.append("Draw Options:") + block.append(("Lines", lines, "Toggle drawing of lines")) + block.append(("Arcs", arcs, "Toggle drawing of arcs")) + block.append(("Circles", circles, "Toggle drawing of circles")) + block.append(("Polylines", polylines, "Toggle drawing of polylines")) + block.append(("Text", text, "Toggle drawing of text")) + block.append(("Blocks", blocks, "Toggle drawing of blocks")) + block.append(("Faces", faces, "Toggle drawing of faces")) + block.append("Update Optimization:") + block.append(("Level: ", optim, 0, 3)) + + retval = Draw.PupBlock("DXF Import", block) + + Window.WaitCursor(True) # Let the user know we are thinking + # The settings object controls how dxf entities are drawn + settings = Settings(drawing, curves=False, + optimization=int(optim.val), + lines=bool(lines.val), + arcs=bool(arcs.val), + circles=bool(circles.val), + polylines=bool(polylines.val), + text=bool(text.val), + blocks=bool(blocks.val), + faces=bool(faces.val) + ) + + settings.write("Drawings entities...") # Draw all the know entity types in the current scene drawEntities(drawing.entities, settings) # Set the visable layers - SCENE.setLayers([i+1 for i in xrange(18)]) # SCENE.Layers = 262143 or (1<<18) + SCENE.setLayers([i+1 for i in range(18)]) Blender.Redraw(-1) - if settings.optimization <= settings.MID: - print "Done!" + settings.write("Done!") + + def drawEntities(entities, settings, group=None): """Draw every kind of thing in the entity list. If provided 'group' is the Blender group new entities are to be added to. """ - for _type, drawer in type_map.iteritems(): + for _type in type_map.keys(): # for each known type get a list of that type and call the associated draw function - drawer(entities.get_type(_type), settings, group) + drawer(_type, entities.get_type(_type), settings, group) -drawLines = Drawer() -def drawLine(line, curves=False): - """Do all the specific things needed to import lines into Blender.""" - # Generate the geometery - points = line.points - edges = [[0, 1]] +def drawer(_type, entities, settings, group): + """Call with a list of entities and a settings object to generate Blender geometry.""" + if entities: + # Break out early if settings says we aren't drawing the current type + block = False + skip = False + if _type == 'block_record': + skip = True + if _type == 'line' and not settings.drawTypes['lines']: + skip = True + elif _type == 'arc' and not settings.drawTypes['arcs']: + skip = True + elif _type == 'circle' and not settings.drawTypes['circles']: + skip = True + elif _type in ['lwpolyline', 'polyline'] and not settings.drawTypes['polylines']: + skip = True + elif _type in ['text', 'mtext'] and not settings.drawTypes['text']: + skip = True + elif _type == 'insert': + if not settings.drawTypes['blocks']: + skip = True + block = True + elif _type == '3dface' and not settings.drawTypes['faces']: + skip = True + if skip: + settings.write("Skipping %s type entities!" %_type) + return + + message = "Drawing %ss..." %_type + settings.write(message, False) + settings.progress(0, message) + + if block: + # create one 'handle' data block to use with all blocks + handle = Mesh.New('insert') + handle.verts.extend( + [(-0.01,0,0), + (0.01,0,0), + (0,-0.01,0), + (0,0.01,0), + (0,0,-0.01), + (0,0,0.01)] + ) + handle.edges.extend([(0,1),(2,3),(4,5)]) + + # For now we only want model-space objects + entities = [entity for entity in entities if entity.space == 0] + + if group: + block_def = True + else: + block_def = False + + tot = len(entities) + cur = 1.0 + for entity in entities: + settings.write('\b.', False) + settings.progress(cur/tot, message) + cur += 1 + + # First get the layer group + if not block_def: + group = getGroup('layer %s' %entity.layer) # add overhead just to make things a little cleaner + + if block: + ob = entity.draw(handle, settings) + else: + ob = entity.draw(settings.curves) + + setColor(entity, ob, settings) + # Link it to the scene and add it to the correct group + SCENE.link(ob) + setGroup(group, ob) + + # Set the visability + if settings.isOff(entity.layer): + ob.layers = [20] + elif block_def: + ob.layers = [19] + else: + ob.layers = [i+1 for i in range(20)] + + # # Set the visability + # if settings.isOff(entity.layer) or block_def: + # ob.restrictDisplay = True + # ob.restrictRender = True + + settings.redraw() + message = "\nFinished drawing %ss!" %entities[0].type + settings.write(message) + settings.progress(1, message) + - me = Mesh.New('line') # create a new mesh - - me.verts.extend(points) # add vertices to mesh - me.edges.extend(edges) # add edges to the mesh - - # Now Create an object - ob = Object.New('Mesh', 'line') # link mesh to an object - ob.link(me) - - return ob -drawLines.draw = drawLine - - -drawLWpolylines = Drawer() -def drawLWpolyline(pline, curves=False): - """Do all the specific things needed to import plines into Blender.""" - # Generate the geometery - points = [] - for i in xrange(len(pline.points)): - point = pline.points[i] - if not point.bulge: - points.append(point.loc) - elif point.bulge and i < len(pline.points)-1:# > 0: - center, radius, start, end = solveBulge(point, pline.points[i+1]) - #print center, radius, start, end - verts, nosense = drawArc(center, radius, start, end) - verts.pop(0) # remove first - verts.pop() #remove last - if point.bulge >= 0: - verts.reverse() - points.extend(verts) - edges = [[num, num+1] for num in xrange(len(points)-1)] - if pline.closed: - edges.append([len(pline.points)-1, 0]) - - me = Mesh.New('lwpline') # create a new mesh - - me.verts.extend(points) # add vertices to mesh - me.edges.extend(edges) # add edges to the mesh - - # Now Create an object - ob = Object.New('Mesh', 'lwpline') # link mesh to an object - ob.link(me) - transform(pline.extrusion, ob) - ob.LocZ = pline.elevation - - return ob -drawLWpolylines.draw = drawLWpolyline - -drawPolylines = Drawer() -def drawPolyline(pline, curves=False): - """Do all the specific things needed to import plines into Blender.""" - # Generate the geometery - points = [] - for i in xrange(len(pline.points)): - point = pline.points[i] - if not point.bulge: - points.append(point.loc) - elif point.bulge and i < len(pline.points)-1:# > 0: - center, radius, start, end = solveBulge(point, pline.points[i+1]) - #print center, radius, start, end - verts, nosense = drawArc(center, radius, start, end) - verts.pop(0) # remove first - verts.pop() #remove last - if point.bulge >= 0: - verts.reverse() - points.extend(verts) - edges = [[num, num+1] for num in xrange(len(points)-1)] - if pline.closed: - edges.append([len(pline.points)-1, 0]) - - me = Mesh.New('pline') # create a new mesh - - me.verts.extend(points) # add vertices to mesh - me.edges.extend(edges) # add edges to the mesh - - # Now Create an object - ob = Object.New('Mesh', 'pline') # link mesh to an object - ob.link(me) - transform(pline.extrusion, ob) - ob.LocZ = pline.elevation - - return ob -drawPolylines.draw = drawPolyline - +def getGroup(name): + """Returns a Blender group object.""" + try: + group = Group.Get(name) + except: # What is the exception? + group = Group.New(name) + return group +def setColor(entity, ob, settings): + # Set the color + if entity.color_index == BYLAYER: + mat = settings.colors(entity.layer) + else: + mat = settings.colors(entity.color_index) + try: + ob.setMaterials([mat]) + except ValueError: + settings.write("material error - %s!" %mat) + ob.colbits = 0x01 # Set OB materials. +def setGroup(group, it): + try: + group.objects.link(it) + except: + group.objects.append(it) def solveBulge(p1, p2): """return the center, radius, start angle, and end angle given two points. @@ -662,147 +2152,14 @@ def solveBulge(p1, p2): # the end angle is just 'angle' more than start angle end = start + angle return list(center), radius, start, end -drawTexts = Drawer() -def drawText(text, curves=False): - """Do all the specific things needed to import texts into Blender.""" - # Generate the geometery - txt = Text3d.New("text") - txt.setSize(1) - txt.setShear(text.oblique/90) - txt.setExtrudeDepth(0.5) - if text.halignment == 0: - align = Text3d.LEFT - elif text.halignment == 1: - align = Text3d.MIDDLE - elif text.halignment == 2: - align = Text3d.RIGHT - elif text.halignment == 3: - align = Text3d.FLUSH - else: - align = Text3d.MIDDLE - txt.setAlignment(align) - txt.setText(text.value) - - # Now Create an object - ob = Object.New('Text', 'text') # link mesh to an object - ob.link(txt) - - transform(text.extrusion, ob) - - # move the object center to the text location - ob.loc = tuple(text.loc) - # scale it to the text size - ob.SizeX = text.height*text.width_factor - ob.SizeY = text.height - ob.SizeZ = text.height - # and rotate it around z - ob.RotZ = radians(text.rotation) - - return ob -drawTexts.draw = drawText - -drawMtexts = Drawer() -def drawMtext(text, curves=False): - """Do all the specific things needed to import mtexts into Blender.""" - # Generate the geometery - txt = Text3d.New("mtext") - txt.setSize(1) - # Blender doesn't give access to its text object width currently - # only to the text3d's curve width... - #txt.setWidth(text.width/10) - txt.setLineSeparation(text.line_space) - txt.setExtrudeDepth(0.5) - txt.setText(text.value) - - # Now Create an object - ob = Object.New('Text', 'mtext') # link mesh to an object - ob.link(txt) - - transform(text.extrusion, ob) - - # move the object center to the text location - ob.loc = tuple(text.loc) - # scale it to the text size - ob.SizeX = text.height*text.width_factor - ob.SizeY = text.height - ob.SizeZ = text.height - # and rotate it around z - ob.RotZ = radians(text.rotation) - - return ob -drawMtexts.draw = drawMtext - - - -drawCircles = Drawer() -def drawCircle(circle, curves=False): - """Do all the specific things needed to import circles into Blender.""" - # Generate the geometery - # Now Create an object - if curves: - ob = drawCurveCircle(circle) - else: - center = circle.loc - radius = circle.radius - - circ = 2 * pi * radius - if circ < 65: # if circumfrance is too small - verts = 32 # set a fixed number of 32 verts - else: - verts = circ/.5 # figure out how many verts we need - if verts > 100: # Blender only accepts values - verts = 100 # [3:100] - - c = Mesh.Primitives.Circle(int(verts), radius*2) - - ob = Object.New('Mesh', 'circle') - ob.link(c) # link curve data with this object - - ob.loc = tuple(center) - transform(circle.extrusion, ob) - - return ob -drawCircles.draw = drawCircle - -drawArcs = Drawer() -def drawArc(arc, curves=False): - """Do all the specific things needed to import arcs into Blender.""" - # Generate the geometery - # Now Create an object - if curves: - ob = drawCurveArc(arc) - else: - center = arc.loc - radius = arc.radius - start = arc.start_angle - end = arc.end_angle - verts, edges = drawArc(None, radius, start, end) - - a = Mesh.New('arc') - - a.verts.extend(verts) # add vertices to mesh - a.edges.extend(edges) # add edges to the mesh - - ob = Object.New('Mesh', 'arc') - ob.link(a) # link curve data with this object - ob.loc = tuple(center) - ob.RotX = radians(180) - - transform(arc.extrusion, ob) - ob.size = (1,1,1) - - return ob -drawArcs.draw = drawArc - - def drawArc(center, radius, start, end, step=0.5): """Draw a mesh arc with the given parameters.""" # center is currently set by object # if start > end: # start = start - 360 - # if end > 360: - # end = end%360 + if end > 360: + end = end%360 startmatrix = Mathutils.RotationMatrix(start, 3, "Z") startpoint = startmatrix * Mathutils.Vector((radius, 0, 0)) endmatrix = Mathutils.RotationMatrix(end, 3, "Z") @@ -824,107 +2181,16 @@ def drawArc(center, radius, start, end, step=0.5): stepmatrix = Mathutils.RotationMatrix(step, 3, "Z") point = Mathutils.Vector(startpoint) - for i in xrange(int(pieces)): + for i in range(int(pieces)): point = stepmatrix * point points.append(point) points.append(endpoint) if center: points = [[point[0]+center[0], point[1]+center[1], point[2]+center[2]] for point in points] - edges = [[num, num+1] for num in xrange(len(points)-1)] + edges = [[num, num+1] for num in range(len(points)-1)] return points, edges -drawEllipses = Drawer() -def drawEllipse(ellipse, curves=False): - """Do all the specific things needed to import ellipses into Blender.""" - # Generate the geometery - # Now Create an object - if curves: - ob = drawCurveArc(ellipse) - else: - major = Mathutils.Vector(ellipse.major) - delta = Mathutils.AngleBetweenVecs(major, WORLDX) - center = ellipse.loc - radius = major.length - start = degrees(ellipse.start_angle) - end = degrees(ellipse.end_angle) - verts, edges = drawArc(None, radius, start, end) - - e = Mesh.New('ellipse') - - e.verts.extend(verts) # add vertices to mesh - e.edges.extend(edges) # add edges to the mesh - - - ob = Object.New('Mesh', 'arc') - ob.link(e) # link curve data with this object - ob.loc = tuple(center) - ob.SizeY = ellipse.ratio - #ob.RotZ = radians(delta) - ob.RotX = radians(180) - - - transform(ellipse.extrusion, ob) - ob.RotZ = radians(delta) - - return ob -drawEllipses.draw = drawEllipse -drawBlocks = Drawer(True) -def drawBlock(insert, handle, settings): - """recursivly draw block objects. - - Blocks are made of three objects: - the block_record in the tables section - the block in the blocks section - the insert object in the entities section - - block_records give the insert units, blocks provide the objects drawn in the - block, and the insert object gives the location/scale/rotation of the block - instances. To draw a block you must first get a group with all the - blocks entities drawn in it, then scale the entities to match the world - units, then dupligroup that data to an object matching each insert object.""" - if settings.blocks: - # get our block group - block = settings.blocks(insert.block) - - # Now Create an object - ob = Object.New('Mesh', insert.block) - ob.link(handle) # Give the object a handle - ob.DupGroup = block - ob.enableDupGroup = True - else: - ob = Object.New('Mesh') - - ob.loc = tuple(insert.loc) - transform(insert.extrusion, ob) - ob.RotZ += radians(insert.rotation) - ob.size = tuple(insert.scale) - - return ob -drawBlocks.draw = drawBlock - - -drawFaces = Drawer() -def drawFace(face, curves=False): - """Do all the specific things needed to import 3d faces into Blender.""" - # Generate the geometery - points = face.points - if len(face.points) > 3: - faces = [[0, 1, 2, 3]] - else: - faces = [[0, 1, 2]] - - me = Mesh.New('line') # create a new mesh - - me.verts.extend(points) # add vertices to mesh - me.faces.extend(faces) # add faces to the mesh - - # Now Create an object - ob = Object.New('Mesh', '3dface') # link mesh to an object - ob.link(me) - - return ob -drawFaces.draw = drawFace # Here are some alternate drawing functions for creating curve geometery. def drawCurveCircle(circle): @@ -994,50 +2260,5 @@ def drawCurveArc(arc): return ob -type_map = { - 'line':drawLines, - 'lwpolyline':drawLWpolylines, - 'polyline':drawPolylines, - 'text':drawTexts, - 'mtext':drawMtexts, - 'circle':drawCircles, - 'arc':drawArcs, - 'ellipse':drawEllipses, - 'insert':drawBlocks, - '3dface':drawFaces -} - - if __name__ == "__main__": Window.FileSelector(main, 'Import a DXF file', '*.dxf') - - -""" -# For testing compatibility -if 1: - # DEBUG ONLY - TIME= Blender.sys.time() - import os - print 'Searching for files' - os.system('find /metavr/ -iname "*.dxf" > /tmp/tempdxf_list') - print '...Done' - file= open('/tmp/tempdxf_list', 'r') - lines= file.readlines() - file.close() - - def between(v,a,b): - if v <= max(a,b) and v >= min(a,b): - return True - return False - - for i, _file in enumerate(lines): - if between(i, 50,60): - _file= _file[:-1] - print 'Importing', _file, '\nNUMBER', i, 'of', len(lines) - _file_name= _file.split('/')[-1].split('\\')[-1] - newScn= Scene.New(_file_name) - newScn.makeCurrent() - main(_file) - - print 'TOTAL TIME: %.6f' % (Blender.sys.time() - TIME) -""" \ No newline at end of file