#dxfLibrary.py : provides functions for generating DXF files
# --------------------------------------------------------------------------
__version__ = "v1.35 - 2010.06.23"
__author__ = "Stani Michiels(Stani), Remigiusz Fiedler(migius)"
__license__ = "GPL"
__url__ = "http://wiki.blender.org/index.php/Extensions:2.4/Py/Scripts/Export/DXF"
__bpydoc__ ="""The library to export geometry data to DXF format r12 version.

Copyright %s
Version %s
License %s
Homepage %s

See the homepage for documentation.
Dedicated thread on BlenderArtists: http://blenderartists.org/forum/showthread.php?t=136439

IDEAs:
-

TODO:
- add support for DXFr14 version (needs extended file header)
- add support for DXF-SPLINEs (possible first in DXFr14 version)
- add support for DXF-MTEXT
- add user preset for floating point precision (3-16?)

History
v1.35 - 2010.06.23 by migius
 - added (as default) writing to DXF file without RAM-buffering: faster and low-RAM-machines friendly
v1.34 - 2010.06.20 by migius
 - bugfix POLYFACE
 - added DXF-flags for POLYLINE and VERTEX class (NURBS-export)
v1.33 - 2009.07.03 by migius
 - fix MTEXT newline bug (not supported by DXF-Exporter yet)
 - modif _point(): converts all coords to floats
 - modif LineType class: implement elements
 - added VPORT class, incl. defaults
 - fix Insert class
v1.32 - 2009.06.06 by migius
 - modif Style class: changed defaults to widthFactor=1.0, obliqueAngle=0.0
 - modif Text class: alignment parameter reactivated
v1.31 - 2009.06.02 by migius
 - modif _Entity class: added paperspace,elevation
v1.30 - 2009.05.28 by migius
 - bugfix 3dPOLYLINE/POLYFACE: VERTEX needs x,y,z coordinates, index starts with 1 not 0
v1.29 - 2008.12.28 by Yorik
 - modif POLYLINE to support bulge segments
v1.28 - 2008.12.13 by Steeve/BlenderArtists
 - bugfix for EXTMIN/EXTMAX to suit Cycas-CAD
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/2009 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"""
	return '\n'.join([' %s\n%s'%((i+1)*10+index,float(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))])
	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,paperspace=None,color=None,layer='0',
				 lineType=None,lineTypeScale=None,lineWeight=None,
				 extrusion=None,elevation=None,thickness=None,
				 parent=None):
		"""None values will be omitted."""
		self.paperspace	  = paperspace
		self.color		  = color
		self.layer		  = layer
		self.lineType	   = lineType
		self.lineTypeScale  = lineTypeScale
		self.lineWeight	 = lineWeight
		self.extrusion	  = extrusion
		self.elevation	  = elevation
		self.thickness	  = thickness
		#self.visible	  = visible
		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.paperspace==1: result+='  67\n1\n'
		if parent.layer!=None: result+='  8\n%s\n'%parent.layer
		if parent.color!=None: result+=' 62\n%s\n'%parent.color
		if parent.lineType!=None: result+='  6\n%s\n'%parent.lineType
		# TODO: if parent.lineWeight!=None: result+='370\n%s\n'%parent.lineWeight
		# TODO: if parent.visible!=None: result+='60\n%s\n'%parent.visible
		if parent.lineTypeScale!=None: result+=' 48\n%s\n'%parent.lineTypeScale
		if parent.elevation!=None: result+=' 38\n%s\n'%parent.elevation
		if parent.thickness!=None: result+=' 39\n%s\n'%parent.thickness
		if parent.extrusion!=None: result+='%s\n'%_point(parent.extrusion,200)
		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 flag 70
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

#---polyline flag 75, = curve type
QUADRIC_NURBS = 5
CUBIC_NURBS = 6
BEZIER_CURVE = 8


