"""
Fillet is a script to fillet or bevel the corners on a gcode file.

The default 'Activate Fillet' checkbox is on.  When it is on, the functions described below will work, when it is off, the functions
will not be called.

Fillet rounds the corners slightly in a variety of ways.  This is to reduce corner blobbing and sudden extruder acceleration.
The 'Arc Point' method fillets the corners with an arc using the gcode point form.  The 'Arc Radius' method fillets with an arc
using the gcode radius form.  The 'Arc Segment' method fillets corners with an arc composed of several segments.  The
'Bevel' method bevels each corner.  The default radio button choice is 'Bevel'.

The 'Corner Feedrate over Operating Feedrate' is the ratio of the feedrate in corners over the operating feedrate.  With a high
value the extruder will move quickly in corners, accelerating quickly and leaving a thin extrusion.  With a low value, the
extruder will move slowly in corners, accelerating gently and leaving a thick extrusion.  The default value is 1.0.  The 'Fillet
Radius over Extrusion Width' ratio determines how much wide the fillet will be, the default is 0.35.  The 'Reversal Slowdown
over Extrusion Width' ratio determines how far before a path reversal the extruder will slow down.  Some tools, like nozzle
wipe, double back the path of the extruder and this option will add a slowdown point in that path so there won't be a sudden
jerk at the end of the path.  The default value is 0.5 and if the value is less than 0.1 a slowdown will not be added.  If
'Use Intermediate Feedrate in Corners' is chosen, the feedrate entering the corner will be the average of the old feedrate and
the new feedrate, the default is true.  To run fillet, in a shell in the folder which fillet is in type:
> python fillet.py

The following examples fillet the files Screw Holder Bottom.gcode & Screw Holder Bottom.stl.  The examples are run in a terminal in the folder
which contains Screw Holder Bottom.gcode, Screw Holder Bottom.stl and fillet.py.  The fillet function executes the preferred fillet type, which
can be set in the dialog or by changing the preferences file 'fillet.csv' with a text editor or a spreadsheet program set to separate
tabs.  The functions writeOutput and getFilletChainGcode check to see if the text has been combed, if not they call the
getOozebaneChainGcode in oozebane.py to fill the text; once they have the oozebaned text, then they fillet.


> python fillet.py
This brings up the dialog, after clicking 'Fillet', the following is printed:
File Screw Holder Bottom.stl is being chain filleted.
The filleted file is saved as Screw Holder Bottom_fillet.gcode


> python
Python 2.5.1 (r251:54863, Sep 22 2007, 01:43:31)
[GCC 4.2.1 (SUSE Linux)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import fillet
>>> fillet.main()
This brings up the fillet dialog.


>>> fillet.arcPointFile()
File Screw Holder Bottom.gcode is being filleted into arc points.
The arc point file is saved as Screw Holder Bottom_fillet.gcode


>>> fillet.arcRadiusFile()
File Screw Holder Bottom.gcode is being filleted into arc radiuses.
The arc radius file is saved as Screw Holder Bottom_fillet.gcode


>>> fillet.arcSegmentFile()
File Screw Holder Bottom.gcode is being arc segmented.
The arc segment file is saved as Screw Holder Bottom_fillet.gcode


>>> fillet.bevelFile()
File Screw Holder Bottom.gcode is being beveled.
The beveled file is saved as Screw Holder Bottom_fillet.gcode


>>> fillet.getArcPointGcode("
( GCode generated by May 8, 2008 carve.py )
( Extruder Initialization )
..
many lines of gcode
..
")


>>> fillet.getArcRadiusGcode("
( GCode generated by May 8, 2008 carve.py )
( Extruder Initialization )
..
many lines of gcode
..
")


>>> fillet.getArcSegmentGcode("
( GCode generated by May 8, 2008 carve.py )
( Extruder Initialization )
..
many lines of gcode
..
")


>>> fillet.getBevelGcode("
( GCode generated by May 8, 2008 carve.py )
( Extruder Initialization )
..
many lines of gcode
..
")


>>> fillet.getFilletGcode("
( GCode generated by May 8, 2008 carve.py )
( Extruder Initialization )
..
many lines of gcode
..
")


>>> fillet.getFilletChainGcode("
( GCode generated by May 8, 2008 carve.py )
( Extruder Initialization )
..
many lines of gcode
..
")


>>> fillet.writeOutput()
Screw Holder Bottom.stl
File Screw Holder Bottom.gcode is being chain filleted.
The filleted file is saved as Screw Holder Bottom_fillet.gcode

"""

