diff options
author | Remigiusz Fiedler <migius@gmx.net> | 2008-10-08 00:34:24 +0400 |
---|---|---|
committer | Remigiusz Fiedler <migius@gmx.net> | 2008-10-08 00:34:24 +0400 |
commit | 8b2f089c6eb77fd979a3d75461405f2236f1d877 (patch) | |
tree | 29a2fb3504234dd8e8f04aeab2afbd1f50557720 /release | |
parent | d8758e32a06f76798285c300d2eb37595e1ba646 (diff) |
DXF-Exporter update
v1.27 - 2008.10.07 by migius
- exclude Stani's DXF-Library to extern module
- add "hidden mode" substitut: back-faces removal
- add support for mesh ->POLYFACE
- optimized code for "Flat" procedure
- modif FACE class for r12
- add mesh-polygon -> Bezier-curve converter (Yorik's code)
- add support for curves ->POLYLINEs
- add "3d-View to Flat" - geometry projection to XY-plane
Diffstat (limited to 'release')
-rw-r--r-- | release/scripts/bpymodules/dxfLibrary.py | 708 | ||||
-rw-r--r-- | release/scripts/export_dxf.py | 1029 |
2 files changed, 996 insertions, 741 deletions
diff --git a/release/scripts/bpymodules/dxfLibrary.py b/release/scripts/bpymodules/dxfLibrary.py new file mode 100644 index 00000000000..5c63e7f5bf5 --- /dev/null +++ b/release/scripts/bpymodules/dxfLibrary.py @@ -0,0 +1,708 @@ +#dxfLibrary.py : provides functions for generating DXF files +# -------------------------------------------------------------------------- +__version__ = "v1.27beta - 2008.10.05" +__author__ = "Stani Michiels(Stani), Remigiusz Fiedler(migius)" +__license__ = "GPL" +__url__ = "http://wiki.blender.org/index.php/Scripts/Manual/Export/autodesk_dxf" +__bpydoc__ ="""The script exports geometry data to DXF format r12 version. + +Copyright %s +Version %s +License %s +Homepage %s + +See the homepage for documentation. +url: + +IDEAs: +- + +TODO: +- + +History +v1.27 - 2008.10.07 by migius +- beautifying output code: keys whitespace prefix +- refactoring DXF-strings format: NewLine moved to the end of +v1.26 - 2008.10.05 by migius +- modif POLYLINE to support POLYFACE +v1.25 - 2008.09.28 by migius +- modif FACE class for r12 +v1.24 - 2008.09.27 by migius +- modif POLYLINE class for r12 +- changing output format from r9 to r12(AC1009) +v1.1 (20/6/2005) by www.stani.be/python/sdxf +- Python library to generate dxf drawings +______________________________________________________________ +""" % (__author__,__version__,__license__,__url__) + +# -------------------------------------------------------------------------- +# DXF Library: copyright (C) 2005 by Stani Michiels (AKA Stani) +# 2008 modif by Remigiusz Fiedler (AKA migius) +# -------------------------------------------------------------------------- +# ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# ***** END GPL LICENCE BLOCK ***** + + +#import Blender +#from Blender import Mathutils, Window, Scene, sys, Draw +#import BPyMessages + +try: + import copy + #from struct import pack +except: + copy = None + +####1) Private (only for developpers) +_HEADER_POINTS=['insbase','extmin','extmax'] + +#---helper functions----------------------------------- +def _point(x,index=0): + """Convert tuple to a dxf point""" + #print 'deb: _point=', x #------------- + return '\n'.join([' %s\n%s'%((i+1)*10+index,x[i]) for i in range(len(x))]) + +def _points(plist): + """Convert a list of tuples to dxf points""" + out = '\n'.join([_point(plist[i],i)for i in range(len(plist))]) + #print 'deb: points=\n', out #------------------- + return out + +#---base classes---------------------------------------- +class _Call: + """Makes a callable class.""" + def copy(self): + """Returns a copy.""" + return copy.deepcopy(self) + + def __call__(self,**attrs): + """Returns a copy with modified attributes.""" + copied=self.copy() + for attr in attrs:setattr(copied,attr,attrs[attr]) + return copied + +#------------------------------------------------------- +class _Entity(_Call): + """Base class for _common group codes for entities.""" + def __init__(self,color=None,extrusion=None,layer='0', + lineType=None,lineTypeScale=None,lineWeight=None, + thickness=None,parent=None): + """None values will be omitted.""" + self.color = color + self.extrusion = extrusion + self.layer = layer + self.lineType = lineType + self.lineTypeScale = lineTypeScale + self.lineWeight = lineWeight + self.thickness = thickness + self.parent = parent + + def _common(self): + """Return common group codes as a string.""" + if self.parent:parent=self.parent + else:parent=self + result ='' + if parent.layer!=None: result+=' 8\n%s\n'%parent.layer + if parent.color!=None: result+=' 62\n%s\n'%parent.color + if parent.extrusion!=None: result+='%s\n'%_point(parent.extrusion,200) + if parent.lineType!=None: result+=' 6\n%s\n'%parent.lineType + #TODO: if parent.lineWeight!=None: result+='370\n%s\n'%parent.lineWeight + if parent.lineTypeScale!=None: result+=' 48\n%s\n'%parent.lineTypeScale + if parent.thickness!=None: result+=' 39\n%s\n'%parent.thickness + return result + +#-------------------------- +class _Entities: + """Base class to deal with composed objects.""" + def __dxf__(self): + return [] + + def __str__(self): + return ''.join([str(x) for x in self.__dxf__()]) + +#-------------------------- +class _Collection(_Call): + """Base class to expose entities methods to main object.""" + def __init__(self,entities=[]): + self.entities=copy.copy(entities) + #link entities methods to drawing + for attr in dir(self.entities): + if attr[0]!='_': + attrObject=getattr(self.entities,attr) + if callable(attrObject): + setattr(self,attr,attrObject) + +####2) Constants +#---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 + +####3) Classes +#---entitities ----------------------------------------------- +#-------------------------- +class Arc(_Entity): + """Arc, angles in degrees.""" + def __init__(self,center=(0,0,0),radius=1, + startAngle=0.0,endAngle=90,**common): + """Angles in degrees.""" + _Entity.__init__(self,**common) + self.center=center + self.radius=radius + self.startAngle=startAngle + self.endAngle=endAngle + def __str__(self): + return ' 0\nARC\n%s%s\n 40\n%s\n 50\n%s\n 51\n%s\n'%\ + (self._common(),_point(self.center), + self.radius,self.startAngle,self.endAngle) + +#----------------------------------------------- +class Circle(_Entity): + """Circle""" + def __init__(self,center=(0,0,0),radius=1,**common): + _Entity.__init__(self,**common) + self.center=center + self.radius=radius + def __str__(self): + return ' 0\nCIRCLE\n%s%s\n 40\n%s\n'%\ + (self._common(),_point(self.center),self.radius) + +#----------------------------------------------- +class Face(_Entity): + """3dface""" + def __init__(self,points,**common): + _Entity.__init__(self,**common) + if len(points)<4: #fix for r12 format + points.append(points[-1]) + self.points=points + + def __str__(self): + out = ' 0\n3DFACE\n%s%s\n' %(self._common(),_points(self.points)) + #print 'deb:out=', out #------------------- + return out + +#----------------------------------------------- +class Insert(_Entity): + """Block instance.""" + def __init__(self,name,point=(0,0,0), + xscale=None,yscale=None,zscale=None, + cols=None,colspacing=None,rows=None,rowspacing=None, + rotation=None, + **common): + _Entity.__init__(self,**common) + self.name=name + self.point=point + self.xscale=xscale + self.yscale=yscale + self.zscale=zscale + self.cols=cols + self.colspacing=colspacing + self.rows=rows + self.rowspacing=rowspacing + self.rotation=rotation + + def __str__(self): + result=' 0\nINSERT\n 2\n%s\n%s\n%s\n'%\ + (self.name,self._common(),_point(self.point)) + if self.xscale!=None:result+=' 41\n%s\n'%self.xscale + if self.yscale!=None:result+=' 42\n%s\n'%self.yscale + if self.zscale!=None:result+=' 43\n%s\n'%self.zscale + if self.rotation:result+=' 50\n%s\n'%self.rotation + if self.cols!=None:result+=' 70\n%s\n'%self.cols + if self.colspacing!=None:result+=' 44\n%s\n'%self.colspacing + if self.rows!=None:result+=' 71\n%s\n'%self.rows + if self.rowspacing!=None:result+=' 45\n%s\n'%self.rowspacing + return result + +#----------------------------------------------- +class Line(_Entity): + """Line""" + def __init__(self,points,**common): + _Entity.__init__(self,**common) + self.points=points + def __str__(self): + return ' 0\nLINE\n%s%s\n' %( + self._common(), _points(self.points)) + + +#----------------------------------------------- +class PolyLine(_Entity): + def __init__(self,points,org_point=[0,0,0],flag=0,width=None,**common): + _Entity.__init__(self,**common) + self.points=points + self.org_point=org_point + self.flag=flag + if self.flag==64: + self.points=points[0] + self.faces=points[1] + self.p_count=len(self.points) + self.f_count=len(self.faces) + self.width=width + + def __str__(self): + result= ' 0\nPOLYLINE\n%s 70\n%s\n' %(self._common(),self.flag) + #print 'deb: self._common()', self._common() #---------- + result+=' 66\n1\n' + result+='%s\n' %_point(self.org_point) + if self.flag==64: + result+=' 71\n%s\n' %self.p_count + result+=' 72\n%s\n' %self.f_count + for point in self.points: + result+=' 0\nVERTEX\n' + result+=' 8\n%s\n' %self.layer + result+='%s\n' %_point(point) + if self.flag==64: result+=' 70\n192\n' + if self.width: result+=' 40\n%s\n 41\n%s\n' %(self.width,self.width) + if self.flag==64: + for face in self.faces: + result+=' 0\nVERTEX\n' + result+=' 8\n%s\n' %self.layer + result+='%s\n' %_point(self.org_point) + result+=' 70\n128\n' + result+=' 71\n%s\n' %face[0] + result+=' 72\n%s\n' %face[1] + result+=' 73\n%s\n' %face[2] + if len(face)==4: result+=' 74\n%s\n' %face[3] + result+=' 0\nSEQEND\n' + result+=' 8\n%s\n' %self.layer + return result + +#----------------------------------------------- +class Point(_Entity): + """Colored solid fill.""" + def __init__(self,points=None,**common): + _Entity.__init__(self,**common) + self.points=points + +#----------------------------------------------- +class Solid(_Entity): + """Colored solid fill.""" + def __init__(self,points=None,**common): + _Entity.__init__(self,**common) + self.points=points + def __str__(self): + return ' 0\nSOLID\n%s%s\n' %(self._common(), + _points(self.points[:2]+[self.points[3],self.points[2]]) + ) + + +#----------------------------------------------- +class Text(_Entity): + """Single text line.""" + def __init__(self,text='',point=(0,0,0),alignment=None, + flag=None,height=1,justifyhor=None,justifyver=None, + rotation=None,obliqueAngle=None,style=None,xscale=None,**common): + _Entity.__init__(self,**common) + self.text=text + self.point=point + self.alignment=alignment + self.flag=flag + self.height=height + self.justifyhor=justifyhor + self.justifyver=justifyver + self.rotation=rotation + self.obliqueAngle=obliqueAngle + self.style=style + self.xscale=xscale + def __str__(self): + result= ' 0\nTEXT\n%s%s\n 40\n%s\n 1\n%s\n'%\ + (self._common(),_point(self.point),self.height,self.text) + if self.rotation: result+=' 50\n%s\n'%self.rotation + if self.xscale: result+=' 41\n%s\n'%self.xscale + if self.obliqueAngle: result+=' 51\n%s\n'%self.obliqueAngle + if self.style: result+=' 7\n%s\n'%self.style + if self.flag: result+=' 71\n%s\n'%self.flag + if self.justifyhor: result+=' 72\n%s\n'%self.justifyhor + #TODO: if self.alignment: result+='%s\n'%_point(self.alignment,1) + if self.justifyver: result+=' 73\n%s\n'%self.justifyver + return result + +#----------------------------------------------- +class Mtext(Text): + """Surrogate for mtext, generates some Text instances.""" + def __init__(self,text='',point=(0,0,0),width=250,spacingFactor=1.5,down=0,spacingWidth=None,**options): + Text.__init__(self,text=text,point=point,**options) + if down:spacingFactor*=-1 + self.spacingFactor=spacingFactor + self.spacingWidth=spacingWidth + self.width=width + self.down=down + def __str__(self): + texts=self.text.replace('\r\n','\n').split('\n') + if not self.down:texts.reverse() + result='' + x=y=0 + if self.spacingWidth:spacingWidth=self.spacingWidth + else:spacingWidth=self.height*self.spacingFactor + for text in texts: + while text: + result+='%s\n'%Text(text[:self.width], + point=(self.point[0]+x*spacingWidth, + self.point[1]+y*spacingWidth, + self.point[2]), + alignment=self.alignment,flag=self.flag,height=self.height, + justifyhor=self.justifyhor,justifyver=self.justifyver, + rotation=self.rotation,obliqueAngle=self.obliqueAngle, + style=self.style,xscale=self.xscale,parent=self + ) + text=text[self.width:] + if self.rotation:x+=1 + else:y+=1 + return result[1:] + +#----------------------------------------------- +##class _Mtext(_Entity): +## """Mtext not functioning for minimal dxf.""" +## def __init__(self,text='',point=(0,0,0),attachment=1, +## charWidth=None,charHeight=1,direction=1,height=100,rotation=0, +## spacingStyle=None,spacingFactor=None,style=None,width=100, +## xdirection=None,**common): +## _Entity.__init__(self,**common) +## self.text=text +## self.point=point +## self.attachment=attachment +## self.charWidth=charWidth +## self.charHeight=charHeight +## self.direction=direction +## self.height=height +## self.rotation=rotation +## self.spacingStyle=spacingStyle +## self.spacingFactor=spacingFactor +## self.style=style +## self.width=width +## self.xdirection=xdirection +## def __str__(self): +## input=self.text +## text='' +## while len(input)>250: +## text+='3\n%s\n'%input[:250] +## input=input[250:] +## text+='1\n%s\n'%input +## result= '0\nMTEXT\n%s\n%s\n40\n%s\n41\n%s\n71\n%s\n72\n%s%s\n43\n%s\n50\n%s\n'%\ +## (self._common(),_point(self.point),self.charHeight,self.width, +## self.attachment,self.direction,text, +## self.height, +## self.rotation) +## if self.style:result+='7\n%s\n'%self.style +## if self.xdirection:result+='%s\n'%_point(self.xdirection,1) +## if self.charWidth:result+='42\n%s\n'%self.charWidth +## if self.spacingStyle:result+='73\n%s\n'%self.spacingStyle +## if self.spacingFactor:result+='44\n%s\n'%self.spacingFactor +## return result + +#---tables --------------------------------------------------- +#----------------------------------------------- +class Block(_Collection): + """Use list methods to add entities, eg append.""" + def __init__(self,name,layer='0',flag=0,base=(0,0,0),entities=[]): + self.entities=copy.copy(entities) + _Collection.__init__(self,entities) + self.layer=layer + self.name=name + self.flag=0 + self.base=base + def __str__(self): + e=''.join([str(x)for x in self.entities]) + return ' 0\nBLOCK\n 8\n%s\n 2\n%s\n 70\n%s\n%s\n 3\n%s\n%s 0\nENDBLK\n'%\ + (self.layer,self.name.upper(),self.flag,_point(self.base),self.name.upper(),e) + +#----------------------------------------------- +class Layer(_Call): + """Layer""" + def __init__(self,name='pydxf',color=7,lineType='continuous',flag=64): + self.name=name + self.color=color + self.lineType=lineType + self.flag=flag + def __str__(self): + return ' 0\nLAYER\n 2\n%s\n 70\n%s\n 62\n%s\n 6\n%s\n'%\ + (self.name.upper(),self.flag,self.color,self.lineType) + +#----------------------------------------------- +class LineType(_Call): + """Custom linetype""" + def __init__(self,name='continuous',description='Solid line',elements=[],flag=64): + # TODO: Implement lineType elements + self.name=name + self.description=description + self.elements=copy.copy(elements) + self.flag=flag + def __str__(self): + return ' 0\nLTYPE\n 2\n%s\n 70\n%s\n 3\n%s\n 72\n65\n 73\n%s\n 40\n0.0\n'%\ + (self.name.upper(),self.flag,self.description,len(self.elements)) + +#----------------------------------------------- +class Style(_Call): + """Text style""" + def __init__(self,name='standard',flag=0,height=0,widthFactor=40,obliqueAngle=50, + mirror=0,lastHeight=1,font='arial.ttf',bigFont=''): + self.name=name + self.flag=flag + self.height=height + self.widthFactor=widthFactor + self.obliqueAngle=obliqueAngle + self.mirror=mirror + self.lastHeight=lastHeight + self.font=font + self.bigFont=bigFont + def __str__(self): + return ' 0\nSTYLE\n 2\n%s\n 70\n%s\n 40\n%s\n 41\n%s\n 50\n%s\n 71\n%s\n 42\n%s\n 3\n%s\n 4\n%s\n'%\ + (self.name.upper(),self.flag,self.flag,self.widthFactor, + self.obliqueAngle,self.mirror,self.lastHeight, + self.font.upper(),self.bigFont.upper()) + +#----------------------------------------------- +class View(_Call): + def __init__(self,name,flag=0,width=1,height=1,center=(0.5,0.5), + direction=(0,0,1),target=(0,0,0),lens=50, + frontClipping=0,backClipping=0,twist=0,mode=0): + self.name=name + self.flag=flag + self.width=width + self.height=height + self.center=center + self.direction=direction + self.target=target + self.lens=lens + self.frontClipping=frontClipping + self.backClipping=backClipping + self.twist=twist + self.mode=mode + def __str__(self): + return ' 0\nVIEW\n 2\n%s\n 70\n%s\n 40\n%s\n%s\n 41\n%s\n%s\n%s\n 42\n%s\n 43\n%s\n 44\n%s\n 50\n%s\n 71\n%s\n'%\ + (self.name,self.flag,self.height,_point(self.center),self.width, + _point(self.direction,1),_point(self.target,2),self.lens, + self.frontClipping,self.backClipping,self.twist,self.mode) + +#----------------------------------------------- +def ViewByWindow(name,leftBottom=(0,0),rightTop=(1,1),**options): + width=abs(rightTop[0]-leftBottom[0]) + height=abs(rightTop[1]-leftBottom[1]) + center=((rightTop[0]+leftBottom[0])*0.5,(rightTop[1]+leftBottom[1])*0.5) + return View(name=name,width=width,height=height,center=center,**options) + +#---drawing +#----------------------------------------------- +class Drawing(_Collection): + """Dxf drawing. Use append or any other list methods to add objects.""" + def __init__(self,insbase=(0.0,0.0,0.0),extmin=(0.0,0.0),extmax=(0.0,0.0), + layers=[Layer()],linetypes=[LineType()],styles=[Style()],blocks=[], + views=[],entities=None,fileName='test.dxf'): + # TODO: replace list with None,arial + if not entities:entities=[] + _Collection.__init__(self,entities) + self.insbase=insbase + self.extmin=extmin + self.extmax=extmax + self.layers=copy.copy(layers) + self.linetypes=copy.copy(linetypes) + self.styles=copy.copy(styles) + self.views=copy.copy(views) + self.blocks=copy.copy(blocks) + self.fileName=fileName + #private + #self.acadver='9\n$ACADVER\n1\nAC1006\n' + self.acadver=' 9\n$ACADVER\n 1\nAC1009\n' + """DXF AutoCAD-Release format codes + AC1021 2008, 2007 + AC1018 2006, 2005, 2004 + AC1015 2002, 2000i, 2000 + AC1014 R14,14.01 + AC1012 R13 + AC1009 R12,11 + AC1006 R10 + AC1004 R9 + AC1002 R2.6 + AC1.50 R2.05 + """ + + def _name(self,x): + """Helper function for self._point""" + return ' 9\n$%s\n' %x.upper() + + def _point(self,name,x): + """Point setting from drawing like extmin,extmax,...""" + return '%s%s' %(self._name(name),_point(x)) + + def _section(self,name,x): + """Sections like tables,blocks,entities,...""" + if x: xstr=''.join(x) + else: xstr='' + return ' 0\nSECTION\n 2\n%s\n%s 0\nENDSEC\n'%(name.upper(),xstr) + + def _table(self,name,x): + """Tables like ltype,layer,style,...""" + if x: xstr=''.join(x) + else: xstr='' + return ' 0\nTABLE\n 2\n%s\n 70\n%s\n%s 0\nENDTAB\n'%(name.upper(),len(x),xstr) + + def __str__(self): + """Returns drawing as dxf string.""" + header=[self.acadver]+[self._point(attr,getattr(self,attr))+'\n' for attr in _HEADER_POINTS] + header=self._section('header',header) + + tables=[self._table('ltype',[str(x) for x in self.linetypes]), + self._table('layer',[str(x) for x in self.layers]), + self._table('style',[str(x) for x in self.styles]), + self._table('view',[str(x) for x in self.views]), + ] + tables=self._section('tables',tables) + + blocks=self._section('blocks',[str(x) for x in self.blocks]) + + entities=self._section('entities',[str(x) for x in self.entities]) + + all=''.join([header,tables,blocks,entities,' 0\nEOF\n']) + return all + + def saveas(self,fileName): + self.fileName=fileName + self.save() + + def save(self): + test=open(self.fileName,'w') + test.write(str(self)) + test.close() + + +#---extras +#----------------------------------------------- +class Rectangle(_Entity): + """Rectangle, creates lines.""" + def __init__(self,point=(0,0,0),width=1,height=1,solid=None,line=1,**common): + _Entity.__init__(self,**common) + self.point=point + self.width=width + self.height=height + self.solid=solid + self.line=line + def __str__(self): + result='' + points=[self.point,(self.point[0]+self.width,self.point[1],self.point[2]), + (self.point[0]+self.width,self.point[1]+self.height,self.point[2]), + (self.point[0],self.point[1]+self.height,self.point[2]),self.point] + if self.solid: + result+= Solid(points=points[:-1],parent=self.solid) + if self.line: + for i in range(4): + result+= Line(points=[points[i],points[i+1]],parent=self) + return result[1:] + +#----------------------------------------------- +class LineList(_Entity): + """Like polyline, but built of individual lines.""" + def __init__(self,points=[],org_point=[0,0,0],closed=0,**common): + _Entity.__init__(self,**common) + self.closed=closed + self.points=copy.copy(points) + def __str__(self): + if self.closed:points=self.points+[self.points[0]] + else: points=self.points + result='' + for i in range(len(points)-1): + result+= Line(points=[points[i],points[i+1]],parent=self) + return result[1:] + +#----------------------------------------------------- +def test(): + #Blocks + b=Block('test') + b.append(Solid(points=[(0,0,0),(1,0,0),(1,1,0),(0,1,0)],color=1)) + b.append(Arc(center=(1,0,0),color=2)) + + #Drawing + d=Drawing() + #tables + d.blocks.append(b) #table blocks + d.styles.append(Style()) #table styles + d.views.append(View('Normal')) #table view + d.views.append(ViewByWindow('Window',leftBottom=(1,0),rightTop=(2,1))) #idem + + #entities + d.append(Circle(center=(1,1,0),color=3)) + d.append(Face(points=[(0,0,0),(1,0,0),(1,1,0),(0,1,0)],color=4)) + d.append(Insert('test',point=(3,3,3),cols=5,colspacing=2)) + d.append(Line(points=[(0,0,0),(1,1,1)])) + d.append(Mtext('Click on Ads\nmultiple lines with mtext',point=(1,1,1),color=5,rotation=90)) + d.append(Text('Please donate!',point=(3,0,1))) + d.append(Rectangle(point=(2,2,2),width=4,height=3,color=6,solid=Solid(color=2))) + d.append(Solid(points=[(4,4,0),(5,4,0),(7,8,0),(9,9,0)],color=3)) + d.append(PolyLine(points=[(1,1,1),(2,1,1),(2,2,1),(1,2,1)],closed=1,color=1)) + + #d.saveas('c:\\test.dxf') + d.saveas('test.dxf') + + +#----------------------------------------------------- +if __name__=='__main__': + if not copy: + Draw.PupMenu('Error%t|This script requires a full python install') + main() +
\ No newline at end of file diff --git a/release/scripts/export_dxf.py b/release/scripts/export_dxf.py index 66586156359..061e29b81c7 100644 --- a/release/scripts/export_dxf.py +++ b/release/scripts/export_dxf.py @@ -7,52 +7,65 @@ Tooltip: 'Export geometry to DXF-r12 (Drawing eXchange Format).' """ -__version__ = "v1.25beta - 2008.09.28" -__author__ = "Stani & migius(Remigiusz Fiedler)" +__version__ = "v1.27beta - 2008.10.07" +__author__ = "Remigiusz Fiedler (AKA migius)" __license__ = "GPL" __url__ = "http://wiki.blender.org/index.php/Scripts/Manual/Export/autodesk_dxf" __bpydoc__ ="""The script exports Blender geometry to DXF format r12 version. -Copyright %s Version %s +Copyright %s License %s -Homepage %s + +extern dependances: dxfLibrary.py See the homepage for documentation. -url: -""" % (__author__,__version__,__license__,__url__) +url: %s -""" IDEAs: - - correct normals for POLYLINE-POLYFACE objects via correct point-order - - HPGL output for 2d and flattened3d content + - correct normals for POLYLINE-POLYFACE via proper point-order + - HPGL output for 2d and flattened 3d content TODO: - - support hierarchies: groups, instances, parented structures - - support 210-code (3d orientation vector) - - presets for architectural scales +- optimize back-faces removal (probably needs matrix transform) +- optimize POLYFACE routine: remove double-vertices +- optimize POLYFACE routine: remove unused vertices +- support hierarchies: groups, instances, parented structures +- support 210-code (3d orientation vector) +- presets for architectural scales +- write drawing extends for automatic view positioning in CAD History +v1.27 - 2008.10.07 by migius +- exclude Stani's DXF-Library to extern module +v1.26 - 2008.10.05 by migius +- add "hidden mode" substitut: back-faces removal +- add support for mesh ->POLYFACE +- optimized code for "Flat" procedure v1.25 - 2008.09.28 by migius - - modif FACE class for r12 - - add mesh-polygon -> Bezier-curve converter (Yorik's code) - - add support for curves ->POLYLINEs - - add "3d-View to Flat" - geometry projection to XY-plane +- modif FACE class for r12 +- add mesh-polygon -> Bezier-curve converter (Yorik's code) +- add support for curves ->POLYLINEs +- add "3d-View to Flat" - geometry projection to XY-plane v1.24 - 2008.09.27 by migius - - add start UI with preferences - - modif POLYLINE class for r12 - - changing output format from r9 to r12(AC1009) +- add start UI with preferences +- modif POLYLINE class for r12 +- changing output format from r9 to r12(AC1009) v1.23 - 2008.09.26 by migius - - add finish message-box +- add finish message-box v1.22 - 2008.09.26 by migius - - add support for curves ->LINEs - - add support for mesh-edges ->LINEs +- add support for curves ->LINEs +- add support for mesh-edges ->LINEs v1.21 - 2008.06.04 by migius - - initial adaptation for Blender -v1.1 (20/6/2005) by www.stani.be/python/sdxf - - Python library to generate dxf drawings +- initial adaptation for Blender +v1.1 (20/6/2005) by Stani Michiels www.stani.be/python/sdxf +- Python library to generate dxf drawings ______________________________________________________________ -""" +""" % (__author__,__version__,__license__,__url__) + +# -------------------------------------------------------------------------- +# Script copyright (C) 2008 Remigiusz Fiedler (AKA migius) +# -------------------------------------------------------------------------- # ***** BEGIN GPL LICENSE BLOCK ***** # # This program is free software; you can redistribute it and/or @@ -76,602 +89,254 @@ import Blender from Blender import Mathutils, Window, Scene, sys, Draw import BPyMessages -try: - import copy - #from struct import pack -except: - copy = None - -####1) Private (only for developpers) -_HEADER_POINTS=['insbase','extmin','extmax'] - -#---helper functions----------------------------------- -def _point(x,index=0): - """Convert tuple to a dxf point""" - #print '_point=', x #------------- - return '\n'.join(['%s\n%s'%((i+1)*10+index,x[i]) for i in range(len(x))]) - -def _points(plist): - """Convert a list of tuples to dxf points""" - return [_point(plist[i],i)for i in range(len(plist))] - -#---base classes---------------------------------------- -class _Call: - """Makes a callable class.""" - def copy(self): - """Returns a copy.""" - return copy.deepcopy(self) - - def __call__(self,**attrs): - """Returns a copy with modified attributes.""" - copied=self.copy() - for attr in attrs:setattr(copied,attr,attrs[attr]) - return copied - -#------------------------------------------------------- -class _Entity(_Call): - """Base class for _common group codes for entities.""" - def __init__(self,color=None,extrusion=None,layer='0', - lineType=None,lineTypeScale=None,lineWeight=None, - thickness=None,parent=None): - """None values will be omitted.""" - self.color = color - self.extrusion = extrusion - self.layer = layer - self.lineType = lineType - self.lineTypeScale = lineTypeScale - self.lineWeight = lineWeight - self.thickness = thickness - self.parent = parent - - def _common(self): - """Return common group codes as a string.""" - if self.parent:parent=self.parent - else:parent=self - result='8\n%s'%parent.layer - if parent.color!=None: result+='\n62\n%s'%parent.color - if parent.extrusion!=None: result+='\n%s'%_point(parent.extrusion,200) - if parent.lineType!=None: result+='\n6\n%s'%parent.lineType - if parent.lineWeight!=None: result+='\n370\n%s'%parent.lineWeight - if parent.lineTypeScale!=None: result+='\n48\n%s'%parent.lineTypeScale - if parent.thickness!=None: result+='\n39\n%s'%parent.thickness - return result - -#-------------------------- -class _Entities: - """Base class to deal with composed objects.""" - def __dxf__(self): - return [] - - def __str__(self): - return '\n'.join([str(x) for x in self.__dxf__()]) - -#-------------------------- -class _Collection(_Call): - """Base class to expose entities methods to main object.""" - def __init__(self,entities=[]): - self.entities=copy.copy(entities) - #link entities methods to drawing - for attr in dir(self.entities): - if attr[0]!='_': - attrObject=getattr(self.entities,attr) - if callable(attrObject): - setattr(self,attr,attrObject) - -####2) Constants -#---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 - -####3) Classes -#---entitities ----------------------------------------------- -#-------------------------- -class Arc(_Entity): - """Arc, angles in degrees.""" - def __init__(self,center=(0,0,0),radius=1, - startAngle=0.0,endAngle=90,**common): - """Angles in degrees.""" - _Entity.__init__(self,**common) - self.center=center - self.radius=radius - self.startAngle=startAngle - self.endAngle=endAngle - def __str__(self): - return '0\nARC\n%s\n%s\n40\n%s\n50\n%s\n51\n%s'%\ - (self._common(),_point(self.center), - self.radius,self.startAngle,self.endAngle) - -#----------------------------------------------- -class Circle(_Entity): - """Circle""" - def __init__(self,center=(0,0,0),radius=1,**common): - _Entity.__init__(self,**common) - self.center=center - self.radius=radius - def __str__(self): - return '0\nCIRCLE\n%s\n%s\n40\n%s'%\ - (self._common(),_point(self.center),self.radius) - -#----------------------------------------------- -class Face(_Entity): - """3dface""" - def __init__(self,points,**common): - _Entity.__init__(self,**common) - if len(points)<4: #fix for r12 format - points.append(points[-1]) - self.points=points - - def __str__(self): - return '\n'.join(['0\n3DFACE',self._common()]+ - _points(self.points) - ) -#----------------------------------------------- -class Insert(_Entity): - """Block instance.""" - def __init__(self,name,point=(0,0,0), - xscale=None,yscale=None,zscale=None, - cols=None,colspacing=None,rows=None,rowspacing=None, - rotation=None, - **common): - _Entity.__init__(self,**common) - self.name=name - self.point=point - self.xscale=xscale - self.yscale=yscale - self.zscale=zscale - self.cols=cols - self.colspacing=colspacing - self.rows=rows - self.rowspacing=rowspacing - self.rotation=rotation - - def __str__(self): - result='0\nINSERT\n2\n%s\n%s\n%s'%\ - (self.name,self._common(),_point(self.point)) - if self.xscale!=None:result+='\n41\n%s'%self.xscale - if self.yscale!=None:result+='\n42\n%s'%self.yscale - if self.zscale!=None:result+='\n43\n%s'%self.zscale - if self.rotation:result+='\n50\n%s'%self.rotation - if self.cols!=None:result+='\n70\n%s'%self.cols - if self.colspacing!=None:result+='\n44\n%s'%self.colspacing - if self.rows!=None:result+='\n71\n%s'%self.rows - if self.rowspacing!=None:result+='\n45\n%s'%self.rowspacing - return result - -#----------------------------------------------- -class Line(_Entity): - """Line""" - def __init__(self,points,**common): - _Entity.__init__(self,**common) - self.points=points - def __str__(self): - return '\n'.join(['0\nLINE',self._common()]+ - _points(self.points)) - -#----------------------------------------------- -class PolyLine(_Entity): - #TODO: Finish polyline (now implemented as a series of lines) - def __init__(self,points,org_point=[0,0,0],flag=0,width=None,**common): - _Entity.__init__(self,**common) - self.points=points - self.org_point=org_point - self.flag=flag - self.width=width - def __str__(self): - result= '0\nPOLYLINE\n%s\n70\n%s' %(self._common(),self.flag) - #print 'self._common()', self._common() #---------- - result+='\n66\n1' - result+='\n%s' %_point(self.org_point) - for point in self.points: - result+='\n0\nVERTEX' - result+='\n8\n%s' %self.layer - result+='\n%s' %_point(point) - if self.width:result+='\n40\n%s\n41\n%s' %(self.width,self.width) - result+='\n0\nSEQEND' - result+='\n8\n%s' %self.layer - return result - -#----------------------------------------------- -class Point(_Entity): - """Colored solid fill.""" - def __init__(self,points=None,**common): - _Entity.__init__(self,**common) - self.points=points - -#----------------------------------------------- -class Solid(_Entity): - """Colored solid fill.""" - def __init__(self,points=None,**common): - _Entity.__init__(self,**common) - self.points=points - def __str__(self): - return '\n'.join(['0\nSOLID',self._common()]+ - _points(self.points[:2]+[self.points[3],self.points[2]]) - ) - -#----------------------------------------------- -class Text(_Entity): - """Single text line.""" - def __init__(self,text='',point=(0,0,0),alignment=None, - flag=None,height=1,justifyhor=None,justifyver=None, - rotation=None,obliqueAngle=None,style=None,xscale=None,**common): - _Entity.__init__(self,**common) - self.text=text - self.point=point - self.alignment=alignment - self.flag=flag - self.height=height - self.justifyhor=justifyhor - self.justifyver=justifyver - self.rotation=rotation - self.obliqueAngle=obliqueAngle - self.style=style - self.xscale=xscale - def __str__(self): - result= '0\nTEXT\n%s\n%s\n40\n%s\n1\n%s'%\ - (self._common(),_point(self.point),self.height,self.text) - if self.rotation:result+='\n50\n%s'%self.rotation - if self.xscale:result+='\n41\n%s'%self.xscale - if self.obliqueAngle:result+='\n51\n%s'%self.obliqueAngle - if self.style:result+='\n7\n%s'%self.style - if self.flag:result+='\n71\n%s'%self.flag - if self.justifyhor:result+='\n72\n%s'%self.justifyhor - if self.alignment:result+='\n%s'%_point(self.alignment,1) - if self.justifyver:result+='\n73\n%s'%self.justifyver - return result - -#----------------------------------------------- -class Mtext(Text): - """Surrogate for mtext, generates some Text instances.""" - def __init__(self,text='',point=(0,0,0),width=250,spacingFactor=1.5,down=0,spacingWidth=None,**options): - Text.__init__(self,text=text,point=point,**options) - if down:spacingFactor*=-1 - self.spacingFactor=spacingFactor - self.spacingWidth=spacingWidth - self.width=width - self.down=down - def __str__(self): - texts=self.text.replace('\r\n','\n').split('\n') - if not self.down:texts.reverse() - result='' - x=y=0 - if self.spacingWidth:spacingWidth=self.spacingWidth - else:spacingWidth=self.height*self.spacingFactor - for text in texts: - while text: - result+='\n%s'%Text(text[:self.width], - point=(self.point[0]+x*spacingWidth, - self.point[1]+y*spacingWidth, - self.point[2]), - alignment=self.alignment,flag=self.flag,height=self.height, - justifyhor=self.justifyhor,justifyver=self.justifyver, - rotation=self.rotation,obliqueAngle=self.obliqueAngle, - style=self.style,xscale=self.xscale,parent=self - ) - text=text[self.width:] - if self.rotation:x+=1 - else:y+=1 - return result[1:] - -#----------------------------------------------- -##class _Mtext(_Entity): -## """Mtext not functioning for minimal dxf.""" -## def __init__(self,text='',point=(0,0,0),attachment=1, -## charWidth=None,charHeight=1,direction=1,height=100,rotation=0, -## spacingStyle=None,spacingFactor=None,style=None,width=100, -## xdirection=None,**common): -## _Entity.__init__(self,**common) -## self.text=text -## self.point=point -## self.attachment=attachment -## self.charWidth=charWidth -## self.charHeight=charHeight -## self.direction=direction -## self.height=height -## self.rotation=rotation -## self.spacingStyle=spacingStyle -## self.spacingFactor=spacingFactor -## self.style=style -## self.width=width -## self.xdirection=xdirection -## def __str__(self): -## input=self.text -## text='' -## while len(input)>250: -## text+='\n3\n%s'%input[:250] -## input=input[250:] -## text+='\n1\n%s'%input -## result= '0\nMTEXT\n%s\n%s\n40\n%s\n41\n%s\n71\n%s\n72\n%s%s\n43\n%s\n50\n%s'%\ -## (self._common(),_point(self.point),self.charHeight,self.width, -## self.attachment,self.direction,text, -## self.height, -## self.rotation) -## if self.style:result+='\n7\n%s'%self.style -## if self.xdirection:result+='\n%s'%_point(self.xdirection,1) -## if self.charWidth:result+='\n42\n%s'%self.charWidth -## if self.spacingStyle:result+='\n73\n%s'%self.spacingStyle -## if self.spacingFactor:result+='\n44\n%s'%self.spacingFactor -## return result - -#---tables --------------------------------------------------- -#----------------------------------------------- -class Block(_Collection): - """Use list methods to add entities, eg append.""" - def __init__(self,name,layer='0',flag=0,base=(0,0,0),entities=[]): - self.entities=copy.copy(entities) - _Collection.__init__(self,entities) - self.layer=layer - self.name=name - self.flag=0 - self.base=base - def __str__(self): - e='\n'.join([str(x)for x in self.entities]) - return '0\nBLOCK\n8\n%s\n2\n%s\n70\n%s\n%s\n3\n%s\n%s\n0\nENDBLK'%\ - (self.layer,self.name.upper(),self.flag,_point(self.base),self.name.upper(),e) - -#----------------------------------------------- -class Layer(_Call): - """Layer""" - def __init__(self,name='pydxf',color=7,lineType='continuous',flag=64): - self.name=name - self.color=color - self.lineType=lineType - self.flag=flag - def __str__(self): - return '0\nLAYER\n2\n%s\n70\n%s\n62\n%s\n6\n%s'%\ - (self.name.upper(),self.flag,self.color,self.lineType) - -#----------------------------------------------- -class LineType(_Call): - """Custom linetype""" - def __init__(self,name='continuous',description='Solid line',elements=[],flag=64): - # TODO: Implement lineType elements - self.name=name - self.description=description - self.elements=copy.copy(elements) - self.flag=flag - def __str__(self): - return '0\nLTYPE\n2\n%s\n70\n%s\n3\n%s\n72\n65\n73\n%s\n40\n0.0'%\ - (self.name.upper(),self.flag,self.description,len(self.elements)) - -#----------------------------------------------- -class Style(_Call): - """Text style""" - def __init__(self,name='standard',flag=0,height=0,widthFactor=40,obliqueAngle=50, - mirror=0,lastHeight=1,font='arial.ttf',bigFont=''): - self.name=name - self.flag=flag - self.height=height - self.widthFactor=widthFactor - self.obliqueAngle=obliqueAngle - self.mirror=mirror - self.lastHeight=lastHeight - self.font=font - self.bigFont=bigFont - def __str__(self): - return '0\nSTYLE\n2\n%s\n70\n%s\n40\n%s\n41\n%s\n50\n%s\n71\n%s\n42\n%s\n3\n%s\n4\n%s'%\ - (self.name.upper(),self.flag,self.flag,self.widthFactor, - self.obliqueAngle,self.mirror,self.lastHeight, - self.font.upper(),self.bigFont.upper()) - -#----------------------------------------------- -class View(_Call): - def __init__(self,name,flag=0,width=1,height=1,center=(0.5,0.5), - direction=(0,0,1),target=(0,0,0),lens=50, - frontClipping=0,backClipping=0,twist=0,mode=0): - self.name=name - self.flag=flag - self.width=width - self.height=height - self.center=center - self.direction=direction - self.target=target - self.lens=lens - self.frontClipping=frontClipping - self.backClipping=backClipping - self.twist=twist - self.mode=mode - def __str__(self): - return '0\nVIEW\n2\n%s\n70\n%s\n40\n%s\n%s\n41\n%s\n%s\n%s\n42\n%s\n43\n%s\n44\n%s\n50\n%s\n71\n%s'%\ - (self.name,self.flag,self.height,_point(self.center),self.width, - _point(self.direction,1),_point(self.target,2),self.lens, - self.frontClipping,self.backClipping,self.twist,self.mode) - -#----------------------------------------------- -def ViewByWindow(name,leftBottom=(0,0),rightTop=(1,1),**options): - width=abs(rightTop[0]-leftBottom[0]) - height=abs(rightTop[1]-leftBottom[1]) - center=((rightTop[0]+leftBottom[0])*0.5,(rightTop[1]+leftBottom[1])*0.5) - return View(name=name,width=width,height=height,center=center,**options) - -#---drawing -#----------------------------------------------- -class Drawing(_Collection): - """Dxf drawing. Use append or any other list methods to add objects.""" - def __init__(self,insbase=(0.0,0.0,0.0),extmin=(0.0,0.0),extmax=(0.0,0.0), - layers=[Layer()],linetypes=[LineType()],styles=[Style()],blocks=[], - views=[],entities=None,fileName='test.dxf'): - # TODO: replace list with None,arial - if not entities:entities=[] - _Collection.__init__(self,entities) - self.insbase=insbase - self.extmin=extmin - self.extmax=extmax - self.layers=copy.copy(layers) - self.linetypes=copy.copy(linetypes) - self.styles=copy.copy(styles) - self.views=copy.copy(views) - self.blocks=copy.copy(blocks) - self.fileName=fileName - #private - #self.acadver='9\n$ACADVER\n1\nAC1006' - self.acadver='9\n$ACADVER\n1\nAC1009' - """DXF AutoCAD-Release format code - AC1021 2008, 2007 - AC1018 2006, 2005, 2004 - AC1015 2002, 2000i, 2000 - AC1014 R14,14.01 - AC1012 R13 - AC1009 R12,11 - AC1006 R10 - AC1004 R9 - AC1002 R2.6 - AC1.50 R2.05 - """ - - def _name(self,x): - """Helper function for self._point""" - return '9\n$%s'%x.upper() - - def _point(self,name,x): - """Point setting from drawing like extmin,extmax,...""" - return '%s\n%s'%(self._name(name),_point(x)) - - def _section(self,name,x): - """Sections like tables,blocks,entities,...""" - if x:xstr='\n'+'\n'.join(x) - else:xstr='' - return '0\nSECTION\n2\n%s%s\n0\nENDSEC'%(name.upper(),xstr) - - def _table(self,name,x): - """Tables like ltype,layer,style,...""" - if x:xstr='\n'+'\n'.join(x) - else:xstr='' - return '0\nTABLE\n2\n%s\n70\n%s%s\n0\nENDTAB'%(name.upper(),len(x),xstr) - - def __str__(self): - """Returns drawing as dxf string.""" - header=[self.acadver]+[self._point(attr,getattr(self,attr)) for attr in _HEADER_POINTS] - header=self._section('header',header) - - tables=[self._table('ltype',[str(x) for x in self.linetypes]), - self._table('layer',[str(x) for x in self.layers]), - self._table('style',[str(x) for x in self.styles]), - self._table('view',[str(x) for x in self.views]), - ] - tables=self._section('tables',tables) - - blocks=self._section('blocks',[str(x) for x in self.blocks]) - - entities=self._section('entities',[str(x) for x in self.entities]) - - all='\n'.join([header,tables,blocks,entities,'0\nEOF\n']) - return all - - def saveas(self,fileName): - self.fileName=fileName - self.save() - - def save(self): - test=open(self.fileName,'w') - test.write(str(self)) - test.close() - - -#---extras -#----------------------------------------------- -class Rectangle(_Entity): - """Rectangle, creates lines.""" - def __init__(self,point=(0,0,0),width=1,height=1,solid=None,line=1,**common): - _Entity.__init__(self,**common) - self.point=point - self.width=width - self.height=height - self.solid=solid - self.line=line - def __str__(self): - result='' - points=[self.point,(self.point[0]+self.width,self.point[1],self.point[2]), - (self.point[0]+self.width,self.point[1]+self.height,self.point[2]), - (self.point[0],self.point[1]+self.height,self.point[2]),self.point] - if self.solid: - result+='\n%s'%Solid(points=points[:-1],parent=self.solid) - if self.line: - for i in range(4):result+='\n%s'%\ - Line(points=[points[i],points[i+1]],parent=self) - return result[1:] - -#----------------------------------------------- -class LineList(_Entity): - """Like polyline, but built of individual lines.""" - def __init__(self,points=[],org_point=[0,0,0],closed=0,**common): - _Entity.__init__(self,**common) - self.closed=closed - self.points=copy.copy(points) - def __str__(self): - if self.closed:points=self.points+[self.points[0]] - else: points=self.points - result='' - for i in range(len(points)-1): - result+='\n%s' %Line(points=[points[i],points[i+1]],parent=self) - return result[1:] +#import dxfLibrary +#reload(dxfLibrary) +from dxfLibrary import * + + +#----------------------------------------------------- +def hidden_status(faces, mx_n): + #print 'HIDDEN_MODE: caution! not full implemented yet' + ok_faces = [] + ok_edges = [] + #sort out back-faces = with normals pointed away from camera + for f in faces: + #print 'deb: face=', f #--------- + # get its normal-vector in localCS + vec_normal = f.no.copy() + #print 'deb: vec_normal=', vec_normal #------------------ + #must be transfered to camera/view-CS + vec_normal *= mx_n + #vec_normal *= mb.rotationPart() + #print 'deb:2vec_normal=', vec_normal #------------------ + #vec_normal *= mw0.rotationPart() + #print 'deb:3vec_normal=', vec_normal, '\n' #------------------ + + # normal must point the Z direction-hemisphere + if vec_normal[2] > 0.0 : + ok_faces.append(f.index) + for key in f.edge_keys: + #this test can be done faster with set() + if key not in ok_edges: + ok_edges.append(key) + #print 'deb: amount of visible faces=', len(ok_faces) #--------- + #print 'deb: visible faces=', ok_faces #--------- + #print 'deb: amount of visible edges=', len(ok_edges) #--------- + #print 'deb: visible edges=', ok_edges #--------- + return ok_faces, ok_edges + #----------------------------------------------------- def projected_co(vec, mw): - # convert the world coordinates of v to screen coordinates + # convert the world coordinates of vector to screen coordinates #co = vec.co.copy().resize4D() co = vec.copy().resize4D() co[3] = 1.0 sc = co * mw - #print 'viewprojection=', sc #--------- + #print 'deb: viewprojection=', sc #--------- return [sc[0],sc[1],0.0] +#--------not used--------------------------------------------- +def flatten(points, mw): + for i,v in enumerate(points): + v = projected_co(v, mw) + points[i]=v + #print 'deb: flatten points=', points #--------- + return points + +#----------------------------------------------------- +def exportMesh(ob, mx, mx_n): + entities = [] + me = ob.getData(mesh=1) + #me.transform(mx) + # above is eventualy faster, but bad, cause + # directly transforms origin geometry and write back rounding errors + me_verts = me.verts[:] #we dont want manipulate origin data + #print 'deb: me_verts=', me_verts #--------- + #me.transform(mx_inv) #counterpart to - back to the origin state + for v in me_verts: + v.co *= mx + faces=[] + edges=[] + if HIDDEN_MODE: + ok_faces, ok_edges = hidden_status(me.faces, mx_n) + + #if (not FLATTEN) and len(me.faces)>0 and ONLYFACES: + if ONLYFACES: + if POLYFACES: #export 3D as POLYFACEs + allpoints = [] + allfaces = [] + allpoints = [v.co[:3] for v in me_verts] + for f in me.faces: + #print 'deb: face=', f #--------- + if not HIDDEN_MODE or \ + (HIDDEN_MODE and f.index in ok_faces): + if 1: + points = f.verts + face = [p.index+1 for p in points] + #print 'deb: face=', face #--------- + allfaces.append(face) + else: #bad, cause create multiple vertex instances + points = f.verts + points = [ me_verts[p.index].co[:3] for p in points] + #points = [p.co[:3] for p in points] + #print 'deb: points=', points #--------- + index = len(allpoints)+1 + face = [index+i for i in range(len(points))] + allpoints.extend(points) + allfaces.append(face) + if allpoints and allfaces: + #print 'deb: allpoints=', allpoints #--------- + #print 'deb: allfaces=', allfaces #--------- + dxfPOLYFACE = PolyLine([allpoints, allfaces], flag=64) + entities.append(dxfPOLYFACE) + else: #export 3D as 3DFACEs + for f in me.faces: + #print 'deb: face=', f #--------- + if not HIDDEN_MODE or \ + (HIDDEN_MODE and f.index in ok_faces): + points = f.verts + points = [ me_verts[p.index].co[:3] for p in points] + #points = [p.co[:3] for p in points] + #print 'deb: points=', points #--------- + dxfFACE = Face(points) + entities.append(dxfFACE) + + else: #export 3D as LINEs + if HIDDEN_MODE and len(me.faces)!=0: + for e in ok_edges: + points = [ me_verts[key].co[:3] for key in e] + dxfLINE = Line(points) + entities.append(dxfLINE) + + else: + for e in me.edges: + #print 'deb: edge=', e #--------- + points=[] + #points = [e.v1.co*mx, e.v2.co*mx] + points = [ me_verts[key].co[:3] for key in e.key] + #print 'deb: points=', points #--------- + dxfLINE = Line(points) + entities.append(dxfLINE) + return entities + + +#----------------------------------------------------- +def exportCurve(ob, mx): + entities = [] + curve = ob.getData() + for cur in curve: + #print 'deb: START cur=', cur #-------------- + if 1: #not cur.isNurb(): + #print 'deb: START points' #-------------- + points = [] + org_point = [0.0,0.0,0.0] + for point in cur: + #print 'deb: point=', point #--------- + if cur.isNurb(): + vec = point[0:3] + else: + point = point.getTriple() + #print 'deb: point=', point #--------- + vec = point[1] + #print 'deb: vec=', vec #--------- + pkt = Mathutils.Vector(vec) * mx + #print 'deb: pkt=', pkt #--------- + #pkt *= SCALE_FACTOR + if 0: #FLATTEN: + pkt = projected_co(pkt, mw) + points.append(pkt) + if cur.isCyclic(): closed = 1 + else: closed = 0 + if len(points)>1: + #print 'deb: points', points #-------------- + if POLYLINES: dxfPLINE = PolyLine(points,org_point,closed) + else: dxfPLINE = LineList(points,org_point,closed) + entities.append(dxfPLINE) + return entities + +#----------------------------------------------------- +def do_export(sel_group, filepath): + Window.WaitCursor(1) + t = sys.time() + + #init Drawing --------------------- + d=Drawing() + #add Tables ----------------- + #d.blocks.append(b) #table blocks + d.styles.append(Style()) #table styles + d.views.append(View('Normal')) #table view + d.views.append(ViewByWindow('Window',leftBottom=(1,0),rightTop=(2,1))) #idem + + #add Entities -------------------- + something_ready = False + #ViewVector = Mathutils.Vector(Window.GetViewVector()) + #print 'deb: ViewVector=', ViewVector #------------------ + mw0 = Window.GetViewMatrix() + #mw0 = Window.GetPerspMatrix() #TODO: how get it working? + mw = mw0.copy() + if FLATTEN: + m0 = Mathutils.Matrix() + m0[2][2]=0.0 + mw *= m0 #flatten ViewMatrix + + for ob in sel_group: + entities = [] + mx = ob.matrix.copy() + mb = mx.copy() + #print 'deb: mb =\n', mb #--------- + #print 'deb: mw0 =\n', mw0 #--------- + mx_n = mx.rotationPart() * mw0.rotationPart() #trans-matrix for normal_vectors + if SCALE_FACTOR!=1.0: mx *= SCALE_FACTOR + if FLATTEN: mx *= mw + + #mx_inv = mx.copy().invert() + #print 'deb: mx =\n', mx #--------- + #print 'deb: mx_inv=\n', mx_inv #--------- + + if (ob.type == 'Mesh'): + entities = exportMesh(ob, mx, mx_n) + elif (ob.type == 'Curve'): + entities = exportCurve(ob, mx) + + for e in entities: + d.append(e) + something_ready = True + + if something_ready: + d.saveas(filepath) + Window.WaitCursor(0) + #Draw.PupMenu('DXF Exporter: job finished') + print 'exported to %s' % filepath + print 'finished in %.2f seconds' % (sys.time()-t) + else: + Window.WaitCursor(0) + print "Abort: selected objects dont mach choosen export option, nothing exported!" + Draw.PupMenu('DXF Exporter: nothing exported!|selected objects dont mach choosen export option!') + +#----globals------------------------------------------ +ONLYSELECTED = True +POLYLINES = True +ONLYFACES = False +POLYFACES = 1 +FLATTEN = 0 +HIDDEN_MODE = False #filter out hidden lines +SCALE_FACTOR = 1.0 #optional, can be done later in CAD too + + + #----------------------------------------------------- def dxf_export_ui(filepath): - print '\n\nDXF-Export %s' %__version__ + global ONLYSELECTED,\ + POLYLINES,\ + ONLYFACES,\ + POLYFACES,\ + FLATTEN,\ + HIDDEN_MODE,\ + SCALE_FACTOR + + print '\n\nDXF-Export %s -----------------------' %__version__ #filepath = 'blend_test.dxf' # Dont overwrite if not BPyMessages.Warning_SaveOver(filepath): @@ -679,29 +344,28 @@ def dxf_export_ui(filepath): return #test():return - ONLYSELECTED = True - POLYLINES = True - ONLYFACES = False - FLATTEN = 0 #dimmensions:1,2,3. Force Z dimmension value to 0.0, equal ground projection - SCALE_FACTOR = 1.0 #optional, can be done in CAD too + PREF_ONLYSELECTED= Draw.Create(ONLYSELECTED) PREF_POLYLINES= Draw.Create(POLYLINES) PREF_ONLYFACES= Draw.Create(ONLYFACES) + PREF_POLYFACES= Draw.Create(POLYFACES) PREF_FLATTEN= Draw.Create(FLATTEN) + PREF_HIDDEN_MODE= Draw.Create(HIDDEN_MODE) PREF_SCALE_FACTOR= Draw.Create(SCALE_FACTOR) PREF_HELP= Draw.Create(0) block = [\ ("only selected", PREF_ONLYSELECTED, "export only selected geometry"),\ ("global Scale:", PREF_SCALE_FACTOR, 0.001, 1000, "set global Scale factor for exporting geometry"),\ - ("only faces", PREF_ONLYFACES, "from mesh-objects export only faces, not edges"),\ - ("write POLYLINEs", PREF_POLYLINES, "export curves to POLYLINEs, otherwise to LINEs"),\ + ("only faces", PREF_ONLYFACES, "from mesh-objects export only faces, otherwise only edges"),\ + ("write POLYFACE", PREF_POLYFACES, "export mesh to POLYFACE, otherwise to 3DFACEs"),\ + ("write POLYLINEs", PREF_POLYLINES, "export curve to POLYLINE, otherwise to LINEs"),\ ("3D-View to Flat", PREF_FLATTEN, "flatten geometry according current 3d-View"),\ - (''),\ + ("Hidden Mode", PREF_HIDDEN_MODE, "filter out hidden lines"),\ + #(''),\ ("online Help", PREF_HELP, "calls DXF-Exporter Manual Page on Wiki.Blender.org"),\ ] - if not Draw.PupBlock("DXF-Exporter %s" %__version__[:10], block): - return + if not Draw.PupBlock("DXF-Exporter %s" %__version__[:10], block): return if PREF_HELP.val!=0: try: @@ -715,139 +379,22 @@ http://wiki.blender.org/index.php?title=Scripts/Manual/Export/autodesk_dxf') ONLYSELECTED = PREF_ONLYSELECTED.val POLYLINES = PREF_POLYLINES.val ONLYFACES = PREF_ONLYFACES.val + POLYFACES = PREF_POLYFACES.val FLATTEN = PREF_FLATTEN.val + HIDDEN_MODE = PREF_HIDDEN_MODE.val SCALE_FACTOR = PREF_SCALE_FACTOR.val sce = Scene.GetCurrent() if ONLYSELECTED: sel_group = sce.objects.selected else: sel_group = sce.objects - if sel_group: - Window.WaitCursor(1) - t = sys.time() - - #init Drawing --------------------- - d=Drawing() - #add Tables ----------------- - #d.blocks.append(b) #table blocks - d.styles.append(Style()) #table styles - d.views.append(View('Normal')) #table view - d.views.append(ViewByWindow('Window',leftBottom=(1,0),rightTop=(2,1))) #idem - - #add Entities -------------------- - something_ready = False - mw = Window.GetViewMatrix() - #mw = Window.GetPerspMatrix() #TODO: how get it working? - for ob in sel_group: - entities = [] - mx = ob.matrix - if (ob.type == 'Mesh'): - me = ob.getData(mesh=1) - faces=[] - edges=[] - if (not FLATTEN) and len(me.faces)>0 and ONLYFACES: - #export 3D as 3DFACEs - for f in me.faces: - #print 'face=', f #--------- - verts = f.verts - points = [verts[i].co*mx for i in range(len(verts))] - if SCALE_FACTOR!=1.0: - points = [p*SCALE_FACTOR for p in points] - #print 'points=', points #--------- - dxfFACE = Face(points) - entities.append(dxfFACE) - else: #export 3D as LINEs - for e in me.edges: - #print 'edge=', e #--------- - points=[] - points = [e.v1.co*mx, e.v2.co*mx] - if SCALE_FACTOR!=1.0: - points = [p*SCALE_FACTOR for p in points] - if FLATTEN: -# for p in points: p[FLATTEN-1]=0.0 - for i,v in enumerate(points): - v = projected_co(v, mw) - points[i]=v - #print 'flatten points=', points #--------- - dxfLINE = Line(points) - entities.append(dxfLINE) - elif (ob.type == 'Curve'): - curve = ob.getData() - for cur in curve: - #print 'deb: START cur=', cur #-------------- - if 1: #not cur.isNurb(): - #print 'deb: START points' #-------------- - points = [] - org_point = [0.0,0.0,0.0] - for point in cur: - #print 'point=', point #--------- - if cur.isNurb(): - vec = point[0:3] - else: - point = point.getTriple() - #print 'point=', point #--------- - vec = point[1] - #print 'vec=', vec #--------- - pkt = Mathutils.Vector(vec) * mx - #print 'pkt=', pkt #--------- - pkt *= SCALE_FACTOR - if FLATTEN: - pkt = projected_co(pkt, mw) - points.append(pkt) - if cur.isCyclic(): closed = 1 - else: closed = 0 - if len(points)>1: - #print 'deb: points', points #-------------- - if POLYLINES: dxfPLINE = PolyLine(points,org_point,closed) - else: dxfPLINE = LineList(points,org_point,closed) - entities.append(dxfPLINE) - for e in entities: - d.append(e) - something_ready = True - if something_ready: - d.saveas(filepath) - Window.WaitCursor(0) - #Draw.PupMenu('DXF Exporter: job finished|search for blend_test.dxf in current project directory') - print 'exported to %s' % filepath - print 'finished in %.2f seconds' % (sys.time()-t) - else: - print "Abort: no supported object types selected, nothing exported!" - Draw.PupMenu('DXF Exporter: Abort!|Not-supported object types selected.') + if sel_group: do_export(sel_group, filepath) else: print "Abort: selection was empty, no object to export!" - Draw.PupMenu('DXF Exporter: Abort!|empty selection, no object to export!') + Draw.PupMenu('DXF Exporter: nothing exported!|empty selection!') # Timing the script is a good way to be aware on any speed hits when scripting -#----------------------------------------------------- -def test(): - #Blocks - b=Block('test') - b.append(Solid(points=[(0,0,0),(1,0,0),(1,1,0),(0,1,0)],color=1)) - b.append(Arc(center=(1,0,0),color=2)) - - #Drawing - d=Drawing() - #tables - d.blocks.append(b) #table blocks - d.styles.append(Style()) #table styles - d.views.append(View('Normal')) #table view - d.views.append(ViewByWindow('Window',leftBottom=(1,0),rightTop=(2,1))) #idem - - #entities - d.append(Circle(center=(1,1,0),color=3)) - d.append(Face(points=[(0,0,0),(1,0,0),(1,1,0),(0,1,0)],color=4)) - d.append(Insert('test',point=(3,3,3),cols=5,colspacing=2)) - d.append(Line(points=[(0,0,0),(1,1,1)])) - d.append(Mtext('Click on Ads\nmultiple lines with mtext',point=(1,1,1),color=5,rotation=90)) - d.append(Text('Please donate!',point=(3,0,1))) - d.append(Rectangle(point=(2,2,2),width=4,height=3,color=6,solid=Solid(color=2))) - d.append(Solid(points=[(4,4,0),(5,4,0),(7,8,0),(9,9,0)],color=3)) - d.append(PolyLine(points=[(1,1,1),(2,1,1),(2,2,1),(1,2,1)],closed=1,color=1)) - - #d.saveas('c:\\test.dxf') - d.saveas('test.dxf') - #----------------------------------------------------- if __name__=='__main__': |