#---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)
		while 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))
		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%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],flag70=0,flag75=0,width=None,**common):
		#width = number, or width = list [width_start=None, width_end=None]
		#for 2d-polyline: points = [ [[x, y, z], vflag=None, [width_start=None, width_end=None], bulge=0 or None] ...]
		#for 3d-polyline: points = [ [[x, y, z], vflag=None], ...]
		#for polyface: points = [points_list, faces_list]
		_Entity.__init__(self,**common)
		self.points=points
		self.org_point=org_point
		self.pflag70 = flag70
		self.pflag75 = flag75
		self.polyface = False
		self.polyline2d = False
		self.faces = [] # dummy value
		self.width= None # dummy value
		if self.pflag70 & POLYFACE_MESH:
			self.polyface=True
			self.points=points[0]
			self.faces=points[1]
			self.p_count=len(self.points)
			self.f_count=len(self.faces)
		elif not (self.pflag70 & POLYLINE_3D):
			self.polyline2d = True
			if width:
				if type(width)!='list':  width=[width,width]
				self.width=width

	def __str__(self):
		result= '  0\nPOLYLINE\n%s 70\n%s\n' %(self._common(),self.pflag70)
		result+=' 66\n1\n'
		result+='%s\n' %_point(self.org_point)
		if self.polyface:
			result+=' 71\n%s\n' %self.p_count
			result+=' 72\n%s\n' %self.f_count
		elif self.polyline2d:
			if self.width!=None: result+=' 40\n%s\n 41\n%s\n' %(self.width[0],self.width[1])
		if self.pflag75:
			result+=' 75\n%s\n' %self.pflag75
		for point in self.points:
			result+='  0\nVERTEX\n'
			result+='  8\n%s\n' %self.layer
			if self.polyface:
				result+='%s\n' %_point(point)
				result+=' 70\n192\n'
			elif self.polyline2d:
				result+='%s\n' %_point(point[0])
				flag = point[1]
				if len(point)>2:
					[width1, width2] = point[2]
					if width1!=None: result+=' 40\n%s\n' %width1
					if width2!=None: result+=' 41\n%s\n' %width2
				if len(point)==4:
					bulge = point[3]
					if bulge: result+=' 42\n%s\n' %bulge
				if flag:
						result+=' 70\n%s\n' %flag
			else:
				result+='%s\n' %_point(point[0])
				flag = point[1]
				if flag:
						result+=' 70\n%s\n' %flag

		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):
	"""Point."""
	def __init__(self,points=None,**common):
		_Entity.__init__(self,**common)
		self.points=points
	def __str__(self): # TODO:
		return '  0\nPOINT\n%s%s\n' %(self._common(),
			 _points(self.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
		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' %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): # TODO:
		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=[0.0],flag=0):
		self.name=name
		self.description=description
		self.elements=copy.copy(elements)
		self.flag=flag
	def __str__(self):
		result = '  0\nLTYPE\n  2\n%s\n 70\n%s\n  3\n%s\n 72\n65\n'%\
			(self.name.upper(),self.flag,self.description)
		if self.elements:
			elements = ' 73\n%s\n' %(len(self.elements)-1)
			elements += ' 40\n%s\n' %(self.elements[0])
			for e in self.elements[1:]:
				elements += ' 49\n%s\n' %e
			result += elements
		return result
		 

#-----------------------------------------------
class Style(_Call):
	"""Text style"""
	def __init__(self,name='standard',flag=0,height=0,widthFactor=1.0,obliqueAngle=0.0,
				 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 VPort(_Call):
	def __init__(self,name,flag=0,
				leftBottom=(0.0,0.0),
				rightTop=(1.0,1.0),
				center=(0.5,0.5),
				snap_base=(0.0,0.0),
				snap_spacing=(0.1,0.1),
				grid_spacing=(0.1,0.1),
				direction=(0.0,0.0,1.0),
				target=(0.0,0.0,0.0),
				height=1.0,
				ratio=1.0,
				lens=50.0,
				frontClipping=0.0,
				backClipping=0.0,
				snap_rotation=0.0,
				twist=0.0,
				mode=0,
				circle_zoom=100,
				fast_zoom=1,
				ucsicon=1,
				snap_on=0,
				grid_on=0,
				snap_style=0,
				snap_isopair=0
				):
		self.name=name
		self.flag=flag
		self.leftBottom=leftBottom
		self.rightTop=rightTop
		self.center=center
		self.snap_base=snap_base
		self.snap_spacing=snap_spacing
		self.grid_spacing=grid_spacing
		self.direction=direction
		self.target=target
		self.height=float(height)
		self.ratio=float(ratio)
		self.lens=float(lens)
		self.frontClipping=float(frontClipping)
		self.backClipping=float(backClipping)
		self.snap_rotation=float(snap_rotation)
		self.twist=float(twist)
		self.mode=mode
		self.circle_zoom=circle_zoom
		self.fast_zoom=fast_zoom
		self.ucsicon=ucsicon
		self.snap_on=snap_on
		self.grid_on=grid_on
		self.snap_style=snap_style
		self.snap_isopair=snap_isopair
	def __str__(self):
		output = ['  0', 'VPORT',
			'  2', self.name,
			' 70', self.flag,
			_point(self.leftBottom),
			_point(self.rightTop,1),
			_point(self.center,2), # View center point (in DCS)
			_point(self.snap_base,3),
			_point(self.snap_spacing,4),
			_point(self.grid_spacing,5),
			_point(self.direction,6), #view direction from target (in WCS)
			_point(self.target,7),
			' 40', self.height,
			' 41', self.ratio,
			' 42', self.lens,
			' 43', self.frontClipping,
			' 44', self.backClipping,
			' 50', self.snap_rotation,
			' 51', self.twist,
			' 71', self.mode,
			' 72', self.circle_zoom,
			' 73', self.fast_zoom,
			' 74', self.ucsicon,
			' 75', self.snap_on,
			' 76', self.grid_on,
			' 77', self.snap_style,
			' 78', self.snap_isopair
			]

		output_str = ''
		for s in output:
			output_str += '%s\n' %s
		return output_str



#-----------------------------------------------
class View(_Call):
	def __init__(self,name,flag=0,
			width=1.0,
			height=1.0,
			center=(0.5,0.5),
			direction=(0.0,0.0,1.0),
			target=(0.0,0.0,0.0),
			lens=50.0,
			frontClipping=0.0,
			backClipping=0.0,
			twist=0.0,mode=0
			):
		self.name=name
		self.flag=flag
		self.width=float(width)
		self.height=float(height)
		self.center=center
		self.direction=direction
		self.target=target
		self.lens=float(lens)
		self.frontClipping=float(frontClipping)
		self.backClipping=float(backClipping)
		self.twist=float(twist)
		self.mode=mode
	def __str__(self):
		output = ['  0', 'VIEW',
			'  2', self.name,
			' 70', self.flag,
			' 40', self.height,
			_point(self.center),
			' 41', self.width,
			_point(self.direction,1),
			_point(self.target,2),
			' 42', self.lens,
			' 43', self.frontClipping,
			' 44', self.backClipping,
			' 50', self.twist,
			' 71', self.mode
			]
		output_str = ''
		for s in output:
			output_str += '%s\n' %s
		return output_str

#-----------------------------------------------
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,0.0),extmax=(0.0,0.0,0.0),
				 layers=[Layer()],linetypes=[LineType()],styles=[Style()],blocks=[],
				 views=[],vports=[],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.vports=copy.copy(vports)
		self.blocks=copy.copy(blocks)
		self.fileName=fileName
		#print 'deb: blocks=',blocks #----------
		#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('vport',[str(x) for x in self.vports]),
				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 _write_section(self,file,name,data):
		file.write('  0\nSECTION\n  2\n%s\n'%name.upper())
		for x in data:
			file.write(str(x))
		file.write('  0\nENDSEC\n')

	def saveas(self,fileName,buffer=0):
		"""Writes DXF file. Needs target file name. If optional parameter buffer>0, then switch to old behavior: store entire output string in RAM.
		"""
		self.fileName=fileName
		if buffer: self.save()
		else: self.export()

	def save(self):
		outfile=open(self.fileName,'w')
		outfile.write(str(self))
		outfile.close()

	def export(self):
		outfile=open(self.fileName,'w')
		header=[self.acadver]+[self._point(attr,getattr(self,attr))+'\n' for attr in _HEADER_POINTS]
		self._write_section(outfile,'header',header)
		tables=[self._table('vport',[str(x) for x in self.vports]),
			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]),
			]
		self._write_section(outfile,'tables',tables)
		self._write_section(outfile,'blocks',self.blocks)
		self._write_section(outfile,'entities',self.entities)
		outfile.write('  0\nEOF\n')
		outfile.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][0],points[i+1][0]],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)],flag=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')
	else: test()
	