from __future__ import absolute_import
#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module.
import __init__

from skeinforge_tools.skeinforge_utilities import euclidean
from skeinforge_tools.skeinforge_utilities import gcodec
from skeinforge_tools.skeinforge_utilities import preferences
from skeinforge_tools import analyze
from skeinforge_tools.skeinforge_utilities import interpret
from skeinforge_tools import oozebane
from skeinforge_tools import polyfile
import cStringIO
import math
import sys
import time


__author__ = "Enrique Perez (perez_enrique@yahoo.com)"
__date__ = "$Date: 2008/21/04 $"
__license__ = "GPL 3.0"


def arcPointFile( fileName = '' ):
	"Fillet a gcode linear move file into a helical point move file.  If no fileName is specified, arc point the first unmodified gcode file in this folder."
	if fileName == '':
		unmodified = gcodec.getUnmodifiedGCodeFiles()
		if len( unmodified ) == 0:
			print( "There are no unmodified gcode files in this folder." )
			return
		fileName = unmodified[ 0 ]
	filletPreferences = FilletPreferences()
	preferences.readPreferences( filletPreferences )
	print( 'File ' + gcodec.getSummarizedFilename( fileName ) + ' is being filleted into arc points.' )
	gcodeText = gcodec.getFileText( fileName )
	if gcodeText == '':
		return
	gcodec.writeFileMessageSuffix( fileName, getArcPointGcode( filletPreferences, gcodeText ), 'The arc point file is saved as ', '_fillet' )

def arcRadiusFile( fileName = '' ):
	"Fillet a gcode linear move file into a helical radius move file.  If no fileName is specified, arc radius the first unmodified gcode file in this folder."
	if fileName == '':
		unmodified = gcodec.getUnmodifiedGCodeFiles()
		if len( unmodified ) == 0:
			print( "There are no unmodified gcode files in this folder." )
			return
		fileName = unmodified[ 0 ]
	filletPreferences = FilletPreferences()
	preferences.readPreferences( filletPreferences )
	print( 'File ' + gcodec.getSummarizedFilename( fileName ) + ' is being filleted into arc radiuses.' )
	gcodeText = gcodec.getFileText( fileName )
	if gcodeText == '':
		return
	gcodec.writeFileMessageSuffix( fileName, getArcRadiusGcode( filletPreferences, gcodeText ), 'The arc radius file is saved as ', '_fillet' )

def arcSegmentFile( fileName = '' ):
	"Fillet a gcode linear move file into an arc segment linear move file.  If no fileName is specified, arc segment the first unmodified gcode file in this folder."
	if fileName == '':
		unmodified = gcodec.getUnmodifiedGCodeFiles()
		if len( unmodified ) == 0:
			print( "There are no unmodified gcode files in this folder." )
			return
		fileName = unmodified[ 0 ]
	filletPreferences = FilletPreferences()
	preferences.readPreferences( filletPreferences )
	print( 'File ' + gcodec.getSummarizedFilename( fileName ) + ' is being arc segmented.' )
	gcodeText = gcodec.getFileText( fileName )
	if gcodeText == '':
		return
	gcodec.writeFileMessageSuffix( fileName, getArcSegmentGcode( filletPreferences, gcodeText ), 'The arc segment file is saved as ', '_fillet' )

def bevelFile( fileName = '' ):
	"Bevel a gcode linear move file.  If no fileName is specified, bevel the first unmodified gcode file in this folder."
	if fileName == '':
		unmodified = gcodec.getUnmodifiedGCodeFiles()
		if len( unmodified ) == 0:
			print( "There are no unmodified gcode files in this folder." )
			return
		fileName = unmodified[ 0 ]
	filletPreferences = FilletPreferences()
	preferences.readPreferences( filletPreferences )
	print( 'File ' + gcodec.getSummarizedFilename( fileName ) + ' is being beveled.' )
	gcodeText = gcodec.getFileText( fileName )
	if gcodeText == '':
		return
	gcodec.writeFileMessageSuffix( fileName, getBevelGcode( filletPreferences, gcodeText ), 'The beveled file is saved as ', '_fillet' )

def getArcPointGcode( filletPreferences, gcodeText ):
	"Arc point a gcode linear move text into a helical point move gcode text."
	skein = ArcPointSkein()
	skein.parseGcode( filletPreferences, gcodeText )
	return skein.output.getvalue()

def getArcRadiusGcode( filletPreferences, gcodeText ):
	"Arc radius a gcode linear move text into a helical radius move gcode text."
	skein = ArcRadiusSkein()
	skein.parseGcode( filletPreferences, gcodeText )
	return skein.output.getvalue()

def getArcSegmentGcode( filletPreferences, gcodeText ):
	"Arc segment a gcode linear move text into an arc segment linear move gcode text."
	skein = ArcSegmentSkein()
	skein.parseGcode( filletPreferences, gcodeText )
	return skein.output.getvalue()

def getBevelGcode( filletPreferences, gcodeText ):
	"Bevel a gcode linear move text."
	skein = BevelSkein()
	skein.parseGcode( filletPreferences, gcodeText )
	return skein.output.getvalue()

def getFilletChainGcode( fileName, gcodeText, filletPreferences = None ):
	"Fillet a gcode linear move text.  Chain oozebane the gcode if it is not already oozebaned."
	gcodeText = gcodec.getGcodeFileText( fileName, gcodeText )
	if not gcodec.isProcedureDone( gcodeText, 'oozebane' ):
		gcodeText = oozebane.getOozebaneChainGcode( fileName, gcodeText )
	return getFilletGcode( gcodeText, filletPreferences )

def getFilletGcode( gcodeText, filletPreferences = None ):
	"Fillet a gcode linear move text."
	if gcodeText == '':
		return ''
	if gcodec.isProcedureDone( gcodeText, 'fillet' ):
		return gcodeText
	if filletPreferences == None:
		filletPreferences = FilletPreferences()
		preferences.readPreferences( filletPreferences )
	if not filletPreferences.activateFillet.value:
		return gcodeText
	if filletPreferences.arcPoint.value:
		return getArcPointGcode( filletPreferences, gcodeText )
	elif filletPreferences.arcRadius.value:
		return getArcRadiusGcode( filletPreferences, gcodeText )
	elif filletPreferences.arcSegment.value:
		return getArcSegmentGcode( filletPreferences, gcodeText )
	elif filletPreferences.bevel.value:
		return getBevelGcode( filletPreferences, gcodeText )
	return gcodeText

def writeOutput( fileName = '' ):
	"""Fillet a gcode linear move file.  Chain fill the gcode if it is not already filled.
	Depending on the preferences, either arcPoint, arcRadius, arcSegment, bevel or do nothing.
	If no fileName is specified, fillet the first unmodified gcode file in this folder."""
	if fileName == '':
		unmodified = interpret.getGNUTranslatorFilesUnmodified()
		if len( unmodified ) == 0:
			print( "There are no unmodified gcode files in this folder." )
			return
		fileName = unmodified[ 0 ]
	filletPreferences = FilletPreferences()
	preferences.readPreferences( filletPreferences )
	startTime = time.time()
	print( 'File ' + gcodec.getSummarizedFilename( fileName ) + ' is being chain filleted.' )
	suffixFilename = fileName[ : fileName.rfind( '.' ) ] + '_fillet.gcode'
	filletGcode = getFilletChainGcode( fileName, '', filletPreferences )
	if filletGcode == '':
		return
	gcodec.writeFileText( suffixFilename, filletGcode )
	print( 'The filleted file is saved as ' + gcodec.getSummarizedFilename( suffixFilename ) )
	analyze.writeOutput( suffixFilename, filletGcode )
	print( 'It took ' + str( int( round( time.time() - startTime ) ) ) + ' seconds to fillet the file.' )


class BevelSkein:
	"A class to bevel a skein of extrusions."
	def __init__( self ):
		self.decimalPlacesCarried = 3
		self.extruderActive = False
		self.feedrateMinute = 960.0
		self.filletRadius = 0.2
		self.lineIndex = 0
		self.lines = None
		self.oldFeedrateMinute = None
		self.oldLocation = None
		self.output = cStringIO.StringIO()
		self.shouldAddLine = True

	def addFeedrateEnd( self, feedrate ):
		"Add the gcode feedrate and a newline to the output."
		self.addLine(  ' F' + self.getRounded( feedrate ) )

	def addLine( self, line ):
		"Add a line of text and a newline to the output."
		self.output.write( line + '\n' )

	def addLinearMovePoint( self, feedrate, point ):
		"Add a gcode linear move, feedrate and newline to the output."
		self.output.write( 'G1' )
		self.addPoint( point )
		self.addFeedrateEnd( feedrate )

	def addPoint( self, point ):
		"Add a gcode point to the output."
		self.output.write( " X%s Y%s Z%s" % ( self.getRounded( point.x ), self.getRounded( point.y ), self.getRounded( point.z ) ) )

	def getCornerFeedrate( self ):
		"Get the corner feedrate, which may be based on the intermediate feedrate."
		feedrateMinute = self.feedrateMinute
		if self.filletPreferences.useIntermediateFeedrateInCorners.value:
			if self.oldFeedrateMinute != None:
				feedrateMinute = 0.5 * ( self.oldFeedrateMinute + self.feedrateMinute )
		return feedrateMinute * self.cornerFeedrateOverOperatingFeedrate

	def getExtruderOffReversalPoint( self, afterSegment, beforeSegment, location ):
		"If the extruder is off and the path is reversing, add intermediate slow points."
		if self.reversalSlowdownDistance < 0.1:
			return None
		reversalBufferSlowdownDistance = self.reversalSlowdownDistance * 1.2
		if afterSegment.magnitude() < reversalBufferSlowdownDistance:
			return None
		if beforeSegment.magnitude() < reversalBufferSlowdownDistance:
			return None
		afterSegmentNormalized = afterSegment / afterSegment.magnitude()
		beforeSegmentNormalized = beforeSegment / beforeSegment.magnitude()
		planeDot = euclidean.getPlaneDot( beforeSegmentNormalized, afterSegmentNormalized )
		if self.extruderActive:
			return None
		if planeDot < 0.95:
			return None
		slowdownFeedrate = self.feedrateMinute * 0.333333333
		self.shouldAddLine = False
		beforePoint = euclidean.getPointPlusSegmentWithLength( self.reversalSlowdownDistance, location, beforeSegment )
		self.addLinearMovePoint( self.feedrateMinute, beforePoint )
		self.addLinearMovePoint( slowdownFeedrate, location )
		afterPoint = euclidean.getPointPlusSegmentWithLength( self.reversalSlowdownDistance, location, afterSegment )
		self.addLinearMovePoint( slowdownFeedrate, afterPoint )
		return afterPoint

	def getNextLocation( self ):
		"Get the next linear move.  Return none is none is found."
		for afterIndex in xrange( self.lineIndex + 1, len( self.lines ) ):
			line = self.lines[ afterIndex ]
			splitLine = line.split( ' ' )
			if gcodec.getFirstWord( splitLine ) == 'G1':
				nextLocation = gcodec.getLocationFromSplitLine( self.oldLocation, splitLine )
				return nextLocation
		return None

	def getRounded( self, number ):
		"Get number rounded to the number of carried decimal places as a string."
		return euclidean.getRoundedToDecimalPlacesString( self.decimalPlacesCarried, number )

	def linearMove( self, splitLine ):
		"Bevel a linear move."
		location = gcodec.getLocationFromSplitLine( self.oldLocation, splitLine )
		self.feedrateMinute = gcodec.getFeedrateMinute( self.feedrateMinute, splitLine )
		if self.oldLocation != None:
			nextLocation = self.getNextLocation()
			if nextLocation != None:
				location = self.splitPointGetAfter( location, nextLocation )
		self.oldLocation = location
		self.oldFeedrateMinute = self.feedrateMinute

	def parseGcode( self, filletPreferences, gcodeText ):
		"Parse gcode text and store the bevel gcode."
		self.cornerFeedrateOverOperatingFeedrate = filletPreferences.cornerFeedrateOverOperatingFeedrate.value
		self.lines = gcodec.getTextLines( gcodeText )
		self.filletPreferences = filletPreferences
		self.parseInitialization( filletPreferences )
		for self.lineIndex in xrange( self.lineIndex, len( self.lines ) ):
			line = self.lines[ self.lineIndex ]
			self.parseLine( line )

	def parseInitialization( self, filletPreferences ):
		"Parse gcode initialization and store the parameters."
		for self.lineIndex in xrange( len( self.lines ) ):
			line = self.lines[ self.lineIndex ]
			splitLine = line.split()
			firstWord = gcodec.getFirstWord( splitLine )
			if firstWord == '(<extrusionWidth>':
				absoluteExtrusionWidth = abs( float( splitLine[ 1 ] ) )
				self.reversalSlowdownDistance = absoluteExtrusionWidth * filletPreferences.reversalSlowdownDistanceOverExtrusionWidth.value
				self.filletRadius = absoluteExtrusionWidth * filletPreferences.filletRadiusOverExtrusionWidth.value
			elif firstWord == '(<decimalPlacesCarried>':
				self.decimalPlacesCarried = int( splitLine[ 1 ] )
			elif firstWord == '(</extruderInitialization>)':
				self.addLine( '(<procedureDone> fillet </procedureDone>)' )
				return
			self.addLine( line )

	def parseLine( self, line ):
		"Parse a gcode line and add it to the bevel gcode."
		self.shouldAddLine = True
		splitLine = line.split()
		if len( splitLine ) < 1:
			return
		firstWord = splitLine[ 0 ]
		if firstWord == 'G1':
			self.linearMove( splitLine )
		elif firstWord == 'M101':
			self.extruderActive = True
		elif firstWord == 'M103':
			self.extruderActive = False
		if self.shouldAddLine:
			self.addLine( line )

	def splitPointGetAfter( self, location, nextLocation ):
		"Bevel a point and return the end of the bevel."
		bevelLength = 0.5 * self.filletRadius
		beforeSegment = self.oldLocation - location
		halfBeforeSegmentLength = 0.5 * beforeSegment.magnitude()
		if halfBeforeSegmentLength <= 0.0:
			return location
		afterSegment = nextLocation - location
		afterSegmentExtension = 0.333 * afterSegment.magnitude()
		if afterSegmentExtension <= 0.0:
			return location
		extruderOffReversalPoint = self.getExtruderOffReversalPoint( afterSegment, beforeSegment, location )
		if extruderOffReversalPoint != None:
			return extruderOffReversalPoint
		bevelLength = min( afterSegmentExtension, bevelLength )
		self.shouldAddLine = False
		if halfBeforeSegmentLength < bevelLength:
			bevelLength = halfBeforeSegmentLength
		else:
			beforePoint = euclidean.getPointPlusSegmentWithLength( bevelLength, location, beforeSegment )
			self.addLinearMovePoint( self.getCornerFeedrate(), beforePoint )
		afterPoint = euclidean.getPointPlusSegmentWithLength( bevelLength, location, afterSegment )
		self.addLinearMovePoint( self.feedrateMinute, afterPoint )
		return afterPoint


class ArcSegmentSkein( BevelSkein ):
	"A class to arc segment a skein of extrusions."
	def addArc( self, afterCenterDifferenceAngle, afterPoint, beforeCenterSegment, beforePoint, center ):
		"Add arc segments to the filleted skein."
		curveSection = 0.5
		absoluteDifferenceAngle = abs( afterCenterDifferenceAngle )
		steps = int( math.ceil( max( absoluteDifferenceAngle * 2.4, absoluteDifferenceAngle * beforeCenterSegment.magnitude() / curveSection ) ) )
		stepPlaneAngle = euclidean.getPolar( afterCenterDifferenceAngle / steps, 1.0 )
		for step in xrange( 1, steps ):
			beforeCenterSegment = euclidean.getRoundZAxisByPlaneAngle( stepPlaneAngle, beforeCenterSegment )
			arcPoint = center + beforeCenterSegment
			self.addLinearMovePoint( self.getCornerFeedrate(), arcPoint )
		self.addLinearMovePoint( self.getCornerFeedrate(), afterPoint )

	def splitPointGetAfter( self, location, nextLocation ):
		"Fillet a point into arc segments and return the end of the last segment."
		afterSegment = nextLocation - location
		afterSegmentLength = afterSegment.magnitude()
		afterSegmentExtension = 0.5 * afterSegmentLength
		if afterSegmentExtension == 0.0:
			return location
		beforeSegment = self.oldLocation - location
		beforeSegmentLength = beforeSegment.magnitude()
		if beforeSegmentLength == 0.0:
			return location
		radius = self.filletRadius
		afterSegmentNormalized = afterSegment / afterSegmentLength
		beforeSegmentNormalized = beforeSegment / beforeSegmentLength
		betweenCenterDotNormalized = afterSegmentNormalized + beforeSegmentNormalized
		if betweenCenterDotNormalized.magnitude() < 0.01 * self.filletRadius:
			return location
		extruderOffReversalPoint = self.getExtruderOffReversalPoint( afterSegment, beforeSegment, location )
		if extruderOffReversalPoint != None:
			return extruderOffReversalPoint
		betweenCenterDotNormalized.normalize()
		beforeSegmentNormalizedWiddershins = euclidean.getRotatedWiddershinsQuarterAroundZAxis( beforeSegmentNormalized )
		betweenAfterPlaneDot = abs( euclidean.getPlaneDot( betweenCenterDotNormalized, beforeSegmentNormalizedWiddershins ) )
		centerDotDistance = radius / betweenAfterPlaneDot
		bevelLength = math.sqrt( centerDotDistance * centerDotDistance - radius * radius )
		radiusOverBevelLength = radius / bevelLength
		bevelLength = min( bevelLength, radius )
		bevelLength = min( afterSegmentExtension, bevelLength )
		beforePoint = self.oldLocation
		if beforeSegmentLength < bevelLength:
			bevelLength = beforeSegmentLength
		else:
			beforePoint = euclidean.getPointPlusSegmentWithLength( bevelLength, location, beforeSegment )
			self.addLinearMovePoint( self.feedrateMinute, beforePoint )
		self.shouldAddLine = False
		afterPoint = euclidean.getPointPlusSegmentWithLength( bevelLength, location, afterSegment )
		radius = bevelLength * radiusOverBevelLength
		centerDotDistance = radius / betweenAfterPlaneDot
		center = location + betweenCenterDotNormalized * centerDotDistance
		afterCenterSegment = afterPoint - center
		beforeCenterSegment = beforePoint - center
		afterCenterDifferenceAngle = euclidean.getAngleAroundZAxisDifference( afterCenterSegment, beforeCenterSegment )
		self.addArc( afterCenterDifferenceAngle, afterPoint, beforeCenterSegment, beforePoint, center )
		return afterPoint


class ArcPointSkein( ArcSegmentSkein ):
	"A class to arc point a skein of extrusions."
	def addArc( self, afterCenterDifferenceAngle, afterPoint, beforeCenterSegment, beforePoint, center ):
		"Add an arc point to the filleted skein."
		afterPointMinusBefore = afterPoint - beforePoint
		centerMinusBefore = center - beforePoint
		if afterCenterDifferenceAngle > 0.0:
			self.output.write( 'G3' )
		else:
			self.output.write( 'G2' )
		self.addPoint( afterPointMinusBefore )
		self.addRelativeCenter( centerMinusBefore )
		self.addFeedrateEnd( self.getCornerFeedrate() )

	def addRelativeCenter( self, centerMinusBefore ):
		"Add the relative center to a line of the arc point filleted skein."
		self.output.write( ' I' + self.getRounded( centerMinusBefore.x ) + ' J' + self.getRounded( centerMinusBefore.y ) )


class ArcRadiusSkein( ArcPointSkein ):
	"A class to arc radius a skein of extrusions."
	def addRelativeCenter( self, centerMinusBefore ):
		"Add the relative center to a line of the arc radius filleted skein."
		planeCenterMinusBefore = centerMinusBefore.dropAxis( 2 )
		radius = abs( planeCenterMinusBefore )
		self.output.write( ' R' + self.getRounded( radius ) )


class FilletPreferences:
	"A class to handle the fillet preferences."
	def __init__( self ):
		"Set the default preferences, execute title & preferences fileName."
		#Set the default preferences.
		self.archive = []
		self.activateFillet = preferences.BooleanPreference().getFromValue( 'Activate Fillet', True )
		self.archive.append( self.activateFillet )
		self.filletProcedureChoiceLabel = preferences.LabelDisplay().getFromName( 'Fillet Procedure Choice: ' )
		self.archive.append( self.filletProcedureChoiceLabel )
		filletRadio = []
		self.arcPoint = preferences.Radio().getFromRadio( 'Arc Point', filletRadio, False )
		self.archive.append( self.arcPoint )
		self.arcRadius = preferences.Radio().getFromRadio( 'Arc Radius', filletRadio, False )
		self.archive.append( self.arcRadius )
		self.arcSegment = preferences.Radio().getFromRadio( 'Arc Segment', filletRadio, False )
		self.archive.append( self.arcSegment )
		self.bevel = preferences.Radio().getFromRadio( 'Bevel', filletRadio, True )
		self.archive.append( self.bevel )
		self.cornerFeedrateOverOperatingFeedrate = preferences.FloatPreference().getFromValue( 'Corner Feedrate over Operating Feedrate (ratio):', 1.0 )
		self.archive.append( self.cornerFeedrateOverOperatingFeedrate )
		self.filletRadiusOverExtrusionWidth = preferences.FloatPreference().getFromValue( 'Fillet Radius over Extrusion Width (ratio):', 0.35 )
		self.archive.append( self.filletRadiusOverExtrusionWidth )
		self.fileNameInput = preferences.Filename().getFromFilename( interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File to be Filleted', '' )
		self.archive.append( self.fileNameInput )
		self.reversalSlowdownDistanceOverExtrusionWidth = preferences.FloatPreference().getFromValue( 'Reversal Slowdown Distance over Extrusion Width (ratio):', 0.5 )
		self.archive.append( self.reversalSlowdownDistanceOverExtrusionWidth )
		self.useIntermediateFeedrateInCorners = preferences.BooleanPreference().getFromValue( 'Use Intermediate Feedrate in Corners', True )
		self.archive.append( self.useIntermediateFeedrateInCorners )
		#Create the archive, title of the execute button, title of the dialog & preferences fileName.
		self.executeTitle = 'Fillet'
		self.saveTitle = 'Save Preferences'
		preferences.setHelpPreferencesFileNameTitleWindowPosition( self, 'skeinforge_tools.fillet.html' )

	def execute( self ):
		"Fillet button has been clicked."
		fileNames = polyfile.getFileOrDirectoryTypesUnmodifiedGcode( self.fileNameInput.value, interpret.getImportPluginFilenames(), self.fileNameInput.wasCancelled )
		for fileName in fileNames:
			writeOutput( fileName )


def main():
	"Display the fillet dialog."
	if len( sys.argv ) > 1:
		writeOutput( ' '.join( sys.argv[ 1 : ] ) )
	else:
		preferences.displayDialog( FilletPreferences() )

if __name__ == "__main__":
	main()
