Adding SkeinPyPy_NewUI as development for a brand new user interface.

Experimental, doesn't slice yet, loads of work ahead.
master
Daid 2012-02-20 00:30:49 +01:00
parent a6f1c941f1
commit a414a80837
237 changed files with 47561 additions and 0 deletions

View File

@ -0,0 +1,2 @@
This SkeinPyPy version is based in Skeinforge: 49

View File

@ -0,0 +1,9 @@
#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
import os
import sys
numberOfLevelsDeepInPackageHierarchy = 0
packageFilePath = os.path.abspath(__file__)
for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ):
packageFilePath = os.path.dirname( packageFilePath )
if packageFilePath not in sys.path:
sys.path.insert( 0, packageFilePath )

View File

@ -0,0 +1,13 @@
"""
This page is in the table of contents.
This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
"""
import os
import sys
numberOfLevelsDeepInPackageHierarchy = 1
packageFilePath = os.path.abspath(__file__)
for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ):
packageFilePath = os.path.dirname( packageFilePath )
if packageFilePath not in sys.path:
sys.path.insert( 0, packageFilePath )

View File

@ -0,0 +1,380 @@
"""
Boolean geometry utilities.
"""
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__
import os
import sys
import traceback
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
globalTemporarySettingsPath = os.path.join(os.path.expanduser('~'), '.skeinforge_pypy')
def addToNamePathDictionary(directoryPath, namePathDictionary):
'Add to the name path dictionary.'
pluginFileNames = getPluginFileNamesFromDirectoryPath(directoryPath)
for pluginFileName in pluginFileNames:
namePathDictionary[pluginFileName.replace('_', '')] = os.path.join(directoryPath, pluginFileName)
def getAbsoluteFolderPath(filePath, folderName=''):
'Get the absolute folder path.'
absoluteFolderPath = os.path.dirname(os.path.abspath(filePath))
if folderName == '':
return absoluteFolderPath
return os.path.join(absoluteFolderPath, folderName)
def getAbsoluteFrozenFolderPath(filePath, folderName=''):
'Get the absolute frozen folder path.'
if hasattr(sys, 'frozen'):
if '.py' in filePath:
filePath = ''.join(filePath.rpartition('\\')[: 2])
filePath = os.path.join(filePath, 'skeinforge_application')
return getAbsoluteFolderPath(filePath, folderName)
def getAnalyzePluginsDirectoryPath(subName=''):
'Get the analyze plugins directory path.'
return getJoinedPath(getSkeinforgePluginsPath('analyze_plugins'), subName)
def getCraftPluginsDirectoryPath(subName=''):
'Get the craft plugins directory path.'
return getJoinedPath(getSkeinforgePluginsPath('craft_plugins'), subName)
def getDocumentationPath(subName=''):
'Get the documentation file path.'
return getJoinedPath(getFabmetheusPath('documentation'), subName)
def getElementsPath(subName=''):
'Get the evaluate_elements directory path.'
return getJoinedPath(getGeometryUtilitiesPath('evaluate_elements'), subName)
def getEndsWithList(word, wordEndings):
'Determine if the word ends with a list.'
for wordEnding in wordEndings:
if word.endswith(wordEnding):
return True
return False
def getFabmetheusPath(subName=''):
'Get the fabmetheus directory path.'
fabmetheusFile = None
if hasattr(sys, 'frozen'):
fabmetheusFile = unicode(sys.executable, sys.getfilesystemencoding())
else:
fabmetheusFile = os.path.dirname(os.path.abspath(__file__))
return getJoinedPath(os.path.dirname(fabmetheusFile), subName)
def getFabmetheusToolsPath(subName=''):
'Get the fabmetheus tools directory path.'
return getJoinedPath(getFabmetheusUtilitiesPath('fabmetheus_tools'), subName)
def getFabmetheusUtilitiesPath(subName=''):
'Get the fabmetheus utilities directory path.'
return getJoinedPath(getFabmetheusPath('fabmetheus_utilities'), subName)
def getFileNamesByFilePaths(pluginFilePaths):
'Get the file names of the plugins by the file paths.'
fileNames = []
for pluginFilePath in pluginFilePaths:
pluginBasename = os.path.basename(pluginFilePath)
pluginBasename = getUntilDot(pluginBasename)
fileNames.append(pluginBasename)
return fileNames
def getFilePaths(fileInDirectory=''):
'Get the file paths in the directory of the file in directory.'
directoryName = os.getcwd()
if fileInDirectory != '':
directoryName = os.path.dirname(fileInDirectory)
return getFilePathsByDirectory(directoryName)
def getFilePathsByDirectory(directoryName):
'Get the file paths in the directory of the file in directory.'
absoluteDirectoryPath = os.path.abspath(directoryName)
directory = os.listdir(directoryName)
filePaths = []
for fileName in directory:
filePaths.append(os.path.join(absoluteDirectoryPath, fileName))
return filePaths
def getFilePathsRecursively(fileInDirectory=''):
'Get the file paths in the directory of the file in directory.'
filePaths = getFilePaths(fileInDirectory)
filePathsRecursively = filePaths[:]
for filePath in filePaths:
if os.path.isdir(filePath):
directory = os.listdir(filePath)
if len(directory) > 0:
filePathsRecursively += getFilePathsRecursively(os.path.join(filePath, directory[0]))
return filePathsRecursively
def getFilePathWithUnderscoredBasename(fileName, suffix):
'Get the file path with all spaces in the basename replaced with underscores.'
suffixFileName = getUntilDot(fileName) + suffix
suffixDirectoryName = os.path.dirname(suffixFileName)
suffixReplacedBaseName = os.path.basename(suffixFileName).replace(' ', '_')
return os.path.join(suffixDirectoryName, suffixReplacedBaseName)
def getFilesWithFileTypesWithoutWords(fileTypes, words = [], fileInDirectory=''):
'Get files which have a given file type, but with do not contain a word in a list.'
filesWithFileTypes = []
for filePath in getFilePaths(fileInDirectory):
for fileType in fileTypes:
if isFileWithFileTypeWithoutWords(fileType, filePath, words):
filesWithFileTypes.append(filePath)
filesWithFileTypes.sort()
return filesWithFileTypes
def getFilesWithFileTypesWithoutWordsRecursively(fileTypes, words = [], fileInDirectory=''):
'Get files recursively which have a given file type, but with do not contain a word in a list.'
filesWithFileTypesRecursively = []
for filePath in getFilePathsRecursively(fileInDirectory):
for fileType in fileTypes:
if isFileWithFileTypeWithoutWords(fileType, filePath, words):
filesWithFileTypesRecursively.append(filePath)
filesWithFileTypesRecursively.sort()
return filesWithFileTypesRecursively
def getFilesWithFileTypeWithoutWords(fileType, words = [], fileInDirectory=''):
'Get files which have a given file type, but with do not contain a word in a list.'
filesWithFileType = []
for filePath in getFilePaths(fileInDirectory):
if isFileWithFileTypeWithoutWords(fileType, filePath, words):
filesWithFileType.append(filePath)
filesWithFileType.sort()
return filesWithFileType
def getFileText(fileName, printWarning=True, readMode='r'):
'Get the entire text of a file.'
try:
file = open(fileName, readMode)
fileText = file.read()
file.close()
return fileText
except IOError:
if printWarning:
print('The file ' + fileName + ' does not exist.')
return ''
def getFileTextInFileDirectory(fileInDirectory, fileName, readMode='r'):
'Get the entire text of a file in the directory of the file in directory.'
absoluteFilePathInFileDirectory = os.path.join(os.path.dirname(fileInDirectory), fileName)
return getFileText(absoluteFilePathInFileDirectory, True, readMode)
def getFundamentalsPath(subName=''):
'Get the evaluate_fundamentals directory path.'
return getJoinedPath(getGeometryUtilitiesPath('evaluate_fundamentals'), subName)
def getGeometryDictionary(folderName):
'Get to the geometry name path dictionary.'
geometryDictionary={}
geometryDirectory = getGeometryPath()
addToNamePathDictionary(os.path.join(geometryDirectory, folderName), geometryDictionary)
geometryPluginsDirectory = getFabmetheusUtilitiesPath('geometry_plugins')
addToNamePathDictionary(os.path.join(geometryPluginsDirectory, folderName), geometryDictionary)
return geometryDictionary
def getGeometryPath(subName=''):
'Get the geometry directory path.'
return getJoinedPath(getFabmetheusUtilitiesPath('geometry'), subName)
def getGeometryToolsPath(subName=''):
'Get the geometry tools directory path.'
return getJoinedPath(getGeometryPath('geometry_tools'), subName)
def getGeometryUtilitiesPath(subName=''):
'Get the geometry_utilities directory path.'
return getJoinedPath(getGeometryPath('geometry_utilities'), subName)
def getInterpretPluginsPath(subName=''):
'Get the interpret plugins directory path.'
return getJoinedPath(getFabmetheusToolsPath('interpret_plugins'), subName)
def getJoinedPath(path, subName=''):
'Get the joined file path.'
if subName == '':
return path
return os.path.join(path, subName)
def getModuleWithDirectoryPath(directoryPath, fileName):
'Get the module from the fileName and folder name.'
if fileName == '':
print('The file name in getModule in archive was empty.')
return None
originalSystemPath = sys.path[:]
try:
sys.path.insert(0, directoryPath)
folderPluginsModule = __import__(fileName)
sys.path = originalSystemPath
return folderPluginsModule
except:
sys.path = originalSystemPath
print('')
print('Exception traceback in getModuleWithDirectoryPath in archive:')
traceback.print_exc(file=sys.stdout)
print('')
print('That error means; could not import a module with the fileName ' + fileName)
print('and an absolute directory name of ' + directoryPath)
print('')
return None
def getModuleWithPath(path):
'Get the module from the path.'
return getModuleWithDirectoryPath(os.path.dirname(path), os.path.basename(path))
def getPluginFileNamesFromDirectoryPath(directoryPath):
'Get the file names of the python plugins in the directory path.'
fileInDirectory = os.path.join(directoryPath, '__init__.py')
return getFileNamesByFilePaths(getPythonFileNamesExceptInit(fileInDirectory))
def getProfilesPath(subName=''):
'Get the profiles directory path, which is the settings directory joined with profiles.'
return getJoinedPath(getSettingsPath('profiles'), subName)
def getPythonDirectoryNames(directoryName):
'Get the python directories.'
pythonDirectoryNames = []
directory = os.listdir(directoryName)
for fileName in directory:
subdirectoryName = os.path.join(directoryName, fileName)
if os.path.isdir(subdirectoryName):
if os.path.isfile(os.path.join(subdirectoryName, '__init__.py')):
pythonDirectoryNames.append(subdirectoryName)
return pythonDirectoryNames
def getPythonDirectoryNamesRecursively(directoryName=''):
'Get the python directories recursively.'
recursivePythonDirectoryNames = []
if directoryName == '':
directoryName = os.getcwd()
if os.path.isfile(os.path.join(directoryName, '__init__.py')):
recursivePythonDirectoryNames.append(directoryName)
pythonDirectoryNames = getPythonDirectoryNames(directoryName)
for pythonDirectoryName in pythonDirectoryNames:
recursivePythonDirectoryNames += getPythonDirectoryNamesRecursively(pythonDirectoryName)
else:
return []
return recursivePythonDirectoryNames
def getPythonFileNamesExceptInit(fileInDirectory=''):
'Get the python fileNames of the directory which the fileInDirectory is in, except for the __init__.py file.'
pythonFileNamesExceptInit = getFilesWithFileTypeWithoutWords('py', ['__init__.py'], fileInDirectory)
pythonFileNamesExceptInit.sort()
return pythonFileNamesExceptInit
def getPythonFileNamesExceptInitRecursively(directoryName=''):
'Get the python fileNames of the directory recursively, except for the __init__.py files.'
pythonDirectoryNames = getPythonDirectoryNamesRecursively(directoryName)
pythonFileNamesExceptInitRecursively = []
for pythonDirectoryName in pythonDirectoryNames:
pythonFileNamesExceptInitRecursively += getPythonFileNamesExceptInit(os.path.join(pythonDirectoryName, '__init__.py'))
pythonFileNamesExceptInitRecursively.sort()
return pythonFileNamesExceptInitRecursively
def getSettingsPath(subName=''):
'Get the settings directory path, which is the home directory joined with .skeinforge.'
global globalTemporarySettingsPath
return getJoinedPath(globalTemporarySettingsPath, subName)
def getSkeinforgePath(subName=''):
'Get the skeinforge directory path.'
return getJoinedPath(getFabmetheusPath('skeinforge_application'), subName)
def getSkeinforgePluginsPath(subName=''):
'Get the skeinforge plugins directory path.'
return getJoinedPath(getSkeinforgePath('skeinforge_plugins'), subName)
def getSummarizedFileName(fileName):
'Get the fileName basename if the file is in the current working directory, otherwise return the original full name.'
if os.getcwd() == os.path.dirname(fileName):
return os.path.basename(fileName)
return fileName
def getTemplatesPath(subName=''):
'Get the templates directory path.'
return getJoinedPath(getFabmetheusUtilitiesPath('templates'), subName)
def getTextIfEmpty(fileName, text):
'Get the text from a file if it the text is empty.'
if text != '':
return text
return getFileText(fileName)
def getTextLines(text):
'Get the all the lines of text of a text.'
if '\r' in text:
text = text.replace('\r', '\n').replace('\n\n', '\n')
textLines = text.split('\n')
if len(textLines) == 1:
if textLines[0] == '':
return []
return textLines
def getUntilDot(text):
'Get the text until the last dot, if any.'
dotIndex = text.rfind('.')
if dotIndex < 0:
return text
return text[: dotIndex]
def getVersionFileName():
'Get the file name of the version date.getFabmetheusUtilitiesPath(subName='')'
return getFabmetheusUtilitiesPath('version.txt')
def isFileWithFileTypeWithoutWords(fileType, fileName, words):
'Determine if file has a given file type, but with does not contain a word in a list.'
fileName = os.path.basename(fileName)
fileTypeDot = '.' + fileType
if not fileName.endswith(fileTypeDot):
return False
for word in words:
if fileName.find(word) >= 0:
return False
return True
def makeDirectory(directoryPath):
'Make a directory if it does not already exist.'
if os.path.isdir(directoryPath):
return
try:
print('The following directory was made:')
print(os.path.abspath(directoryPath))
os.makedirs(directoryPath)
except OSError:
print('Skeinforge can not make the directory %s so give it read/write permission for that directory and the containing directory.' % directoryPath)
def removeBackupFilesByType(fileType):
'Remove backup files by type.'
backupFilePaths = getFilesWithFileTypesWithoutWordsRecursively([fileType + '~'])
for backupFilePath in backupFilePaths:
os.remove(backupFilePath)
def removeBackupFilesByTypes(fileTypes):
'Remove backup files by types.'
for fileType in fileTypes:
removeBackupFilesByType(fileType)
def writeFileMessageEnd(end, fileName, fileText, message):
'Write to a fileName with a suffix and print a message.'
suffixFileName = getUntilDot(fileName) + end
writeFileText(suffixFileName, fileText)
print(message + getSummarizedFileName(suffixFileName))
def writeFileText(fileName, fileText, writeMode='w+'):
'Write a text to a file.'
try:
file = open(fileName, writeMode)
file.write(fileText)
file.close()
except IOError:
print('The file ' + fileName + ' can not be written to.')

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,9 @@
#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
import os
import sys
numberOfLevelsDeepInPackageHierarchy = 2
packageFilePath = os.path.abspath(__file__)
for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ):
packageFilePath = os.path.dirname( packageFilePath )
if packageFilePath not in sys.path:
sys.path.insert( 0, packageFilePath )

View File

@ -0,0 +1,232 @@
"""
Alphabetize is a script to alphabetize functions and signatures.
"""
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 fabmetheus_utilities import archive
import cStringIO
import os
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__date__ = '$Date: 2008/21/04 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def addTogetherList(functionList, togetherLists):
'Add the togetherList to the togetherLists is the sorted is different.'
sortedList = functionList[:]
sortedList.sort(compareFunctionName)
togetherList = None
for functionIndex in xrange(len(functionList)):
function = functionList[functionIndex]
sorted = sortedList[functionIndex]
if function != sorted:
together = (function, sorted)
if togetherList == None:
togetherList = []
togetherLists.append(togetherList)
togetherList.append(together)
def compareFunctionName(first, second):
'Compare the function names.'
first = getConvertedName(first)
second = getConvertedName(second)
if first < second:
return -1
return first < second
def getConvertedName(name):
'Get converted name with init at the beginning and main at the endCompare the function names.'
if name == 'def __init__':
return 'def !__init__'
if name == 'def main':
return 'def |main'
return name.lower()
def getFunctionLists(fileName):
'Get the function lists in the file.'
fileText = archive.getFileText(fileName)
functionList = []
functionLists = [functionList]
lines = archive.getTextLines(fileText)
for line in lines:
lineStripped = line.strip()
if lineStripped.startswith('def '):
bracketIndex = lineStripped.find('(')
if bracketIndex > -1:
lineStripped = lineStripped[: bracketIndex]
functionList.append(lineStripped)
elif line.startswith('class'):
functionList = []
functionLists.append(functionList)
return functionLists
def getFunctionsWithStringByFileName(fileName, searchString):
'Get the functions with the search string in the file.'
fileText = archive.getFileText(fileName)
functions = []
lines = archive.getTextLines(fileText)
for line in lines:
lineStripped = line.strip()
# if lineStripped.startswith('def ') and searchString in lineStripped and '=' in lineStripped:
if lineStripped.startswith('def ') and searchString in lineStripped:
if '(self, ' not in lineStripped or lineStripped.count(',') > 1:
functions.append(lineStripped[len('def ') :].strip())
functions.sort()
return functions
def getFunctionsWithStringByFileNames(fileNames, searchString):
'Get the functions with the search string in the files.'
functions = []
for fileName in fileNames:
functions += getFunctionsWithStringByFileName(fileName, searchString)
functions.sort()
return functions
def getParameterSequence(functionName):
'Get the parameter sequence.'
parameterDictionary = {}
parameterSequence = []
parameterText = functionName[functionName.find('(') + 1 :].replace('xmlElement', 'elementNode')
snippet = Snippet(0, parameterText)
strippedParameters = []
for parameter in snippet.parameters:
strippedParameter = parameter.strip()
if strippedParameter != 'self':
strippedParameters.append(strippedParameter)
for parameterIndex, parameter in enumerate(strippedParameters):
parameterDictionary[parameter] = parameterIndex
sortedParameters = strippedParameters[:]
sortedParameters.sort()
for sortedParameter in sortedParameters:
parameterSequence.append(parameterDictionary[sortedParameter])
return parameterSequence
def getSnippetsByFileName(fileName, functionName):
'Get the function signature snippets by the file name.'
fileText = archive.getFileText(fileName)
snippets = []
functionStart = functionName[: functionName.find('(') + 1]
tokenEnd = getTokenEnd(0, fileText, functionStart)
while tokenEnd != -1:
snippet = Snippet(tokenEnd, fileText)
snippets.append(snippet)
tokenEnd = getTokenEnd(snippet.characterIndex, fileText, functionStart)
return snippets
def getTogetherLists(fileName):
'Get the lists of the unsorted and sorted functions in the file.'
functionLists = getFunctionLists(fileName)
togetherLists = []
for functionList in functionLists:
addTogetherList(functionList, togetherLists)
return togetherLists
def getTokenEnd(characterIndex, fileText, token):
'Get the token end index for the file text and token.'
tokenIndex = fileText.find(token, characterIndex)
if tokenIndex == -1:
return -1
return tokenIndex + len(token)
def printTogetherListsByFileNames(fileNames):
'Print the together lists of the file names, if the file name has a together list.'
for fileName in fileNames:
togetherLists = getTogetherLists(fileName)
if len(togetherLists) > 0:
for togetherList in togetherLists:
for together in togetherList:
function = together[0]
sorted = together[1]
return
class EndCharacterMonad:
'A monad to return the parent monad when it encounters the end character.'
def __init__(self, endCharacter, parentMonad):
'Initialize.'
self.endCharacter = endCharacter
self.parentMonad = parentMonad
def getNextMonad(self, character):
'Get the next monad.'
self.getSnippet().input.write(character)
if character == self.endCharacter:
return self.parentMonad
return self
def getSnippet(self):
'Get the snippet.'
return self.parentMonad.getSnippet()
class ParameterMonad:
'A monad to handle parameters.'
def __init__(self, snippet):
'Initialize.'
self.snippet = snippet
def addParameter(self):
'Add parameter to the snippet.'
parameterString = self.snippet.input.getvalue()
if len(parameterString) != 0:
self.snippet.input = cStringIO.StringIO()
self.snippet.parameters.append(parameterString)
def getNextMonad(self, character):
'Get the next monad.'
if character == '"':
self.snippet.input.write(character)
return EndCharacterMonad('"', self)
if character == '"':
self.snippet.input.write(character)
return EndCharacterMonad('"', self)
if character == '(':
self.snippet.input.write(character)
return EndCharacterMonad(')', self)
if character == ')':
self.addParameter()
return None
if character == ',':
self.addParameter()
return self
self.snippet.input.write(character)
return self
def getSnippet(self):
'Get the snippet.'
return self.snippet
class Snippet:
'A class to get the variables for a function.'
def __init__(self, characterIndex, fileText):
'Initialize.'
self.characterIndex = characterIndex
self.input = cStringIO.StringIO()
self.parameters = []
monad = ParameterMonad(self)
for characterIndex in xrange(self.characterIndex, len(fileText)):
character = fileText[characterIndex]
monad = monad.getNextMonad(character)
if monad == None:
return
def __repr__(self):
'Get the string representation of this Snippet.'
return '%s %s' % (self.characterIndex, self.parameters)
def main():
'Run main function.'
# printTogetherListsByFileNames(archive.getPythonFileNamesExceptInitRecursively('/home/enrique/Desktop/fabmetheus'))
functions = getFunctionsWithStringByFileNames(archive.getPythonFileNamesExceptInitRecursively('/home/enrique/Desktop/fabmetheus'), ', xmlElement')
print(functions)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,132 @@
"""
Fabmetheus interpret is a fabmetheus utility to interpret a file, turning it into fabmetheus constructive solid geometry xml.
"""
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 fabmetheus_utilities import archive
from fabmetheus_utilities import euclidean
from fabmetheus_utilities import gcodec
from fabmetheus_utilities import settings
from skeinforge_application.skeinforge_utilities import skeinforge_polyfile
from skeinforge_application.skeinforge_utilities import skeinforge_profile
import os
import time
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__date__ = '$Date: 2008/21/04 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getCarving(fileName):
"Get carving."
pluginModule = getInterpretPlugin(fileName)
if pluginModule == None:
return None
return pluginModule.getCarving(fileName)
def getGNUTranslatorFilesUnmodified():
"Get the file types from the translators in the import plugins folder."
return archive.getFilesWithFileTypesWithoutWords(getImportPluginFileNames())
def getGNUTranslatorGcodeFileTypeTuples():
"Get the file type tuples from the translators in the import plugins folder plus gcode."
fileTypeTuples = getTranslatorFileTypeTuples()
fileTypeTuples.append( ('Gcode text files', '*.gcode') )
fileTypeTuples.sort()
return fileTypeTuples
def getImportPluginFileNames():
"Get interpret plugin fileNames."
return archive.getPluginFileNamesFromDirectoryPath( getPluginsDirectoryPath() )
def getInterpretPlugin(fileName):
"Get the interpret plugin for the file."
importPluginFileNames = getImportPluginFileNames()
for importPluginFileName in importPluginFileNames:
fileTypeDot = '.' + importPluginFileName
if fileName[ - len(fileTypeDot) : ].lower() == fileTypeDot:
importPluginsDirectoryPath = getPluginsDirectoryPath()
pluginModule = archive.getModuleWithDirectoryPath( importPluginsDirectoryPath, importPluginFileName )
if pluginModule != None:
return pluginModule
print('Could not find plugin to handle ' + fileName )
return None
def getNewRepository():
'Get new repository.'
return InterpretRepository()
def getPluginsDirectoryPath():
"Get the plugins directory path."
return archive.getInterpretPluginsPath()
def getTranslatorFileTypeTuples():
"Get the file types from the translators in the import plugins folder."
importPluginFileNames = getImportPluginFileNames()
fileTypeTuples = []
for importPluginFileName in importPluginFileNames:
fileTypeTitle = importPluginFileName.upper() + ' files'
fileType = ( fileTypeTitle, '*.' + importPluginFileName )
fileTypeTuples.append( fileType )
fileTypeTuples.sort()
return fileTypeTuples
def getWindowAnalyzeFile(fileName):
"Get file interpretion."
startTime = time.time()
carving = getCarving(fileName)
if carving == None:
return None
interpretGcode = str( carving )
if interpretGcode == '':
return None
repository = settings.getReadRepository( InterpretRepository() )
if repository.printInterpretion.value:
print(interpretGcode)
suffixFileName = fileName[ : fileName.rfind('.') ] + '_interpret.' + carving.getInterpretationSuffix()
suffixDirectoryName = os.path.dirname(suffixFileName)
suffixReplacedBaseName = os.path.basename(suffixFileName).replace(' ', '_')
suffixFileName = os.path.join( suffixDirectoryName, suffixReplacedBaseName )
archive.writeFileText( suffixFileName, interpretGcode )
print('The interpret file is saved as ' + archive.getSummarizedFileName(suffixFileName) )
print('It took %s to interpret the file.' % euclidean.getDurationString( time.time() - startTime ) )
textProgram = repository.textProgram.value
if textProgram == '':
return None
if textProgram == 'webbrowser':
settings.openWebPage(suffixFileName)
return None
textFilePath = '"' + os.path.normpath(suffixFileName) + '"' # " to send in file name with spaces
shellCommand = textProgram + ' ' + textFilePath
print('Sending the shell command:')
print(shellCommand)
commandResult = os.system(shellCommand)
if commandResult != 0:
print('It may be that the system could not find the %s program.' % textProgram )
print('If so, try installing the %s program or look for another one, like Open Office which can be found at:' % textProgram )
print('http://www.openoffice.org/')
print('Open office writer can then be started from the command line with the command "soffice -writer".')
class InterpretRepository:
"A class to handle the interpret settings."
def __init__(self):
"Set the default settings, execute title & settings fileName."
skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.analyze_plugins.interpret.html', self)
self.fileNameInput = settings.FileNameInput().getFromFileName( getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Interpret', self, '')
self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Interpret')
self.activateInterpret = settings.BooleanSetting().getFromValue('Activate Interpret', self, False )
self.printInterpretion = settings.BooleanSetting().getFromValue('Print Interpretion', self, False )
self.textProgram = settings.StringSetting().getFromValue('Text Program:', self, 'webbrowser')
self.executeTitle = 'Interpret'
def execute(self):
"Write button has been clicked."
fileNames = skeinforge_polyfile.getFileOrGcodeDirectory( self.fileNameInput.value, self.fileNameInput.wasCancelled )
for fileName in fileNames:
getWindowAnalyzeFile(fileName)

View File

@ -0,0 +1,12 @@
"""
This page is in the table of contents.
This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
"""
import os
import sys
numberOfLevelsDeepInPackageHierarchy = 3
packageFilePath = os.path.abspath(__file__)
for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ):
packageFilePath = os.path.dirname( packageFilePath )
if packageFilePath not in sys.path:
sys.path.insert( 0, packageFilePath )

View File

@ -0,0 +1,159 @@
"""
This page is in the table of contents.
The csv.py script is an import translator plugin to get a carving from an csv file.
An import plugin is a script in the interpret_plugins folder which has the function getCarving. It is meant to be run from the interpret tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name.
The getCarving function takes the file name of an csv file and returns the carving.
"""
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 fabmetheus_utilities import archive
from fabmetheus_utilities import gcodec
from fabmetheus_utilities import xml_simple_reader
import sys
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Nophead <http://hydraraptor.blogspot.com/>\nArt of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/21/04 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getCarving(fileName=''):
"Get the carving for the csv file."
csvText = archive.getFileText(fileName)
if csvText == '':
return None
csvParser = CSVSimpleParser( fileName, None, csvText )
lowerLocalName = csvParser.getDocumentElement().getNodeName().lower()
pluginModule = archive.getModuleWithDirectoryPath( getPluginsDirectoryPath(), lowerLocalName )
if pluginModule == None:
return None
return pluginModule.getCarvingFromParser( csvParser )
def getLineDictionary(line):
"Get the line dictionary."
lineDictionary = {}
splitLine = line.split('\t')
for splitLineIndex in xrange( len(splitLine) ):
word = splitLine[ splitLineIndex ]
if word != '':
lineDictionary[ splitLineIndex ] = word
return lineDictionary
def getPluginsDirectoryPath():
"Get the plugins directory path."
return archive.getInterpretPluginsPath('xml_plugins')
class CSVElement( xml_simple_reader.XMLElement ):
"A csv element."
def continueParsingObject( self, line, lineStripped ):
"Parse replaced line."
splitLineStripped = lineStripped.split('\t')
key = splitLineStripped[0]
value = splitLineStripped[1]
self.attributes[key] = value
self.addToIdentifierDictionaries()
def continueParsingTable( self, line, lineStripped ):
"Parse replaced line."
if self.headingDictionary == None:
self.headingDictionary = getLineDictionary(line)
return
csvElement = self
oldAttributesLength = len( self.attributes )
if oldAttributesLength > 0:
csvElement = CSVElement()
csvElement.parentNode = self.parentNode
csvElement.localName = self.localName
lineDictionary = getLineDictionary(line)
for columnIndex in lineDictionary.keys():
if columnIndex in self.headingDictionary:
key = self.headingDictionary[ columnIndex ]
value = lineDictionary[ columnIndex ]
csvElement.attributes[key] = value
csvElement.addToIdentifierDictionaries()
if len( csvElement.attributes ) == 0 or oldAttributesLength == 0 or self.parentNode == None:
return
self.parentNode.childNodes.append( csvElement )
def getElementFromObject( self, leadingTabCount, lineStripped, oldElement ):
"Parse replaced line."
splitLine = lineStripped.split('\t')
self.localName = splitLine[1]
if leadingTabCount == 0:
return self
self.parentNode = oldElement
while leadingTabCount <= self.parentNode.getNumberOfParents():
self.parentNode = self.parentNode.parentNode
self.parentNode.childNodes.append(self)
return self
def getElementFromTable( self, leadingTabCount, lineStripped, oldElement ):
"Parse replaced line."
self.headingDictionary = None
return self.getElementFromObject( leadingTabCount, lineStripped, oldElement )
def getNumberOfParents(self):
"Get the number of parent nodes."
if self.parentNode == None:
return 0
return self.parentNode.getNumberOfParents() + 1
class CSVSimpleParser( xml_simple_reader.DocumentNode ):
"A simple csv parser."
def __init__( self, parentNode, csvText ):
"Add empty lists."
self.continueFunction = None
self.extraLeadingTabCount = None
self.lines = archive.getTextLines( csvText )
self.oldCSVElement = None
self.documentElement = None
for line in self.lines:
self.parseLine(line)
def getNewCSVElement( self, leadingTabCount, lineStripped ):
"Get a new csv element."
if self.documentElement != None and self.extraLeadingTabCount == None:
self.extraLeadingTabCount = 1 - leadingTabCount
if self.extraLeadingTabCount != None:
leadingTabCount += self.extraLeadingTabCount
if lineStripped[ : len('_table') ] == '_table' or lineStripped[ : len('_t') ] == '_t':
self.oldCSVElement = CSVElement().getElementFromTable( leadingTabCount, lineStripped, self.oldCSVElement )
self.continueFunction = self.oldCSVElement.continueParsingTable
return
self.oldCSVElement = CSVElement().getElementFromObject( leadingTabCount, lineStripped, self.oldCSVElement )
self.continueFunction = self.oldCSVElement.continueParsingObject
def parseLine(self, line):
"Parse a gcode line and add it to the inset skein."
lineStripped = line.lstrip()
if len( lineStripped ) < 1:
return
leadingPart = line[ : line.find( lineStripped ) ]
leadingTabCount = leadingPart.count('\t')
if lineStripped[ : len('_') ] == '_':
self.getNewCSVElement( leadingTabCount, lineStripped )
if self.documentElement == None:
self.documentElement = self.oldCSVElement
self.documentElement.document = self
return
if self.continueFunction != None:
self.continueFunction( line, lineStripped )
def main():
"Display the inset dialog."
if len(sys.argv) > 1:
getCarving(' '.join(sys.argv[1 :]))
if __name__ == "__main__":
main()

View File

@ -0,0 +1,80 @@
"""
This page is in the table of contents.
The gts.py script is an import translator plugin to get a carving from an gts file.
An import plugin is a script in the interpret_plugins folder which has the function getCarving. It is meant to be run from the interpret tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name.
The getCarving function takes the file name of an gts file and returns the carving.
The GNU Triangulated Surface (.gts) format is described at:
http://gts.sourceforge.net/reference/gts-surfaces.html#GTS-SURFACE-WRITE
Quoted from http://gts.sourceforge.net/reference/gts-surfaces.html#GTS-SURFACE-WRITE
"All the lines beginning with GTS_COMMENTS (#!) are ignored. The first line contains three unsigned integers separated by spaces. The first integer is the number of vertexes, nv, the second is the number of edges, ne and the third is the number of faces, nf.
Follows nv lines containing the x, y and z coordinates of the vertexes. Follows ne lines containing the two indices (starting from one) of the vertexes of each edge. Follows nf lines containing the three ordered indices (also starting from one) of the edges of each face.
The format described above is the least common denominator to all GTS files. Consistent with an object-oriented approach, the GTS file format is extensible. Each of the lines of the file can be extended with user-specific attributes accessible through the read() and write() virtual methods of each of the objects written (surface, vertexes, edges or faces). When read with different object classes, these extra attributes are just ignored."
"""
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 fabmetheus_utilities.geometry.geometry_tools import face
from fabmetheus_utilities.geometry.solids import triangle_mesh
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import archive
from fabmetheus_utilities import gcodec
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Nophead <http://hydraraptor.blogspot.com/>\nArt of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/21/04 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getCarving(fileName):
"Get the carving for the gts file."
return getFromGNUTriangulatedSurfaceText( archive.getFileText(fileName), triangle_mesh.TriangleMesh() )
def getFromGNUTriangulatedSurfaceText( gnuTriangulatedSurfaceText, triangleMesh ):
"Initialize from a GNU Triangulated Surface Text."
if gnuTriangulatedSurfaceText == '':
return None
lines = archive.getTextLines( gnuTriangulatedSurfaceText )
linesWithoutComments = []
for line in lines:
if len(line) > 0:
firstCharacter = line[0]
if firstCharacter != '#' and firstCharacter != '!':
linesWithoutComments.append(line)
splitLine = linesWithoutComments[0].split()
numberOfVertexes = int( splitLine[0] )
numberOfEdges = int(splitLine[1])
numberOfFaces = int( splitLine[2] )
faceTriples = []
for vertexIndex in xrange( numberOfVertexes ):
line = linesWithoutComments[ vertexIndex + 1 ]
splitLine = line.split()
vertex = Vector3( float( splitLine[0] ), float(splitLine[1]), float( splitLine[2] ) )
triangleMesh.vertexes.append(vertex)
edgeStart = numberOfVertexes + 1
for edgeIndex in xrange( numberOfEdges ):
line = linesWithoutComments[ edgeIndex + edgeStart ]
splitLine = line.split()
vertexIndexes = []
for word in splitLine[ : 2 ]:
vertexIndexes.append( int(word) - 1 )
edge = face.Edge().getFromVertexIndexes( edgeIndex, vertexIndexes )
triangleMesh.edges.append( edge )
faceStart = edgeStart + numberOfEdges
for faceIndex in xrange( numberOfFaces ):
line = linesWithoutComments[ faceIndex + faceStart ]
splitLine = line.split()
edgeIndexes = []
for word in splitLine[ : 3 ]:
edgeIndexes.append( int(word) - 1 )
triangleMesh.faces.append( face.Face().getFromEdgeIndexes( edgeIndexes, triangleMesh.edges, faceIndex ) )
return triangleMesh

View File

@ -0,0 +1,78 @@
"""
This page is in the table of contents.
The obj.py script is an import translator plugin to get a carving from an obj file.
An example obj file is box.obj in the models folder.
An import plugin is a script in the interpret_plugins folder which has the function getCarving. It is meant to be run from the interpret tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name.
The getCarving function takes the file name of an obj file and returns the carving.
From wikipedia, OBJ (or .OBJ) is a geometry definition file format first developed by Wavefront Technologies for its Advanced Visualizer animation package:
http://en.wikipedia.org/wiki/Obj
The Object File specification is at:
http://local.wasp.uwa.edu.au/~pbourke/dataformats/obj/
An excellent link page about obj files is at:
http://people.sc.fsu.edu/~burkardt/data/obj/obj.html
"""
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 fabmetheus_utilities.geometry.geometry_tools import face
from fabmetheus_utilities.geometry.solids import triangle_mesh
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import archive
from fabmetheus_utilities import gcodec
from struct import unpack
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Nophead <http://hydraraptor.blogspot.com/>\nArt of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/21/04 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def addFacesGivenText( objText, triangleMesh ):
"Add faces given obj text."
lines = archive.getTextLines( objText )
for line in lines:
splitLine = line.split()
firstWord = gcodec.getFirstWord(splitLine)
if firstWord == 'v':
triangleMesh.vertexes.append( getVertexGivenLine(line) )
elif firstWord == 'f':
triangleMesh.faces.append( getFaceGivenLine( line, triangleMesh ) )
def getCarving(fileName=''):
"Get the triangle mesh for the obj file."
if fileName == '':
return None
objText = archive.getFileText(fileName, True, 'rb')
if objText == '':
return None
triangleMesh = triangle_mesh.TriangleMesh()
addFacesGivenText(objText, triangleMesh)
return triangleMesh
def getFaceGivenLine( line, triangleMesh ):
"Add face given line index and lines."
faceGivenLine = face.Face()
faceGivenLine.index = len( triangleMesh.faces )
splitLine = line.split()
for vertexStringIndex in xrange( 1, 4 ):
vertexString = splitLine[ vertexStringIndex ]
vertexStringWithSpaces = vertexString.replace('/', ' ')
vertexStringSplit = vertexStringWithSpaces.split()
vertexIndex = int( vertexStringSplit[0] ) - 1
faceGivenLine.vertexIndexes.append(vertexIndex)
return faceGivenLine
def getVertexGivenLine(line):
"Get vertex given obj vertex line."
splitLine = line.split()
return Vector3( float(splitLine[1]), float( splitLine[2] ), float( splitLine[3] ) )

View File

@ -0,0 +1,188 @@
"""
This page is in the table of contents.
The slc.py script is an import translator plugin to get a carving from an [http://rapid.lpt.fi/archives/rp-ml-1999/0713.html slc file].
An import plugin is a script in the interpret_plugins folder which has the function getCarving. It is meant to be run from the interpret tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name.
The getCarving function takes the file name of an slc file and returns the carving.
"""
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 fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
from fabmetheus_utilities import gcodec
from fabmetheus_utilities import svg_writer
from struct import unpack
import math
import sys
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Nophead <http://hydraraptor.blogspot.com/>\nArt of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/21/04 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getCarving(fileName=''):
"Get the triangle mesh for the slc file."
carving = SLCCarving()
carving.readFile(fileName)
return carving
def getLittleEndianFloatGivenFile( file ):
"Get little endian float given a file."
return unpack('<f', file.read(4) )[0]
def getLittleEndianUnsignedLongGivenFile( file ):
"Get little endian float given a file."
return unpack('<L', file.read(4) )[0]
def getPointsFromFile( numPoints, file ):
"Process the vertice points for a given boundary."
points = []
for pointIndex in xrange( numPoints ):
x = getLittleEndianFloatGivenFile( file )
y = getLittleEndianFloatGivenFile( file )
points.append( complex(x, y) )
return points
def readHeader( file ):
"Read the slc header."
while ord( file.read( 1 ) ) != 0x1A:
pass
class SampleTableEntry:
"Sample table entry."
def __init__( self, file ):
"Read in the sampling table section. It contains a table length (byte) and the table entries."
self.min_z_level = getLittleEndianFloatGivenFile( file )
self.layer_thickness = getLittleEndianFloatGivenFile( file )
self.beam_comp = getLittleEndianFloatGivenFile( file )
getLittleEndianFloatGivenFile( file )
def __repr__(self):
"Get the string representation of this sample table entry."
return '%s, %s, %s' % ( self.min_z_level, self.layer_thickness, self.beam_comp )
class SLCCarving:
"An slc carving."
def __init__(self):
"Add empty lists."
self.layerHeight = None
self.loopLayers = []
self.maximumZ = - 987654321.0
self.minimumZ = 987654321.0
def __repr__(self):
"Get the string representation of this carving."
return self.getCarvedSVG()
def addXML(self, depth, output):
"Add xml for this object."
xml_simple_writer.addXMLFromObjects(depth, self.loopLayers, output)
def getCarveBoundaryLayers(self):
"Get the boundary layers."
return self.loopLayers
def getCarveCornerMaximum(self):
"Get the corner maximum of the vertexes."
return self.cornerMaximum
def getCarveCornerMinimum(self):
"Get the corner minimum of the vertexes."
return self.cornerMinimum
def getCarvedSVG(self):
"Get the carved svg text."
if len(self.loopLayers) < 1:
return ''
decimalPlaces = max(0, 2 - int(math.floor(math.log10(self.layerHeight))))
self.svgWriter = svg_writer.SVGWriter(True, self.cornerMaximum, self.cornerMinimum, decimalPlaces, self.layerHeight)
return self.svgWriter.getReplacedSVGTemplate(self.fileName, self.loopLayers, 'basic')
def getCarveLayerHeight(self):
"Get the layer height."
return self.layerHeight
def getFabmetheusXML(self):
"Return the fabmetheus XML."
return None
def getInterpretationSuffix(self):
"Return the suffix for a carving."
return 'svg'
def processContourLayers( self, file ):
"Process a contour layer at a time until the top of the part."
while True:
minLayer = getLittleEndianFloatGivenFile( file )
numContours = getLittleEndianUnsignedLongGivenFile( file )
if numContours == 0xFFFFFFFF:
return
loopLayer = euclidean.LoopLayer( minLayer )
self.loopLayers.append( loopLayer )
for contourIndex in xrange( numContours ):
numPoints = getLittleEndianUnsignedLongGivenFile( file )
numGaps = getLittleEndianUnsignedLongGivenFile( file )
if numPoints > 2:
loopLayer.loops.append( getPointsFromFile( numPoints, file ) )
def readFile( self, fileName ):
"Read SLC and store the layers."
self.fileName = fileName
pslcfile = open( fileName, 'rb')
readHeader( pslcfile )
pslcfile.read( 256 ) #Go past the 256 byte 3D Reserved Section.
self.readTableEntry( pslcfile )
self.processContourLayers( pslcfile )
pslcfile.close()
self.cornerMaximum = Vector3(-987654321.0, -987654321.0, self.maximumZ)
self.cornerMinimum = Vector3(987654321.0, 987654321.0, self.minimumZ)
for loopLayer in self.loopLayers:
for loop in loopLayer.loops:
for point in loop:
pointVector3 = Vector3(point.real, point.imag, loopLayer.z)
self.cornerMaximum.maximize(pointVector3)
self.cornerMinimum.minimize(pointVector3)
halfLayerThickness = 0.5 * self.layerHeight
self.cornerMaximum.z += halfLayerThickness
self.cornerMinimum.z -= halfLayerThickness
def readTableEntry( self, file ):
"Read in the sampling table section. It contains a table length (byte) and the table entries."
tableEntrySize = ord( file.read( 1 ) )
if tableEntrySize == 0:
print("Sampling table size is zero!")
exit()
for index in xrange( tableEntrySize ):
sampleTableEntry = SampleTableEntry( file )
self.layerHeight = sampleTableEntry.layerHeight
def setCarveImportRadius( self, importRadius ):
"Set the import radius."
pass
def setCarveIsCorrectMesh( self, isCorrectMesh ):
"Set the is correct mesh flag."
pass
def setCarveLayerHeight( self, layerHeight ):
"Set the layer height."
pass
def main():
"Display the inset dialog."
if len(sys.argv) > 1:
getCarving(' '.join(sys.argv[1 :]))
if __name__ == "__main__":
main()

View File

@ -0,0 +1,111 @@
"""
This page is in the table of contents.
The stl.py script is an import translator plugin to get a carving from an stl file.
An import plugin is a script in the interpret_plugins folder which has the function getCarving. It is meant to be run from the interpret tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name.
The getCarving function takes the file name of an stl file and returns the carving.
STL is an inferior triangle surface format, described at:
http://en.wikipedia.org/wiki/STL_(file_format)
A good triangle surface format is the GNU Triangulated Surface format which is described at:
http://gts.sourceforge.net/reference/gts-surfaces.html#GTS-SURFACE-WRITE
"""
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 fabmetheus_utilities.geometry.geometry_tools import face
from fabmetheus_utilities.geometry.solids import triangle_mesh
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import archive
from fabmetheus_utilities import gcodec
from struct import unpack
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Nophead <http://hydraraptor.blogspot.com/>\nArt of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/21/04 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def addFacesGivenBinary( stlData, triangleMesh, vertexIndexTable ):
"Add faces given stl binary."
numberOfVertexes = ( len( stlData ) - 84 ) / 50
vertexes = []
for vertexIndex in xrange( numberOfVertexes ):
byteIndex = 84 + vertexIndex * 50
vertexes.append( getVertexGivenBinary( byteIndex + 12, stlData ) )
vertexes.append( getVertexGivenBinary( byteIndex + 24, stlData ) )
vertexes.append( getVertexGivenBinary( byteIndex + 36, stlData ) )
addFacesGivenVertexes( triangleMesh, vertexIndexTable, vertexes )
def addFacesGivenText( stlText, triangleMesh, vertexIndexTable ):
"Add faces given stl text."
lines = archive.getTextLines( stlText )
vertexes = []
for line in lines:
if line.find('vertex') != - 1:
vertexes.append( getVertexGivenLine(line) )
addFacesGivenVertexes( triangleMesh, vertexIndexTable, vertexes )
def addFacesGivenVertexes( triangleMesh, vertexIndexTable, vertexes ):
"Add faces given stl text."
for vertexIndex in xrange( 0, len(vertexes), 3 ):
triangleMesh.faces.append( getFaceGivenLines( triangleMesh, vertexIndex, vertexIndexTable, vertexes ) )
def getCarving(fileName=''):
"Get the triangle mesh for the stl file."
if fileName == '':
return None
stlData = archive.getFileText(fileName, True, 'rb')
if stlData == '':
return None
triangleMesh = triangle_mesh.TriangleMesh()
vertexIndexTable = {}
numberOfVertexStrings = stlData.count('vertex')
requiredVertexStringsForText = max( 2, len( stlData ) / 8000 )
if numberOfVertexStrings > requiredVertexStringsForText:
addFacesGivenText( stlData, triangleMesh, vertexIndexTable )
else:
# A binary stl should never start with the word "solid". Because this error is common the file is been parsed as binary regardless.
addFacesGivenBinary( stlData, triangleMesh, vertexIndexTable )
return triangleMesh
def getFaceGivenLines( triangleMesh, vertexStartIndex, vertexIndexTable, vertexes ):
"Add face given line index and lines."
faceGivenLines = face.Face()
faceGivenLines.index = len( triangleMesh.faces )
for vertexIndex in xrange( vertexStartIndex, vertexStartIndex + 3 ):
vertex = vertexes[vertexIndex]
vertexUniqueIndex = len( vertexIndexTable )
if str(vertex) in vertexIndexTable:
vertexUniqueIndex = vertexIndexTable[ str(vertex) ]
else:
vertexIndexTable[ str(vertex) ] = vertexUniqueIndex
triangleMesh.vertexes.append(vertex)
faceGivenLines.vertexIndexes.append( vertexUniqueIndex )
return faceGivenLines
def getFloat(floatString):
"Get the float, replacing commas if necessary because an inferior program is using a comma instead of a point for the decimal point."
try:
return float(floatString)
except:
return float( floatString.replace(',', '.') )
def getFloatGivenBinary( byteIndex, stlData ):
"Get vertex given stl vertex line."
return unpack('f', stlData[ byteIndex : byteIndex + 4 ] )[0]
def getVertexGivenBinary( byteIndex, stlData ):
"Get vertex given stl vertex line."
return Vector3( getFloatGivenBinary( byteIndex, stlData ), getFloatGivenBinary( byteIndex + 4, stlData ), getFloatGivenBinary( byteIndex + 8, stlData ) )
def getVertexGivenLine(line):
"Get vertex given stl vertex line."
splitLine = line.split()
return Vector3( getFloat(splitLine[1]), getFloat( splitLine[2] ), getFloat( splitLine[3] ) )

View File

@ -0,0 +1,109 @@
"""
This page is in the table of contents.
The svg.py script is an import translator plugin to get a carving from an svg file. This script will read an svg file made by skeinforge or by inkscape.
An example inkscape svg file is inkscape_star.svg in the models folder.
An import plugin is a script in the interpret_plugins folder which has the function getCarving. It is meant to be run from the interpret tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name.
The getCarving function takes the file name of an svg file and returns the carving.
"""
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 fabmetheus_utilities.svg_reader import SVGReader
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import archive
from fabmetheus_utilities import euclidean
from fabmetheus_utilities import gcodec
from fabmetheus_utilities import svg_writer
from fabmetheus_utilities import xml_simple_writer
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Nophead <http://hydraraptor.blogspot.com/>\nArt of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/21/04 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getCarving(fileName=''):
'Get the triangle mesh for the gts file.'
carving = SVGCarving()
carving.parseSVG(fileName, archive.getFileText(fileName))
return carving
class SVGCarving:
'An svg carving.'
def __init__(self):
'Add empty lists.'
self.layerHeight = 1.0
self.maximumZ = - 987654321.0
self.minimumZ = 987654321.0
self.svgReader = SVGReader()
def __repr__(self):
'Get the string representation of this carving.'
return self.getCarvedSVG()
def addXML(self, depth, output):
'Add xml for this object.'
xml_simple_writer.addXMLFromObjects(depth, self.svgReader.loopLayers, output)
def getCarveBoundaryLayers(self):
'Get the boundary layers.'
return self.svgReader.loopLayers
def getCarveCornerMaximum(self):
'Get the corner maximum of the vertexes.'
return self.cornerMaximum
def getCarveCornerMinimum(self):
'Get the corner minimum of the vertexes.'
return self.cornerMinimum
def getCarvedSVG(self):
'Get the carved svg text.'
return svg_writer.getSVGByLoopLayers(True, self, self.svgReader.loopLayers)
def getCarveLayerHeight(self):
'Get the layer height.'
return self.layerHeight
def getFabmetheusXML(self):
'Return the fabmetheus XML.'
return None
def getInterpretationSuffix(self):
'Return the suffix for a carving.'
return 'svg'
def parseSVG(self, fileName, svgText):
'Parse SVG text and store the layers.'
if svgText == '':
return
self.fileName = fileName
self.svgReader.parseSVG(fileName, svgText)
self.layerHeight = euclidean.getFloatDefaultByDictionary(
self.layerHeight, self.svgReader.sliceDictionary, 'layerHeight')
self.cornerMaximum = Vector3(-987654321.0, -987654321.0, self.maximumZ)
self.cornerMinimum = Vector3(987654321.0, 987654321.0, self.minimumZ)
svg_writer.setSVGCarvingCorners(
self.cornerMaximum, self.cornerMinimum, self.layerHeight, self.svgReader.loopLayers)
def setCarveImportRadius(self, importRadius):
'Set the import radius.'
pass
def setCarveIsCorrectMesh(self, isCorrectMesh):
'Set the is correct mesh flag.'
pass
def setCarveLayerHeight(self, layerHeight):
'Set the layer height.'
self.layerHeight = layerHeight

View File

@ -0,0 +1,86 @@
"""
Prepare is a script to remove the generated files, run wikifier, and finally zip the package.
"""
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 fabmetheus_utilities import archive
from fabmetheus_utilities.fabmetheus_tools import wikifier
import os
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__date__ = '$Date: 2008/21/04 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def prepareWikify():
'Remove generated files, then wikify the file comments.'
removeGeneratedFiles()
wikifier.main()
removeZip()
def removeCSVFile(csvFilePath):
'Remove csv file.'
if 'alterations' in csvFilePath and 'example_' not in csvFilePath:
os.remove(csvFilePath)
print('removeGeneratedFiles deleted ' + csvFilePath)
def removeGcodeFile(gcodeFilePath):
'Remove gcode file.'
if 'alterations' not in gcodeFilePath:
os.remove(gcodeFilePath)
print('removeGeneratedFiles deleted ' + gcodeFilePath)
return
if 'example_' not in gcodeFilePath:
os.remove(gcodeFilePath)
print('removeGeneratedFiles deleted ' + gcodeFilePath)
def removeGeneratedFiles():
'Remove generated files.'
csvFilePaths = archive.getFilesWithFileTypesWithoutWordsRecursively(['csv'])
for csvFilePath in csvFilePaths:
removeCSVFile(csvFilePath)
gcodeFilePaths = archive.getFilesWithFileTypesWithoutWordsRecursively(['gcode'])
for gcodeFilePath in gcodeFilePaths:
removeGcodeFile(gcodeFilePath)
svgFilePaths = archive.getFilesWithFileTypesWithoutWordsRecursively(['svg'])
for svgFilePath in svgFilePaths:
removeSVGFile(svgFilePath)
xmlFilePaths = archive.getFilesWithFileTypesWithoutWordsRecursively(['xml'])
for xmlFilePath in xmlFilePaths:
removeXMLFile(xmlFilePath)
archive.removeBackupFilesByTypes(['gcode', 'svg', 'xml'])
def removeSVGFile(svgFilePath):
'Remove svg file.'
if archive.getEndsWithList(svgFilePath, ['_bottom.svg', '_carve.svg', '_chop.svg', '_cleave.svg', '_scale.svg', '_vectorwrite.svg']):
os.remove(svgFilePath)
print('removeGeneratedFiles deleted ' + svgFilePath)
def removeXMLFile(xmlFilePath):
'Remove xml file.'
if archive.getEndsWithList(xmlFilePath, ['_interpret.xml']):
os.remove(xmlFilePath)
print('removeGeneratedFiles deleted ' + xmlFilePath)
def removeZip():
'Remove the zip file, then generate a new one.zip -r reprap_python_beanshell * -x \*.pyc \*~'
zipName = 'reprap_python_beanshell'
zipNameExtension = zipName + '.zip'
if zipNameExtension in os.listdir(os.getcwd()):
os.remove(zipNameExtension)
shellCommand = 'zip -r %s * -x \*.pyc \*~' % zipName
if os.system(shellCommand) != 0:
print('Failed to execute the following command in removeZip in prepare.')
print(shellCommand)
def main():
'Run main function.'
prepareWikify()
if __name__ == "__main__":
main()

View File

@ -0,0 +1,215 @@
"""
Wikifier is a script to add spaces to the pydoc files and move them to the documentation folder.
"""
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 fabmetheus_utilities import archive
from fabmetheus_utilities import gcodec
from fabmetheus_utilities import settings
import cStringIO
import os
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__date__ = '$Date: 2008/21/04 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
globalWikiLinkStart = '[<a href='
def addToHeadings(headingLineTable, headings, line):
'Add the line to the headings.'
for depth in xrange(4, -1, -1):
equalSymbolLength = depth + 2
if line[: equalSymbolLength] == '=' * equalSymbolLength:
headings.append(Heading(depth).getFromLine(headingLineTable, line))
return
def getLinkLine(line):
'Get the link line with the wiki style link converted into a hypertext link.'
linkStartIndex = line.find(globalWikiLinkStart)
squareEndBracketIndex = line.find(']', linkStartIndex)
greaterThanIndex = line.find('>', linkStartIndex, squareEndBracketIndex)
greaterThanIndexPlusOne = greaterThanIndex + 1
closeATagIndex = line.find('</a>', greaterThanIndexPlusOne, squareEndBracketIndex)
linkText = line[closeATagIndex + len('</a>') + 1: squareEndBracketIndex]
linkLine = line[: linkStartIndex] + line[linkStartIndex + 1: greaterThanIndexPlusOne] + linkText + '</a>' + line[squareEndBracketIndex + 1 :]
return linkLine
def getNavigationHypertext(fileText, transferredFileNameIndex, transferredFileNames):
'Get the hypertext help with navigation lines.'
helpTextEnd = fileText.find('</p>')
helpTextStart = fileText.find('<p>')
helpText = fileText[helpTextStart : helpTextEnd]
lines = archive.getTextLines(helpText)
headings = []
headingLineTable = {}
for line in lines:
addToHeadings(headingLineTable, headings, line)
headingsToBeenAdded = True
output = cStringIO.StringIO()
for line in lines:
if line[: 2] == '==':
if headingsToBeenAdded:
output.write('<br />\n')
for heading in headings:
heading.addToOutput(output)
output.write('<br />\n')
headingsToBeenAdded = False
if line in headingLineTable:
line = headingLineTable[line]
if '&lt;a href=' in line:
line = line.replace('&lt;', '<').replace('&gt;', '>')
while globalWikiLinkStart in line and ']' in line:
line = getLinkLine(line)
output.write(line + '\n')
helpText = output.getvalue()
previousFileName = 'contents.html'
previousIndex = transferredFileNameIndex - 1
if previousIndex >= 0:
previousFileName = transferredFileNames[previousIndex]
previousLinkText = '<a href="%s">Previous</a>' % previousFileName
nextLinkText = getNextLinkText(transferredFileNames, transferredFileNameIndex + 1)
navigationLine = getNavigationLine('<a href="contents.html">Contents</a>', previousLinkText, nextLinkText)
helpText = navigationLine + helpText + '<br />\n<br />\n' + navigationLine + '<hr>\n'
return fileText[: helpTextStart] + helpText + fileText[helpTextEnd :]
def getNavigationLine(contentsLinkText, previousLinkText, nextLinkText):
'Get the wrapped pydoc hypertext help.'
return '<p>\n%s / %s / %s\n</p>\n' % (previousLinkText, nextLinkText, contentsLinkText)
def getNextLinkText(hypertextFiles, nextIndex):
'Get the next link text.'
nextFileName = 'contents.html'
if nextIndex < len(hypertextFiles):
nextFileName = hypertextFiles[nextIndex]
return '<a href="%s">Next</a>' % nextFileName
def getWrappedHypertext(fileText, hypertextFileIndex, hypertextFiles):
'Get the wrapped pydoc hypertext help.'
helpTextEnd = fileText.find('</p>')
if helpTextEnd < 0:
print('Failed to find the helpTextEnd in getWrappedHypertext in docwrap.')
helpTextStart = fileText.find('<p>')
if helpTextStart < 0:
print('Failed to find the helpTextStart in getWrappedHypertext in docwrap.')
helpText = fileText[helpTextStart : helpTextEnd]
helpText = helpText.replace('&nbsp;', ' ')
return fileText[: helpTextStart] + helpText + fileText[helpTextEnd :]
def readWriteDeleteHypertextHelp(documentDirectoryPath, hypertextFileIndex, hypertextFiles, transferredFileNames):
'Read the pydoc hypertext help documents, write them in the documentation folder then delete the originals.'
fileName = os.path.basename(hypertextFiles[hypertextFileIndex])
print('readWriteDeleteHypertextHelp ' + fileName)
filePath = os.path.join(documentDirectoryPath, fileName)
fileText = archive.getFileText(fileName)
fileText = getWrappedHypertext(fileText, hypertextFileIndex, hypertextFiles)
if fileText.find('This page is in the table of contents.') > - 1:
fileText = fileText.replace('This page is in the table of contents.', '')
transferredFileNames.append(fileName)
archive.writeFileText(filePath, fileText)
os.remove(fileName)
def readWriteNavigationHelp(documentDirectoryPath, transferredFileNameIndex, transferredFileNames):
'Read the hypertext help documents, and add the navigation lines to them.'
fileName = os.path.basename(transferredFileNames[transferredFileNameIndex])
print('readWriteNavigationHelp ' + fileName)
filePath = os.path.join(documentDirectoryPath, fileName)
fileText = archive.getFileText(filePath)
fileText = getNavigationHypertext(fileText, transferredFileNameIndex, transferredFileNames)
archive.writeFileText(filePath, fileText)
def removeFilesInDirectory(directoryPath):
'Remove all the files in a directory.'
fileNames = os.listdir(directoryPath)
for fileName in fileNames:
filePath = os.path.join(directoryPath, fileName)
os.remove(filePath)
def writeContentsFile(documentDirectoryPath, hypertextFiles):
'Write the contents file.'
output = cStringIO.StringIO()
output.write('<html>\n <head>\n <title>Contents</title>\n </head>\n <body>\n')
navigationLine = getNavigationLine('Contents', 'Previous', getNextLinkText(hypertextFiles, 0))
output.write(navigationLine)
for hypertextFile in hypertextFiles:
writeContentsLine(hypertextFile, output)
output.write(navigationLine)
output.write(' </body>\n</html>\n')
filePath = os.path.join( documentDirectoryPath, 'contents.html')
archive.writeFileText(filePath, output.getvalue())
def writeContentsLine(hypertextFile, output):
'Write a line of the contents file.'
summarizedFileName = hypertextFile[: hypertextFile.rfind('.')]
numberOfDots = summarizedFileName.count('.')
prefixSpaces = '&nbsp;&nbsp;' * numberOfDots
if numberOfDots > 0:
summarizedFileName = summarizedFileName[summarizedFileName.rfind('.') + 1 :]
capitalizedSummarizedFileName = settings.getEachWordCapitalized(summarizedFileName)
output.write('%s<a href="%s">%s</a><br>\n' % (prefixSpaces, hypertextFile, capitalizedSummarizedFileName))
def writeHypertext():
'Run pydoc, then read, write and delete each of the files.'
shellCommand = 'pydoc -w ./'
commandResult = os.system(shellCommand)
if commandResult != 0:
print('Failed to execute the following command in writeHypertext in docwrap.')
print(shellCommand)
hypertextFiles = archive.getFilesWithFileTypeWithoutWords('html')
if len( hypertextFiles ) <= 0:
print('Failed to find any help files in writeHypertext in docwrap.')
return
documentDirectoryPath = archive.getAbsoluteFolderPath( hypertextFiles[0], 'documentation')
removeFilesInDirectory(documentDirectoryPath)
sortedReplaceFiles = []
for hypertextFile in hypertextFiles:
sortedReplaceFiles.append(hypertextFile.replace('.html', '. html'))
sortedReplaceFiles.sort()
hypertextFiles = []
for sortedReplaceFile in sortedReplaceFiles:
hypertextFiles.append(sortedReplaceFile.replace('. html', '.html'))
transferredFileNames = []
for hypertextFileIndex in xrange(len(hypertextFiles)):
readWriteDeleteHypertextHelp(documentDirectoryPath, hypertextFileIndex, hypertextFiles, transferredFileNames)
for transferredFileNameIndex in xrange(len(transferredFileNames)):
readWriteNavigationHelp(documentDirectoryPath, transferredFileNameIndex, transferredFileNames)
writeContentsFile(documentDirectoryPath, transferredFileNames)
print('%s files were wrapped.' % len(transferredFileNames))
class Heading:
'A class to hold the heading and subheadings.'
def __init__(self, depth=0):
'Initialize.'
self.depth = depth
def addToOutput(self, output):
'Add to the output.'
line = '&nbsp;&nbsp;' * self.depth + '<a href="#%s">%s</a><br />\n' % (self.name, self.name)
output.write(line)
def getFromLine(self, headingLineTable, line):
'Get the heading from a line.'
heading = 'h%s' % (self.depth + 2)
nextLine = '\n<hr>\n'
if self.depth > 0:
nextLine = '\n'
self.name = line.replace('=', '').replace('<br>', '')
name = self.name
headingLine = '<a name="%s" id="%s"></a><%s>%s</%s>%s' % (name, name, heading, name, heading, nextLine)
headingLineTable[line] = headingLine
return self
def main():
'Display the craft dialog.'
writeHypertext()
if __name__ == '__main__':
main()

View File

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
<defs>
<font horiz-adv-x="1025"><font-face font-family="Gentium Basic" units-per-em="2048" panose-1="2 0 5 3 6 0 0 2 0 4" ascent="1790" descent="-580" alphabetic="0"/>
<missing-glyph horiz-adv-x="1400" d="M150 50H1250V1750H150V50ZM100 1800H1300V0H100V1800Z"/>
<glyph unicode=" " glyph-name="space" horiz-adv-x="451"/>
<glyph unicode="!" glyph-name="exclam" horiz-adv-x="557" d="M390 123Q390 88 380 59T351 7T307 -27T252 -40Q198 -40 175 -11T152 72Q152 106 163 135T193 187T237 223T291 236Q341 236 365 207T390 123ZM326 389Q307 375 292 367T252 350L223 371L176 1398Q214 1423 256 1444T332 1480L375 1455L326 389Z"/>
<glyph unicode="&quot;" glyph-name="quotedbl" horiz-adv-x="870" d="M309 834Q301 830 290 826T265 819T239 815T213 813L160 1426Q171 1432 192 1440T235 1457T278 1471T309 1480L352 1459L309 834ZM668 834Q660 830 649 826T624 819T597 815T571 813L518 1426Q529 1432 549 1440T593 1457T636 1471T668 1480L711 1459L668 834Z"/>
<glyph unicode="#" glyph-name="numbersign" horiz-adv-x="961" d="M399 590H588L661 846H473L399 590ZM806 961H957L982 936Q977 914 968 889T951 846H773L700 590H851L873 563Q868 543 861 518T845 475H667L580 170Q562 156 538 146T488 127L462 147L555 475H366L279 170Q261 156 238 146T189 127L163 147L256 475H109L87 500Q92 520 100 545T117 590H289L362 846H218L193 870Q198 892 207 916T224 961H395L478 1251Q500 1267 522 1275T566 1292L595 1270L506 961H694L777 1251Q799 1267 821 1275T867 1292L894 1270L806 961Z"/>
<glyph unicode="$" glyph-name="dollar" horiz-adv-x="961" d="M225 958Q225 920 241 892T285 840T351 799T432 763V1115Q374 1113 335 1099T271 1062T236 1013T225 958ZM727 324Q727 371 711 407T667 470T602 519T522 558V113Q562 118 599 135T664 180T710 244T727 324ZM522 -143Q513 -151 507 -155T494 -163T479 -168T459 -174L432 -152V-10Q384 -9 343 -3T263 16T185 48T102 96Q95 101 92 129T88 195T91 272T102 340L145 336Q193 233 267 180T432 114V593Q370 616 311 641T204 701T127 787T98 911Q98 953 114 1001T170 1092T273 1167T432 1210V1356Q446 1364 452 1368T463 1374T474 1378T496 1384L522 1362V1214Q568 1213 611 1207T693 1188T761 1160T811 1124Q817 1117 810 1099T788 1059T757 1016T727 983L688 991Q648 1037 606 1065T522 1105V728Q584 704 645 676T754 608T832 512T862 377Q862 317 841 255T776 140T670 48T522 -4V-143Z"/>
<glyph unicode="%" glyph-name="percent" horiz-adv-x="1413" d="M1194 289Q1194 359 1182 411T1148 497T1100 548T1044 565Q1022 565 1001 551T962 507T935 435T924 332Q924 260 935 207T965 120T1012 68T1071 51Q1096 51 1118 66T1157 111T1184 185T1194 289ZM1337 309Q1337 240 1313 179T1249 71T1156 -3T1044 -30Q986 -30 937 -3T853 70T798 178T778 309Q778 378 802 439T867 547T961 619T1071 646Q1131 646 1180 620T1264 548T1318 441T1337 309ZM358 17Q349 10 332 3T294 -11T255 -24T223 -34L199 1L1055 1188Q1083 1205 1120 1217T1188 1237L1217 1204L358 17ZM492 877Q492 948 480 1000T446 1086T398 1136T342 1153Q320 1153 299 1139T260 1095T232 1023T221 920Q221 848 232 795T262 708T309 656T369 639Q393 639 415 654T455 699T482 773T492 877ZM637 897Q637 828 613 767T549 659T455 585T342 558Q283 558 235 585T151 658T96 766T76 897Q76 966 100 1027T164 1134T258 1207T369 1234Q429 1234 478 1208T562 1135T617 1028T637 897Z"/>
<glyph unicode="&amp;" glyph-name="ampersand" horiz-adv-x="1366" d="M444 1130Q444 1072 461 1009T508 879Q576 918 619 956T686 1031T720 1103T729 1171Q729 1206 720 1237T694 1291T650 1329T588 1343Q552 1343 525 1325T480 1276T453 1208T444 1130ZM528 84Q613 84 684 111T812 183Q762 235 709 295T606 422T509 560T425 703Q374 659 339 618T283 537T252 456T242 373Q242 303 266 249T330 159T421 103T528 84ZM1346 748Q1321 717 1297 697T1247 659Q1229 668 1206 675T1160 687T1114 696T1075 700Q1086 682 1092 667T1102 633T1107 594T1108 543Q1108 472 1077 393T989 239Q1058 173 1116 127Q1154 95 1198 85T1300 88L1309 45Q1222 7 1163 -11T1081 -30Q1059 -30 1004 10T877 119Q799 53 703 12T498 -30Q405 -30 328 -6T196 67T111 186T80 352Q80 408 100 466T158 581T252 693T380 799Q351 870 334 938T317 1073Q317 1154 344 1223T416 1342T519 1421T641 1450Q705 1450 748 1430T818 1375T856 1297T868 1204Q868 1099 803 1008T618 842Q599 830 582 819T549 796Q584 731 627 666T719 538T819 416T922 305Q954 355 970 406T987 502Q987 544 973 581T937 645T885 689T827 705L805 733Q816 746 837 761T879 784H1323L1346 748Z"/>
<glyph unicode="'" glyph-name="quotesingle" horiz-adv-x="512" d="M309 834Q301 830 290 826T265 819T239 815T213 813L160 1426Q171 1432 192 1440T235 1457T278 1471T309 1480L352 1459L309 834Z"/>
<glyph unicode="(" glyph-name="parenleft" horiz-adv-x="649" d="M578 -315Q465 -263 380 -170T237 47T150 317T121 623Q121 787 153 940T246 1222T390 1448T578 1600L610 1555Q545 1501 487 1413T386 1209T317 952T291 651Q291 505 312 367T374 108T474 -110T610 -270L578 -315Z"/>
<glyph unicode=")" glyph-name="parenright" horiz-adv-x="649" d="M530 664Q530 499 497 346T404 63T260 -163T72 -315L41 -270Q105 -216 163 -128T265 76T334 333T360 635Q360 780 339 918T278 1178T178 1396T41 1555L72 1600Q185 1549 270 1456T414 1239T501 970T530 664Z"/>
<glyph unicode="*" glyph-name="asterisk" horiz-adv-x="915" d="M489 1188L750 1384Q771 1370 799 1352T844 1317L846 1276L520 1135L821 1006Q819 981 818 947T809 891L772 868L490 1079L528 756Q517 750 504 743T477 729T449 717T424 709L385 727L427 1079L166 883Q155 890 142 898T115 916T91 933T72 950L70 993L396 1133L94 1262Q95 1274 95 1289T97 1321T100 1351T106 1376L141 1399L426 1187L387 1513Q398 1519 412 1526T440 1540T469 1552T494 1561L530 1540L489 1188Z"/>
<glyph unicode="+" glyph-name="plus" horiz-adv-x="840" d="M477 236Q457 226 433 218T387 205L362 227V507H86L61 531Q66 551 74 574T92 617H362V891Q381 897 406 906T453 920L477 897V617H754L778 593Q773 573 765 549T748 507H477V236Z"/>
<glyph unicode="," glyph-name="comma" horiz-adv-x="469" d="M390 70Q390 31 375 -18T330 -118T260 -217T166 -305L121 -270Q149 -238 168 -208T201 -146T220 -79T226 0Q226 44 197 70T104 92L88 139Q97 151 126 166T189 196T257 220T309 229Q357 206 373 167T390 70Z"/>
<glyph unicode="-" glyph-name="hyphen" horiz-adv-x="690" d="M629 546Q623 524 615 498T598 455H86L61 479Q66 501 74 525T92 570H604L629 546Z"/>
<glyph unicode="." glyph-name="period" horiz-adv-x="469" d="M369 123Q369 88 359 59T330 7T286 -27T231 -40Q177 -40 154 -11T131 72Q131 106 142 135T172 187T216 223T270 236Q320 236 344 207T369 123Z"/>
<glyph unicode="/" glyph-name="slash" horiz-adv-x="961" d="M211 -284Q201 -291 185 -298T151 -312T116 -325T86 -335L49 -310L750 1551Q777 1568 810 1580T872 1600L909 1578L211 -284Z"/>
<glyph unicode="0" glyph-name="zero" horiz-adv-x="961" d="M721 565Q721 702 700 806T642 979T559 1084T461 1120Q410 1120 369 1093T300 1008T256 860T240 645Q240 508 260 403T314 228T396 121T500 85Q552 85 593 112T662 198T706 347T721 565ZM885 602Q885 473 855 358T769 157T635 21T461 -30Q366 -30 294 20T174 157T101 358T76 602Q76 731 107 846T193 1047T327 1184T500 1235Q595 1235 667 1185T787 1049T860 848T885 602Z"/>
<glyph unicode="1" glyph-name="one" horiz-adv-x="961" d="M170 0V53Q248 62 300 73T383 97T427 120T440 143V940Q440 980 437 1002T422 1038Q416 1044 399 1048T351 1051T273 1044T160 1024L139 1075Q181 1087 238 1108T355 1153T467 1202T553 1245L590 1210V143Q590 133 601 122T640 98T716 74T840 53V0H170Z"/>
<glyph unicode="2" glyph-name="two" horiz-adv-x="961" d="M821 0H127L98 74Q217 212 305 318T457 505T561 648T626 758T659 846T668 924Q668 970 657 1010T622 1081T560 1128T467 1145Q426 1145 392 1125T333 1075T294 1007T280 934Q263 925 250 917T222 903T192 892T152 885L125 915Q125 967 160 1023T253 1127T384 1204T535 1235Q595 1235 647 1218T739 1167T800 1079T823 952Q823 907 810 862T770 765T699 650T593 511T450 337T266 121H684Q701 121 714 128T738 148T756 177T769 210Q781 249 786 299L836 287L821 0Z"/>
<glyph unicode="3" glyph-name="three" horiz-adv-x="961" d="M825 381Q825 297 798 222T718 91T587 3T406 -30Q367 -30 326 -23T243 1T157 47T68 119L102 186Q147 152 184 130T255 95T322 76T393 70Q454 70 504 89T590 145T645 232T665 348Q665 423 642 475T582 561T500 608T410 623H389Q383 623 378 622T365 620T342 616L328 676Q420 701 475 734T559 802T598 873T608 940Q608 974 598 1010T565 1077T508 1126T426 1145Q386 1145 355 1131T302 1093T273 1038T270 973Q236 956 205 947T131 934L104 965Q104 1007 135 1054T218 1142T338 1208T483 1235Q558 1235 611 1211T700 1148T751 1059T768 958Q768 920 753 883T711 812T644 749T555 698Q612 691 661 664T747 594T804 497T825 381Z"/>
<glyph unicode="4" glyph-name="four" horiz-adv-x="961" d="M585 1040L213 460H585V1040ZM874 430Q853 398 836 383T795 350H725V104Q725 96 731 90T753 76T796 60T864 43V0H375V43Q440 54 481 63T545 81T576 98T585 115V350H88L55 383L551 1174Q593 1192 626 1211T688 1245L725 1210V460H848L874 430Z"/>
<glyph unicode="5" glyph-name="five" horiz-adv-x="961" d="M836 410Q836 323 810 244T733 103T608 6T440 -30Q347 -30 256 4T78 119L113 186Q166 149 209 127T290 92T358 75T418 70Q483 70 531 95T612 161T660 256T676 367Q676 435 660 491T610 586T525 648T403 670Q382 670 355 665T299 650T240 625T184 590L137 623Q143 659 151 706T167 807T184 915T200 1023T212 1123T219 1205H655Q684 1205 706 1207T744 1214Q762 1218 776 1223L805 1194Q794 1178 780 1161T750 1127T721 1098T694 1075H307Q305 1046 300 1001T289 908T275 818T264 754Q304 766 358 773T463 780Q554 780 623 750T740 668T811 550T836 410Z"/>
<glyph unicode="6" glyph-name="six" horiz-adv-x="961" d="M500 660Q449 660 388 632T270 536Q270 417 289 331T345 187T432 103T545 75Q599 75 634 100T691 165T722 253T731 348Q731 442 709 502T652 598T578 646T500 660ZM881 412Q881 365 871 314T839 214T786 121T713 44T619 -10T506 -30Q424 -30 352 6T226 109T141 267T110 473Q110 614 150 743T275 977T486 1152T788 1249L807 1190Q689 1163 598 1109T441 982T335 825T279 650Q314 684 350 707T421 744T487 764T541 770Q622 770 685 745T791 674T858 561T881 412Z"/>
<glyph unicode="7" glyph-name="seven" horiz-adv-x="961" d="M885 1167Q839 1059 792 949T701 732T615 524T540 334T477 171T432 44Q397 19 359 1T270 -30L229 3Q316 156 385 295T513 564T625 821T733 1075H279Q264 1075 250 1074T221 1061T189 1018T150 930L104 948Q107 971 112 1006T123 1077T135 1148T147 1205H852L885 1167Z"/>
<glyph unicode="8" glyph-name="eight" horiz-adv-x="961" d="M275 950Q275 906 296 874T354 816T436 769T533 725Q606 778 636 831T667 938Q667 990 652 1028T608 1092T540 1129T451 1141Q406 1141 373 1124T319 1080T286 1018T275 950ZM707 309Q707 375 684 422T622 503T535 562T434 610Q386 575 352 542T297 473T265 399T254 315Q254 263 271 218T320 140T397 89T500 70Q555 70 594 91T659 146T695 222T707 309ZM867 352Q867 274 834 205T744 84T612 1T451 -30Q365 -30 299 -4T187 66T118 166T94 285Q94 339 112 389T165 485T248 571T357 644Q314 664 276 688T208 744T162 817T145 915Q145 984 172 1043T248 1144T361 1211T502 1235Q577 1235 635 1215T734 1160T796 1076T817 969Q817 927 802 890T759 818T695 751T613 688Q663 664 709 635T790 565T846 473T867 352Z"/>
<glyph unicode="9" glyph-name="nine" horiz-adv-x="961" d="M481 555Q550 555 608 593T709 694Q707 819 684 903T625 1037T548 1109T465 1130Q414 1130 374 1110T307 1053T265 966T250 856Q250 764 272 706T329 614T403 568T481 555ZM870 731Q870 579 829 449T704 218T492 50T190 -43L172 16Q305 49 401 103T562 228T660 388T705 577Q678 547 646 523T580 481T509 455T438 445Q358 445 296 471T190 545T123 656T100 795Q100 845 113 897T152 999T214 1091T296 1166T394 1216T506 1235Q576 1235 641 1206T758 1116T839 959T870 731Z"/>
<glyph unicode=":" glyph-name="colon" horiz-adv-x="469" d="M369 123Q369 88 359 59T330 7T286 -27T231 -40Q177 -40 154 -11T131 72Q131 106 142 135T172 187T216 223T270 236Q320 236 344 207T369 123ZM369 848Q369 813 359 784T330 732T286 698T231 685Q177 685 154 714T131 797Q131 831 142 860T172 912T216 948T270 961Q320 961 344 932T369 848Z"/>
<glyph unicode=";" glyph-name="semicolon" horiz-adv-x="469" d="M390 70Q390 31 375 -18T330 -118T260 -217T166 -305L121 -270Q149 -238 168 -208T201 -146T220 -79T226 0Q226 44 197 70T104 92L88 139Q97 151 126 166T189 196T257 220T309 229Q357 206 373 167T390 70ZM369 848Q369 813 359 784T330 732T286 698T231 685Q177 685 154 714T131 797Q131 831 142 860T172 912T216 948T270 961Q320 961 344 932T369 848Z"/>
<glyph unicode="&lt;" glyph-name="less" horiz-adv-x="899" d="M838 322Q827 312 819 304T801 289T780 273T754 256L86 506L61 530Q61 532 61 533T62 536L65 545Q65 547 67 553L70 566L73 574Q74 577 74 580T77 587L79 595L85 613L90 625L92 629L813 899L838 874Q835 863 832 850T824 823T816 796T809 774L254 568L821 358L838 322Z"/>
<glyph unicode="=" glyph-name="equal" horiz-adv-x="899" d="M838 440Q833 419 824 397T809 355H86L61 382Q66 402 74 423T92 465H813L838 440ZM838 740Q833 720 824 696T809 655H86L61 680Q66 700 74 723T92 765H813L838 740Z"/>
<glyph unicode="&gt;" glyph-name="greater" horiz-adv-x="899" d="M838 625Q833 606 825 577T807 524L86 256L61 281Q64 291 68 304T76 331T84 357T92 379L643 585L78 795L61 834Q84 852 105 868T145 899L815 649L838 625Z"/>
<glyph unicode="?" glyph-name="question" horiz-adv-x="887" d="M806 1165Q806 1090 783 1030T723 918T645 819T566 721T501 613T469 485L463 389Q444 375 430 366T387 348L362 369L354 485Q351 542 371 598T423 709T493 817T565 922T620 1022T642 1120Q642 1244 586 1314T424 1384Q388 1384 356 1368T298 1324T259 1261T244 1186Q244 1172 247 1157T256 1128Q224 1111 189 1103T109 1090L82 1120Q80 1128 80 1136V1153Q80 1223 114 1283T207 1387T342 1455T502 1480Q575 1480 632 1457T727 1393T786 1293T806 1165ZM539 123Q539 88 529 59T500 7T456 -27T401 -40Q347 -40 324 -11T301 72Q301 106 312 135T342 187T386 223T440 236Q490 236 514 207T539 123Z"/>
<glyph unicode="@" glyph-name="at" horiz-adv-x="1653" d="M1014 731Q986 787 952 818T858 850Q803 850 758 827T679 756T628 639T610 475Q610 390 630 321T682 202T752 127T825 100Q844 100 862 106T901 130T949 176T1014 250V731ZM1583 549Q1583 462 1564 386T1511 245T1434 130T1343 44T1248 -11T1157 -30Q1133 -30 1111 -20T1071 14T1040 73T1020 162Q979 105 948 68T886 9T825 -21T754 -30Q703 -30 651 2T556 96T487 245T460 444Q460 501 473 562T513 680T577 788T662 877T767 938T889 960Q912 960 931 957T968 943T1008 915T1055 868Q1081 884 1108 906T1163 960L1194 930Q1185 901 1179 868Q1173 839 1169 802T1164 723V285Q1164 195 1194 152T1272 109Q1297 109 1330 140T1392 226T1442 354T1463 512Q1463 680 1420 811T1301 1031T1118 1166T885 1213Q783 1213 694 1187T531 1115T400 1004T302 864T241 701T220 524Q220 403 245 299T316 108T425 -46T565 -160T727 -231T903 -255Q987 -255 1067 -238T1217 -194T1339 -138T1423 -82L1450 -137Q1414 -175 1357 -217T1224 -294T1055 -352T854 -375Q682 -375 538 -316T290 -148T128 117T70 465Q70 578 100 686T187 891T321 1069T494 1208T698 1300T924 1333Q1073 1333 1194 1283T1402 1133T1536 888T1583 549Z"/>
<glyph unicode="A" glyph-name="A" horiz-adv-x="1219" d="M758 535L582 1049L411 535H758ZM381 445L270 111Q260 80 297 66T416 43V0H0V43Q68 55 108 68T158 111L530 1237Q553 1262 587 1282T647 1317L1067 111Q1072 97 1081 86T1105 67T1143 53T1198 43V0H776V43Q854 48 884 63T903 111L789 445H381Z"/>
<glyph unicode="B" glyph-name="B" horiz-adv-x="1126" d="M413 1206Q399 1206 385 1206T355 1204V725H389Q503 725 573 747T683 805T736 884T750 971Q750 1021 734 1064T678 1138T575 1188T413 1206ZM520 650Q472 650 432 646T355 635V104Q355 96 364 89Q381 83 403 80T448 74T494 71T537 70Q609 70 667 88T766 140T830 219T852 322Q852 377 833 435T773 541T670 619T520 650ZM1016 356Q1016 271 983 203T889 86T744 12T559 -14Q537 -14 507 -14T441 -12T369 -10T297 -7Q214 -4 123 0H50V43Q118 57 156 73T195 104V1190Q157 1185 121 1180T50 1168L41 1230Q80 1241 131 1251T240 1270T356 1284T469 1290Q569 1290 650 1272T787 1218T874 1133T905 1020Q905 912 854 835T715 723Q779 711 834 678T929 597T993 487T1016 356Z"/>
<glyph unicode="C" glyph-name="C" horiz-adv-x="1098" d="M1018 211Q954 143 896 97T783 22T676 -18T571 -30Q478 -30 389 12T228 133T114 326T70 586Q70 746 118 876T250 1098T443 1240T676 1290Q784 1290 865 1261T999 1190Q1005 1185 996 1170T972 1136T939 1098T909 1067L874 1073Q823 1120 753 1150T592 1180Q558 1180 518 1168T436 1128T357 1058T290 952T243 809T225 623Q225 490 263 390T362 224T497 124T643 90Q697 90 784 127T981 256Q986 254 991 248T1002 234T1011 221T1018 211Z"/>
<glyph unicode="D" glyph-name="D" horiz-adv-x="1214" d="M453 1206Q403 1206 355 1203V154Q355 132 365 118Q379 101 419 93T537 84Q606 84 679 117T814 219T914 392T953 637Q953 770 920 875T824 1054T667 1167T453 1206ZM1123 676Q1123 551 1098 453T1029 279T930 151T810 65T683 16T559 0H50V43Q118 57 156 73T195 104V1188Q155 1183 119 1178T50 1168L41 1230Q85 1242 142 1253T264 1272T393 1285T518 1290Q657 1290 769 1249T959 1128T1080 935T1123 676Z"/>
<glyph unicode="E" glyph-name="E" horiz-adv-x="1016" d="M954 244Q946 158 934 94T913 0H50V43Q118 57 156 73T195 104V1155Q195 1167 159 1185T50 1217V1260H856L889 1235Q887 1209 883 1178T873 1116T861 1057T848 1010H803Q801 1056 795 1087T778 1136T754 1162T721 1170H355V735H760L786 707Q778 693 767 677T743 646T718 617T694 596Q679 611 662 621T622 638T569 648T496 651H355V154Q355 139 362 128T391 108T453 95T559 90H688Q734 90 766 95T824 117T869 169T911 262L954 244Z"/>
<glyph unicode="F" glyph-name="F" horiz-adv-x="979" d="M50 0V43Q118 57 156 73T195 104V1155Q195 1167 159 1185T50 1217V1260H866L897 1235Q896 1209 892 1178T883 1116T871 1057T858 1010H811Q809 1056 804 1087T788 1136T763 1162T729 1170H355V735H729L758 707Q749 693 737 677T712 646T687 617T664 596Q649 611 632 621T593 638T540 648T467 651H355V104Q355 92 393 77T530 43V0H50Z"/>
<glyph unicode="G" glyph-name="G" horiz-adv-x="1194" d="M1042 1188Q1049 1183 1040 1169T1015 1136T979 1101T946 1071L913 1077Q880 1108 845 1128T772 1160T690 1175T598 1180Q573 1180 536 1169T457 1132T374 1065T300 963T246 822T225 637Q225 498 262 395T359 225T494 124T645 90Q718 90 779 104T890 146V485Q890 496 881 506T848 527T785 547T684 567V610H1145V567Q1088 553 1064 531T1040 485V154Q966 93 908 57T800 2T707 -24T618 -30Q517 -30 419 7T244 121T118 312T70 582Q70 748 122 879T266 1102T478 1242T737 1290Q770 1290 809 1283T890 1263T970 1231T1042 1188Z"/>
<glyph unicode="H" glyph-name="H" horiz-adv-x="1339" d="M50 0V43Q118 57 156 73T195 104V1155Q195 1167 159 1185T50 1217V1260H500V1217Q432 1203 394 1186T355 1155V705H965V1155Q965 1167 929 1185T820 1217V1260H1270V1217Q1202 1203 1164 1186T1125 1155V104Q1125 92 1161 75T1270 43V0H820V43Q888 57 926 73T965 104V615H355V104Q355 92 391 75T500 43V0H50Z"/>
<glyph unicode="I" glyph-name="I" horiz-adv-x="610" d="M70 0V43Q138 57 176 73T215 104V1155Q215 1167 179 1185T70 1217V1260H520V1217Q452 1203 414 1186T375 1155V104Q375 92 411 75T520 43V0H70Z"/>
<glyph unicode="J" glyph-name="J" horiz-adv-x="625" d="M555 1217Q487 1203 449 1186T410 1155V205Q410 86 394 7T350 -128T286 -219T211 -288Q184 -310 152 -327T86 -356T24 -374T-25 -380Q-55 -380 -85 -371T-138 -349T-175 -323T-190 -300Q-190 -292 -177 -277T-146 -247T-108 -219T-74 -200Q-46 -218 -25 -229T14 -247T48 -255T84 -257Q110 -257 139 -240T193 -180T234 -60T250 133V1155Q250 1161 242 1168T213 1183T153 1199T55 1217V1260H555V1217Z"/>
<glyph unicode="K" glyph-name="K" horiz-adv-x="1184" d="M50 0V43Q118 57 156 73T195 104V1155Q195 1167 159 1185T50 1217V1260H500V1217Q432 1203 394 1186T355 1155V646L764 1128Q787 1155 793 1171T789 1196T754 1209T690 1217V1260H1110V1217Q1078 1213 1053 1209T1008 1199T972 1182T940 1155L502 671L985 127Q1003 107 1023 96T1067 81T1116 76T1167 80L1174 37Q1115 18 1056 2T948 -14Q919 -14 897 -5T854 29L355 639V104Q355 92 391 75T500 43V0H50Z"/>
<glyph unicode="L" glyph-name="L" horiz-adv-x="975" d="M944 244Q936 157 924 93T903 0H50V43Q118 57 156 73T195 104V1155Q195 1167 159 1185T50 1217V1260H500V1217Q432 1203 394 1186T355 1155V164Q355 146 363 133T393 110T450 95T541 90H684Q730 90 761 95T817 117T860 169T901 262L944 244Z"/>
<glyph unicode="M" glyph-name="M" horiz-adv-x="1651" d="M1550 1217Q1521 1217 1486 1208T1415 1181L1425 104Q1425 92 1461 75T1570 43V0H1115V43Q1183 57 1226 73T1270 104L1261 1040L823 0H774L329 1033L320 104Q320 92 356 75T465 43V0H60V43Q129 57 167 73T205 104L215 1177Q177 1199 139 1208T70 1217V1260H356Q366 1260 373 1257T386 1245T400 1220T420 1176L818 274L1200 1176Q1213 1206 1221 1223T1235 1248T1248 1258T1264 1260H1550V1217Z"/>
<glyph unicode="N" glyph-name="N" horiz-adv-x="1329" d="M50 0V43Q124 52 159 71T195 104V1149Q162 1180 126 1195T50 1217V1260H262Q277 1260 286 1258T306 1247T327 1224T358 1182L1015 276V1155Q1015 1167 983 1186T870 1217V1260H1280V1217Q1208 1207 1172 1188T1135 1155V-30Q1085 -24 1057 -10T1016 21L315 993V104Q315 92 348 73T460 43V0H50Z"/>
<glyph unicode="O" glyph-name="O" horiz-adv-x="1229" d="M983 629Q983 698 971 765T935 893T879 1005T803 1094T711 1153T604 1175Q515 1175 445 1138T326 1031T251 862T225 641Q225 529 255 429T337 254T458 134T604 90Q687 90 756 124T876 227T955 396T983 629ZM1138 647Q1138 511 1093 389T971 173T791 25T575 -30Q455 -30 361 23T203 165T104 370T70 612Q70 748 114 871T234 1087T412 1235T633 1290Q757 1290 851 1236T1009 1091T1105 885T1138 647Z"/>
<glyph unicode="P" glyph-name="P" horiz-adv-x="1071" d="M50 0V43Q118 57 156 73T195 104V1190Q158 1185 122 1180T50 1168L41 1230Q89 1243 145 1254T263 1273T386 1285T510 1290Q618 1290 705 1267T853 1199T948 1088T982 936Q982 867 963 810T911 709T836 632T749 578T658 546T573 535Q479 535 412 565L389 640Q431 621 469 616T543 610Q589 610 639 628T731 682T800 771T827 895Q827 977 798 1036T717 1132T598 1188T453 1206Q428 1206 404 1206T355 1204V104Q355 98 363 91T390 76T444 60T530 43V0H50Z"/>
<glyph unicode="Q" glyph-name="Q" horiz-adv-x="1229" d="M983 629Q983 698 971 765T935 893T879 1005T803 1094T711 1153T604 1175Q515 1175 445 1138T326 1031T251 862T225 641Q225 529 255 429T337 254T458 134T604 90Q687 90 756 124T876 227T955 396T983 629ZM1282 -117Q1258 -161 1233 -195T1184 -254T1139 -292T1102 -305Q1040 -305 980 -275T859 -201T740 -110T620 -27Q597 -30 575 -30Q465 -30 372 21T212 159T108 363T70 612Q70 748 114 871T234 1087T412 1235T633 1290Q757 1290 851 1236T1009 1091T1105 885T1138 647Q1138 541 1110 442T1032 260T914 112T765 12Q811 -12 856 -42T945 -98T1030 -142T1108 -160Q1120 -160 1133 -156T1164 -143T1203 -119T1255 -82L1282 -117Z"/>
<glyph unicode="R" glyph-name="R" horiz-adv-x="1149" d="M50 0V43Q118 57 156 73T195 104V1189Q160 1184 124 1179T50 1168L41 1230Q88 1242 136 1252T236 1271T345 1285T469 1290Q585 1290 671 1267T814 1203T899 1107T927 987Q927 915 907 857T849 753T759 676T645 625L948 139Q963 117 980 104T1019 85T1067 78T1128 80L1139 37Q1073 15 1014 1T915 -14Q886 -14 859 2T817 41L514 602Q501 600 488 600H461Q435 600 409 602T355 611V104Q355 92 391 75T500 43V0H50ZM432 1206Q394 1206 355 1203V691Q382 686 402 685T444 684Q602 684 687 753T772 956Q772 1011 753 1057T692 1136T586 1187T432 1206Z"/>
<glyph unicode="S" glyph-name="S" horiz-adv-x="975" d="M885 377Q885 332 873 285T837 193T776 107T690 36T578 -12T440 -30Q407 -30 368 -23T288 -4T207 27T131 68Q124 72 121 102T117 172T120 255T131 324L172 319Q195 260 229 217T306 146T396 104T496 90Q541 90 586 108T668 159T727 233T750 324Q750 385 723 429T653 507T553 568T439 622T324 679T225 749T154 841T127 967Q127 998 136 1034T165 1106T216 1175T292 1234T395 1275T526 1290Q571 1290 616 1283T701 1264T773 1235T823 1198Q830 1192 824 1175T804 1137T774 1096T745 1065L709 1071Q681 1104 651 1126T589 1163T526 1184T467 1190Q411 1190 372 1173T307 1131T269 1074T257 1014Q257 967 284 931T355 864T455 806T571 750T686 686T787 608T858 508T885 377Z"/>
<glyph unicode="T" glyph-name="T" horiz-adv-x="1128" d="M307 0V43Q353 52 384 62T435 81T462 99T471 115V1170H172Q157 1170 145 1165T120 1141T91 1088T53 995L10 1014Q15 1073 23 1140T43 1260H1053L1083 1235Q1081 1184 1073 1124T1053 1000H1008Q998 1046 991 1078T973 1131T950 1161T915 1170H631V115Q631 102 667 82T795 43V0H307Z"/>
<glyph unicode="U" glyph-name="U" horiz-adv-x="1339" d="M1275 1217Q1207 1203 1169 1186T1130 1155V524Q1130 393 1098 290T1006 116T860 8T666 -30Q563 -30 477 -2T328 85T230 233T195 444V1155Q195 1167 159 1185T50 1217V1260H500V1217Q432 1203 394 1186T355 1155V494Q355 398 377 322T444 194T554 113T705 85Q776 85 831 119T923 210T980 337T1000 483V1155Q1000 1167 964 1185T855 1217V1260H1275V1217Z"/>
<glyph unicode="V" glyph-name="V" horiz-adv-x="1331" d="M1290 1217Q1222 1204 1184 1192T1135 1151L752 59Q744 37 725 22T682 -4T637 -21T602 -30L162 1151Q152 1177 118 1193T20 1217V1260H455V1217Q375 1211 346 1197T327 1153L680 203L1016 1151Q1027 1180 992 1193T874 1217V1260H1290V1217Z"/>
<glyph unicode="W" glyph-name="W" horiz-adv-x="1741" d="M1702 1217Q1658 1208 1629 1200T1583 1185T1559 1170T1550 1155L1321 59Q1316 38 1300 23T1262 -3T1219 -20T1182 -30L856 944L569 59Q562 38 545 23T507 -2T462 -19T418 -30L158 1147Q153 1172 122 1188T20 1217V1260H449V1217Q400 1212 372 1205T330 1188T314 1169T313 1147L512 250L836 1260H891L1245 250L1432 1155Q1434 1167 1423 1176T1391 1192T1341 1205T1278 1217V1260H1702V1217Z"/>
<glyph unicode="X" glyph-name="X" horiz-adv-x="1260" d="M778 0V43Q822 47 849 53T888 69T899 93T883 127L604 535L352 127Q324 83 356 66T481 43V0H31V43Q96 48 142 66T213 127L530 644L197 1133Q182 1155 168 1169T137 1192T97 1206T41 1217V1260H492V1217Q407 1209 381 1193T385 1133L632 770L858 1133Q872 1156 870 1170T851 1194T803 1209T727 1217V1260H1180V1217Q1144 1213 1116 1208T1065 1193T1027 1169T997 1133L706 662L1071 127Q1086 106 1101 92T1135 68T1177 53T1229 43V0H778Z"/>
<glyph unicode="Y" glyph-name="Y" horiz-adv-x="1217" d="M360 0V43Q451 62 487 81T524 115V537Q487 618 440 706T344 877T249 1031T168 1151Q159 1162 148 1172T118 1189T73 1201T4 1206L0 1249Q59 1257 118 1263T219 1270Q265 1270 297 1231Q334 1183 377 1118T463 979T547 829T623 680L891 1151Q906 1177 882 1192T780 1217V1260H1176V1217Q1107 1204 1071 1191T1020 1151L684 541V115Q684 109 692 101T720 82T771 62T850 43V0H360Z"/>
<glyph unicode="Z" glyph-name="Z" horiz-adv-x="1071" d="M991 293Q989 261 988 222T985 144T982 67T979 0H88L59 45L770 1160H311Q295 1160 276 1149T239 1114T205 1053T178 963L121 975L150 1276Q177 1270 199 1267T243 1262T288 1260H956L981 1217L276 100H799Q820 100 837 109T869 142T901 205T936 307L991 293Z"/>
<glyph unicode="[" glyph-name="bracketleft" horiz-adv-x="631" d="M590 -249Q585 -269 577 -293T561 -335H140V1600H555L588 1574Q583 1549 575 1523T561 1485H280V-220H555L590 -249Z"/>
<glyph unicode="\" glyph-name="backslash" horiz-adv-x="961" d="M870 -335Q858 -331 842 -325T810 -313T780 -299T756 -284L55 1578L92 1600Q150 1585 205 1551L905 -310L870 -335Z"/>
<glyph unicode="]" glyph-name="bracketright" horiz-adv-x="631" d="M76 -335L43 -306Q46 -283 53 -258T68 -220H350V1485H76L41 1511Q44 1533 52 1558T68 1600H490V-335H76Z"/>
<glyph unicode="^" glyph-name="asciicircum" horiz-adv-x="1008" d="M907 635Q882 612 859 597T801 565L764 592L481 1315L231 635Q220 625 206 615T177 595T148 578T123 565L100 592L422 1462Q434 1474 451 1486T487 1511T523 1533T555 1550L907 635Z"/>
<glyph unicode="_" glyph-name="underscore" horiz-adv-x="961" d="M897 -156Q892 -177 884 -202T868 -246H86L61 -219Q66 -199 74 -175T92 -131H874L897 -156Z"/>
<glyph unicode="`" glyph-name="grave" horiz-adv-x="653" d="M438 1078Q429 1070 414 1061T382 1047L30 1424L52 1465Q62 1467 81 1470T122 1478T165 1485T198 1489L438 1078Z"/>
<glyph unicode="a" glyph-name="a" horiz-adv-x="940" d="M352 100Q412 100 479 130T623 227V486Q539 472 485 459T395 433T339 404T302 372Q276 345 260 311T244 229Q244 189 256 164T285 125T320 105T352 100ZM926 82Q841 23 786 -3T702 -30Q669 -30 647 16T623 143Q578 98 533 65T445 11T364 -20T293 -30Q257 -30 219 -18T151 20T100 87T80 186Q80 262 106 313T170 401Q194 425 224 446T305 488T432 526T623 561V702Q623 736 615 765T588 816T537 849T455 860Q423 859 392 849T338 821T302 779T292 724Q293 715 272 704T221 683T163 669T122 667L108 706Q131 756 178 802T285 883T413 939T544 960Q654 960 713 903T773 742V178Q773 136 784 118T813 100Q827 100 849 105T911 127L926 82Z"/>
<glyph unicode="b" glyph-name="b" horiz-adv-x="1040" d="M950 492Q950 434 935 373T891 254T820 144T725 54T607 -7T469 -30Q450 -30 417 -22T340 3T245 42T140 96V1296Q140 1342 135 1367T116 1404T77 1421T10 1430V1470Q76 1486 136 1502T253 1550L263 1540Q269 1534 275 1529Q282 1523 290 1516V727Q340 787 389 831T484 903T569 946T637 960Q705 960 762 928T861 836T926 688T950 492ZM815 408Q815 508 795 585T740 716T660 797T567 825Q549 825 520 815T453 780T374 714T290 610V203Q330 173 371 153T450 122T518 105T567 100Q628 100 674 126T751 196T799 295T815 408Z"/>
<glyph unicode="c" glyph-name="c" horiz-adv-x="895" d="M840 190Q775 113 726 70T634 4T552 -24T465 -30Q392 -30 323 2T199 96T113 245T80 444Q80 552 119 646T227 809T389 919T590 960Q623 960 657 955T723 939T781 914T825 881Q827 869 822 847T808 801T788 754T768 717L731 727Q723 745 705 766T659 806T591 837T500 850Q447 850 399 827T313 756T253 639T230 475Q230 390 255 321T323 202T421 127T537 100Q565 100 589 102T641 118T707 157T801 231L840 190Z"/>
<glyph unicode="d" glyph-name="d" horiz-adv-x="1065" d="M1051 84Q1005 53 970 32T908 -3T862 -23T829 -30Q796 -30 775 15T747 169Q709 123 672 86T595 24T511 -16T416 -30Q360 -30 301 2T193 96T112 245T80 444Q80 501 95 562T140 680T211 788T305 877T419 938T551 960Q598 960 644 949T745 897V1274Q745 1329 742 1361T725 1411T682 1435T600 1446V1485Q682 1496 747 1514T864 1550L895 1520V254Q895 219 896 194T901 152T908 124T918 106Q927 95 952 99T1038 127L1051 84ZM745 261V730Q712 787 652 818T520 850Q456 850 403 827T312 756T252 639T230 475Q230 390 254 321T317 202T400 127T487 100Q521 100 554 114T619 150T682 202T745 261Z"/>
<glyph unicode="e" glyph-name="e" horiz-adv-x="946" d="M496 855Q444 855 401 835T324 779T268 692T236 580H664Q687 580 694 587T702 616Q702 643 695 683T665 762T602 828T496 855ZM866 557Q848 537 818 520T756 490H230Q231 412 251 342T310 218T403 132T526 100Q557 100 586 104T650 122T725 163T823 236Q836 229 845 216T860 195Q793 122 743 78T648 9T560 -22T465 -30Q388 -30 319 3T196 96T112 242T80 434Q80 501 95 566T138 689T208 796T301 881Q323 896 350 910T408 936T467 953T524 960Q589 960 640 942T729 894T793 823T835 737T859 646T866 557Z"/>
<glyph unicode="f" glyph-name="f" horiz-adv-x="637" d="M817 1454Q817 1446 803 1430T768 1397T727 1365T690 1343Q666 1364 642 1379T595 1405T553 1420T520 1425Q494 1425 465 1408T410 1344T367 1219T350 1016V930H608L637 901Q628 887 615 871T588 841T561 816T539 800Q516 812 473 823T350 835V102Q350 96 360 90T396 78T460 63T559 43V0H60V43Q129 55 164 72T200 102V835H66L45 863L123 930H200V961Q200 1079 216 1159T259 1295T323 1388T399 1458Q426 1480 458 1497T521 1526T581 1544T629 1550Q660 1550 693 1538T753 1511T799 1479T817 1454Z"/>
<glyph unicode="g" glyph-name="g" horiz-adv-x="999" d="M690 600Q690 653 673 700T621 783T535 839T416 860Q391 860 361 847T303 809T258 746T240 659Q240 606 256 559T306 476T391 421T514 400Q543 400 574 412T631 450T673 513T690 600ZM518 -4Q480 0 448 5T388 16Q316 -23 275 -54T212 -109T186 -154T180 -190Q180 -232 208 -269T283 -334T392 -378T522 -395Q589 -395 644 -377T738 -328T798 -253T820 -157Q820 -129 807 -106T760 -63T668 -29T518 -4ZM830 641Q830 564 796 501T705 392T578 321T434 295H430Q382 255 364 229T346 196Q346 184 356 173T395 151T474 130T602 110Q711 98 782 74T895 19T953 -49T970 -120Q970 -175 949 -224T892 -316T807 -392T702 -450T586 -487T467 -500Q420 -500 370 -494T271 -473T179 -438T102 -386T50 -317T30 -229Q30 -203 39 -175T78 -113T159 -42T297 42Q232 65 207 94T182 153Q182 161 187 174T207 207T251 252T326 309Q275 323 234 349T163 413T117 498T100 602Q100 675 133 740T220 854T343 931T485 960Q545 960 597 943T693 895Q745 900 787 908T862 925T922 943T969 960L989 930Q977 902 966 879T932 830Q894 823 858 819T775 813Q801 775 815 732T830 641Z"/>
<glyph unicode="h" glyph-name="h" horiz-adv-x="1130" d="M670 0V43Q742 62 776 76T810 104V633Q810 694 803 732T779 792T739 822T682 830Q647 830 607 812T522 759T433 672T345 549V104Q345 89 382 73T485 43V0H55V43Q121 60 158 73T195 104V1298Q195 1340 192 1363T174 1400T131 1418T55 1430V1470Q97 1478 131 1486T195 1503T252 1524T308 1550L345 1516V684Q388 748 440 799T546 886T649 941T739 960Q782 960 822 947T892 907T941 839T960 741V104Q960 90 990 77T1100 43V0H670Z"/>
<glyph unicode="i" glyph-name="i" horiz-adv-x="555" d="M70 0V43Q138 57 174 73T210 104V670Q210 721 208 752T193 801T152 827T70 840V880Q101 886 135 894T204 913T269 936T326 960H360V104Q360 92 393 75T500 43V0H70ZM390 1261Q390 1233 381 1208T357 1165T320 1135T274 1124Q229 1124 210 1149T190 1220Q190 1248 199 1273T224 1316T260 1345T305 1356Q390 1356 390 1261Z"/>
<glyph unicode="j" glyph-name="j" horiz-adv-x="530" d="M355 99Q355 -16 341 -96T301 -233T240 -329T165 -400Q138 -421 108 -439T48 -471T-8 -492T-54 -500Q-85 -500 -114 -493T-167 -474T-205 -451T-220 -431Q-220 -422 -206 -407T-171 -374T-130 -342T-93 -320Q-61 -348 -27 -356T45 -365Q74 -365 102 -347T154 -282T191 -157T205 43V670Q205 721 203 752T187 800T146 826T65 840V880Q107 887 139 896T201 914T258 935T318 960H355V99ZM385 1261Q385 1233 376 1208T352 1165T315 1135T270 1124Q225 1124 205 1149T185 1220Q185 1248 194 1273T218 1316T255 1345T300 1356Q385 1356 385 1261Z"/>
<glyph unicode="k" glyph-name="k" horiz-adv-x="1024" d="M55 0V43Q121 60 158 73T195 104V1296Q195 1342 190 1367T169 1404T126 1421T55 1430V1470Q124 1484 190 1502T308 1550L345 1516V530L653 799Q686 828 693 845T691 872T659 884T610 887V930H989V887Q945 882 908 871T829 825L488 552L864 111Q878 95 893 85T926 68T966 61T1018 61L1024 18Q995 11 967 6T914 -2T870 -8T840 -10Q798 -10 773 2T725 43L345 522V104Q345 96 348 90T363 78T396 63T455 43V0H55Z"/>
<glyph unicode="l" glyph-name="l" horiz-adv-x="555" d="M60 0V43Q103 50 132 57T178 72T202 88T210 104V1296Q210 1341 204 1366T182 1403T139 1420T70 1430V1470Q142 1484 202 1501T324 1550L360 1516V104Q360 89 394 73T510 43V0H60Z"/>
<glyph unicode="m" glyph-name="m" horiz-adv-x="1645" d="M1186 0V43Q1258 62 1291 76T1325 104V633Q1325 694 1319 734T1299 798T1264 830T1212 840Q1179 840 1142 822T1066 769T988 685T910 573V104Q910 90 941 77T1051 43V0H621V43Q693 62 726 76T760 104V633Q760 694 754 734T735 798T700 830T647 840Q579 840 505 770T345 573V104Q345 89 382 73T485 43V0H55V43Q121 60 158 73T195 104V711Q195 751 192 774T174 809T131 828T55 840V880Q93 886 125 894T186 913T241 935T295 960L330 925L341 696Q386 762 432 811T523 894T610 943T688 960Q736 960 776 949T846 910T893 837T910 725V704Q952 767 997 814T1086 894T1173 943T1253 960Q1301 960 1341 947T1411 907T1458 839T1475 741V104Q1475 90 1506 77T1616 43V0H1186Z"/>
<glyph unicode="n" glyph-name="n" horiz-adv-x="1130" d="M670 0V43Q742 62 776 76T810 104V633Q810 694 803 732T781 792T742 822T682 830Q651 830 613 815T532 767T441 681T345 549V104Q345 89 382 73T485 43V0H55V43Q121 60 158 73T195 104V711Q195 750 192 773T176 809T134 828T55 840V880Q123 891 181 911T295 960L330 925L341 676Q385 743 438 795T545 884T649 940T739 960Q782 960 822 947T892 907T941 839T960 741V104Q960 90 990 77T1100 43V0H670Z"/>
<glyph unicode="o" glyph-name="o" horiz-adv-x="1030" d="M795 455Q795 534 769 605T702 731T609 818T506 850Q430 850 378 821T295 742T249 622T235 473Q235 394 263 323T333 197T427 112T524 80Q595 80 646 106T730 182T779 300T795 455ZM950 485Q950 418 933 354T886 233T814 128T722 44T614 -10T496 -30Q401 -30 325 6T194 105T110 255T80 444Q80 510 96 574T142 695T213 801T305 885T414 940T535 960Q629 960 705 924T836 825T920 674T950 485Z"/>
<glyph unicode="p" glyph-name="p" horiz-adv-x="1085" d="M870 426Q870 513 850 588T795 718T716 804T623 835Q605 835 576 824T510 787T431 718T345 610V213Q390 179 428 157T500 123T564 105T621 100Q675 100 720 121T799 183T851 285T870 426ZM1005 492Q1005 434 990 373T949 254T887 144T809 54T719 -7T623 -30Q564 -30 490 3T345 98V-375Q345 -391 382 -407T514 -437V-480H55V-437Q121 -421 158 -407T195 -375V711Q195 748 191 772T173 810T131 831T55 840V880Q89 887 120 895T180 913T237 934T295 960L330 925L339 727Q389 787 439 831T536 903T621 946T690 960Q758 960 816 928T916 836T981 688T1005 492Z"/>
<glyph unicode="q" glyph-name="q" horiz-adv-x="1055" d="M487 100Q521 100 554 114T619 150T682 202T745 261V730Q712 787 652 818T520 850Q464 850 412 827T320 756T255 639T230 475Q230 390 254 321T317 202T400 127T487 100ZM575 -480V-437Q669 -423 707 -408T745 -375V166Q707 120 670 84T594 23T511 -16T416 -30Q360 -30 301 2T193 96T112 245T80 444Q80 532 108 606T176 737T263 835T344 895Q401 926 457 943T551 960Q578 960 605 957T660 943T718 915T782 867Q797 876 813 888T844 913T872 938T895 960L926 930Q917 902 910 869Q904 840 900 801T895 717V-375Q895 -391 926 -407T1034 -437V-480H575Z"/>
<glyph unicode="r" glyph-name="r" horiz-adv-x="811" d="M770 923Q779 917 779 888T771 821T751 743T727 676H684Q678 716 667 744T642 788T610 812T573 820Q551 820 522 802T461 744T400 643T345 496V104Q345 89 382 73T514 43V0H55V43Q121 58 158 72T195 104V686Q195 719 193 741T188 778T181 800T172 813Q165 820 157 824T135 832T102 836T55 840V880Q120 897 182 914T295 960L330 925L343 698Q370 753 401 801T469 884T544 940T627 960Q659 960 696 952T770 923Z"/>
<glyph unicode="s" glyph-name="s" horiz-adv-x="791" d="M700 283Q700 212 680 162T628 76T557 20T480 -12T409 -26T356 -30Q308 -30 241 -12T106 43Q99 46 97 75T95 143T101 221T113 283L156 272Q158 230 178 193T231 129T309 86T406 70Q442 70 472 81T524 113T558 163T570 227Q570 267 549 298T492 354T413 402T322 449Q280 470 241 494T172 548T123 615T104 700Q104 762 129 810T195 892T290 942T401 960Q432 960 469 955T543 939T612 915T664 883Q670 877 666 856T652 810T631 763T614 733L575 741Q527 813 476 841T375 870Q342 870 316 859T272 831T244 792T234 750Q234 718 253 693T303 645T373 602T455 559Q498 537 542 512T621 455T678 381T700 283Z"/>
<glyph unicode="t" glyph-name="t" horiz-adv-x="705" d="M676 117Q627 81 579 54T488 8T407 -20T344 -30Q309 -30 278 -17T222 25T184 103T170 221V835H41L20 863L98 930H170V1176L289 1280L320 1255V930H647L676 901Q667 887 654 871T626 841T599 816T575 800Q551 812 502 823T372 835H320V307Q320 247 325 207T344 144T377 110T428 100Q463 100 516 114T647 168L676 117Z"/>
<glyph unicode="u" glyph-name="u" horiz-adv-x="1085" d="M1069 82Q1032 58 998 38T935 2T884 -21T850 -30Q815 -30 793 16T765 166Q703 104 652 66T557 6T477 -22T408 -30Q361 -30 318 -17T242 30T190 125T170 279V711Q170 759 167 785T152 825T113 844T41 854V894Q77 898 108 904T169 918T229 936T292 960L320 921V334Q320 264 330 217T360 142T408 102T473 90Q505 90 538 99T607 129T682 182T765 262V711Q765 756 761 783T741 825T699 846T625 854V894Q697 903 763 922T885 960L915 921V254Q915 192 919 153T938 104Q950 96 979 100T1059 127L1069 82Z"/>
<glyph unicode="v" glyph-name="v" horiz-adv-x="1010" d="M989 887Q959 880 940 874T909 861T891 844T879 819L596 59Q587 37 570 22T533 -4T494 -21T461 -30L131 819Q122 847 96 861T20 887V930H406V887Q367 882 343 877T305 865T289 846T293 819L539 178L770 819Q775 834 774 844T760 862T726 875T666 887V930H989V887Z"/>
<glyph unicode="w" glyph-name="w" horiz-adv-x="1446" d="M1425 887Q1394 880 1376 874T1348 861T1333 846T1325 825L1104 59Q1098 37 1081 22T1042 -4T1001 -21T967 -30L729 648L524 59Q516 36 500 21T463 -5T424 -21T391 -30L129 825Q120 864 20 887V930H406V887Q357 882 330 876T291 861T279 843T281 825L469 190L715 930H784L1044 190L1217 825Q1223 848 1196 862T1100 887V930H1425V887Z"/>
<glyph unicode="x" glyph-name="x" horiz-adv-x="1030" d="M614 0V43Q639 45 661 49T695 62T708 87T688 131L490 400L301 131Q282 104 286 88T307 62T349 49T399 43V0H20V43Q60 49 88 58T135 80T168 107T193 137L436 475L201 797Q188 815 175 830T144 857T98 876T31 887V930H451V887Q420 884 398 879T364 864T353 839T373 799L534 577L688 799Q706 823 708 839T700 864T667 879T614 887V930H995V887Q914 881 870 858T799 797L589 502L856 137Q868 122 882 108T914 80T958 57T1016 43V0H614Z"/>
<glyph unicode="y" glyph-name="y" horiz-adv-x="1010" d="M989 887Q959 880 940 874T909 861T891 844T879 819L545 -82Q503 -193 452 -272T345 -401T233 -476T127 -500Q89 -500 57 -495T2 -483T-34 -466T-47 -447Q-47 -441 -35 -423T-5 -384T33 -342T70 -312Q118 -340 165 -342T250 -330Q268 -324 292 -302T340 -248T388 -176T430 -92L459 -23L131 819Q122 847 96 861T20 887V930H406V887Q367 882 343 877T305 865T289 846T293 819L539 182L770 819Q775 834 772 844T755 862T717 875T655 887V930H989V887Z"/>
<glyph unicode="z" glyph-name="z" horiz-adv-x="905" d="M829 248Q829 216 828 180T826 110T822 47T817 0H102L76 45L631 840H285Q269 840 253 834T221 812T192 766T168 690L119 702L137 946Q159 940 177 937T214 932T257 930H805L827 887L270 90H692Q717 90 738 128T782 258L829 248Z"/>
<glyph unicode="{" glyph-name="braceleft" horiz-adv-x="688" d="M649 1553Q610 1529 577 1498T518 1430T479 1348T465 1252Q465 1194 471 1157T485 1088T499 1018T506 923Q506 881 494 841T458 765T402 700T326 653Q376 644 410 622T466 565T496 486T506 385Q506 325 500 283T486 204T472 130T465 43Q465 -12 472 -54T499 -131T552 -195T639 -254L616 -315Q532 -281 472 -244T374 -160T317 -57T299 72Q299 131 305 169T319 243T333 319T340 422Q340 503 300 544T184 586H163Q157 586 152 586T141 584T119 580L102 633Q220 664 280 726T340 863Q340 903 337 932T330 984T320 1028T310 1072T302 1124T299 1195Q299 1262 321 1321T386 1431T488 1524T623 1600L649 1553Z"/>
<glyph unicode="|" glyph-name="bar" horiz-adv-x="469" d="M320 -353Q302 -369 271 -383T215 -405L180 -382V1621Q208 1638 231 1650T284 1670L320 1648V-353Z"/>
<glyph unicode="}" glyph-name="braceright" horiz-adv-x="688" d="M588 651Q469 620 410 560T350 422Q350 381 353 353T360 301T369 258T379 215T386 162T389 92Q389 24 367 -35T302 -145T200 -238T66 -315L39 -266Q78 -242 111 -212T170 -144T209 -62T223 35Q223 93 217 129T204 198T190 268T184 365Q184 406 196 446T231 521T288 584T365 631Q314 641 280 663T224 720T193 800T184 902Q184 961 190 1003T203 1082T217 1156T223 1242Q223 1298 216 1341T189 1418T135 1481T49 1539L72 1600Q156 1567 216 1530T315 1446T371 1344T389 1215Q389 1156 383 1117T370 1043T356 967T350 865Q350 784 390 742T506 700H525Q531 700 536 700T548 702L571 707L588 651Z"/>
<glyph unicode="~" glyph-name="asciitilde" horiz-adv-x="999" d="M950 774Q929 725 897 673T826 577T741 505T647 477Q605 477 558 502T463 557T367 612T276 637Q227 637 186 598T102 475L49 502Q70 551 102 603T174 699T259 771T352 799Q398 799 446 774T542 719T635 664T721 639Q769 639 812 678T897 799L950 774Z"/>
</font>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -0,0 +1,442 @@
"""
Gcodec is a collection of utilities to decode and encode gcode.
To run gcodec, install python 2.x on your machine, which is avaliable from http://www.python.org/download/
Then in the folder which gcodec is in, type 'python' in a shell to run the python interpreter. Finally type 'from gcodec import *' to import this program.
Below is an example of gcodec use. This example is run in a terminal in the folder which contains gcodec and Screw Holder Bottom_export.gcode.
>>> from gcodec import *
>>> getFileText('Screw Holder Bottom_export.gcode')
'G90\nG21\nM103\nM105\nM106\nM110 S60.0\nM111 S30.0\nM108 S210.0\nM104 S235.0\nG1 X0.37 Y-4.07 Z1.9 F60.0\nM101\n
..
many lines of text
..
"""
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 fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import archive
from fabmetheus_utilities import euclidean
import cStringIO
import math
import os
import sys
import traceback
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__date__ = '$Date: 2008/21/04 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def addLineAndNewlineIfNecessary(line, output):
'Add the line and if the line does not end with a newline add a newline.'
output.write(line)
if len(line) < 1:
return
if not line.endswith('\n'):
output.write('\n')
def addLinesToCString(cString, lines):
'Add lines which have something to cStringIO.'
for line in lines:
if line != '':
cString.write(line + '\n')
def getArcDistance(relativeLocation, splitLine):
'Get arc distance.'
halfPlaneLineDistance = 0.5 * abs(relativeLocation.dropAxis())
radius = getDoubleFromCharacterSplitLine('R', splitLine)
if radius == None:
iFloat = getDoubleFromCharacterSplitLine('I', splitLine)
jFloat = getDoubleFromCharacterSplitLine('J', splitLine)
radius = abs(complex(iFloat, jFloat))
angle = 0.0
if radius > 0.0:
halfPlaneLineDistanceOverRadius = halfPlaneLineDistance / radius
if halfPlaneLineDistance < radius:
angle = 2.0 * math.asin(halfPlaneLineDistanceOverRadius)
else:
angle = math.pi * halfPlaneLineDistanceOverRadius
return abs(complex(angle * radius, relativeLocation.z))
def getDoubleAfterFirstLetter(word):
'Get the double value of the word after the first letter.'
return float(word[1 :])
def getDoubleForLetter(letter, splitLine):
'Get the double value of the word after the first occurence of the letter in the split line.'
return getDoubleAfterFirstLetter(splitLine[getIndexOfStartingWithSecond(letter, splitLine)])
def getDoubleFromCharacterSplitLine(character, splitLine):
'Get the double value of the string after the first occurence of the character in the split line.'
indexOfCharacter = getIndexOfStartingWithSecond(character, splitLine)
if indexOfCharacter < 0:
return None
floatString = splitLine[indexOfCharacter][1 :]
try:
return float(floatString)
except ValueError:
return None
def getDoubleFromCharacterSplitLineValue(character, splitLine, value):
'Get the double value of the string after the first occurence of the character in the split line, if it does not exist return the value.'
splitLineFloat = getDoubleFromCharacterSplitLine(character, splitLine)
if splitLineFloat == None:
return value
return splitLineFloat
def getFeedRateMinute(feedRateMinute, splitLine):
'Get the feed rate per minute if the split line has a feed rate.'
indexOfF = getIndexOfStartingWithSecond('F', splitLine)
if indexOfF > 0:
return getDoubleAfterFirstLetter( splitLine[indexOfF] )
return feedRateMinute
def getFirstWord(splitLine):
'Get the first word of a split line.'
if len(splitLine) > 0:
return splitLine[0]
return ''
def getFirstWordFromLine(line):
'Get the first word of a line.'
return getFirstWord(line.split())
def getFirstWordIndexReverse(firstWord, lines, startIndex):
'Parse gcode in reverse order until the first word if there is one, otherwise return -1.'
for lineIndex in xrange(len(lines) - 1, startIndex - 1, -1):
if firstWord == getFirstWord(getSplitLineBeforeBracketSemicolon(lines[lineIndex])):
return lineIndex
return -1
def getGcodeFileText(fileName, gcodeText):
'Get the gcode text from a file if it the gcode text is empty and if the file is a gcode file.'
if gcodeText != '':
return gcodeText
if fileName.endswith('.gcode'):
return archive.getFileText(fileName)
return ''
def getGcodeWithoutDuplication(duplicateWord, gcodeText):
'Get gcode text without duplicate first words.'
lines = archive.getTextLines(gcodeText)
oldWrittenLine = None
output = cStringIO.StringIO()
for line in lines:
firstWord = getFirstWordFromLine(line)
if firstWord == duplicateWord:
if line != oldWrittenLine:
output.write(line + '\n')
oldWrittenLine = line
else:
if len(line) > 0:
output.write(line + '\n')
return output.getvalue()
def getIndexOfStartingWithSecond(letter, splitLine):
'Get index of the first occurence of the given letter in the split line, starting with the second word. Return - 1 if letter is not found'
for wordIndex in xrange( 1, len(splitLine) ):
word = splitLine[ wordIndex ]
firstLetter = word[0]
if firstLetter == letter:
return wordIndex
return - 1
def getLineWithValueString(character, line, splitLine, valueString):
'Get the line with a valueString.'
roundedValueString = character + valueString
indexOfValue = getIndexOfStartingWithSecond(character, splitLine)
if indexOfValue == -1:
return line + ' ' + roundedValueString
word = splitLine[indexOfValue]
return line.replace(word, roundedValueString)
def getLocationFromSplitLine(oldLocation, splitLine):
'Get the location from the split line.'
if oldLocation == None:
oldLocation = Vector3()
return Vector3(
getDoubleFromCharacterSplitLineValue('X', splitLine, oldLocation.x),
getDoubleFromCharacterSplitLineValue('Y', splitLine, oldLocation.y),
getDoubleFromCharacterSplitLineValue('Z', splitLine, oldLocation.z))
def getRotationBySplitLine(splitLine):
'Get the complex rotation from the split gcode line.'
return complex(splitLine[1].replace('(', '').replace(')', ''))
def getSplitLineBeforeBracketSemicolon(line):
'Get the split line before a bracket or semicolon.'
if ';' in line:
line = line[: line.find(';')]
bracketIndex = line.find('(')
if bracketIndex > 0:
return line[: bracketIndex].split()
return line.split()
def getStringFromCharacterSplitLine(character, splitLine):
'Get the string after the first occurence of the character in the split line.'
indexOfCharacter = getIndexOfStartingWithSecond(character, splitLine)
if indexOfCharacter < 0:
return None
return splitLine[indexOfCharacter][1 :]
def getTagBracketedLine(tagName, value):
'Get line with a begin tag, value and end tag.'
return '(<%s> %s </%s>)' % (tagName, value, tagName)
def getTagBracketedProcedure(procedure):
'Get line with a begin procedure tag, procedure and end procedure tag.'
return getTagBracketedLine('procedureName', procedure)
def isProcedureDone(gcodeText, procedure):
'Determine if the procedure has been done on the gcode text.'
if gcodeText == '':
return False
extruderInitializationIndex = gcodeText.find('(</extruderInitialization>)')
if extruderInitializationIndex == -1:
metadataBeginIndex = gcodeText.find('<metadata>')
metadataEndIndex = gcodeText.find('</metadata>')
if metadataBeginIndex != -1 and metadataEndIndex != -1:
attributeString = "procedureName='%s'" % procedure
return gcodeText.find(attributeString, metadataBeginIndex, metadataEndIndex) != -1
return False
return gcodeText.find(getTagBracketedProcedure(procedure), 0, extruderInitializationIndex) != -1
def isProcedureDoneOrFileIsEmpty(gcodeText, procedure):
'Determine if the procedure has been done on the gcode text or the file is empty.'
if gcodeText == '':
return True
return isProcedureDone(gcodeText, procedure)
def isThereAFirstWord(firstWord, lines, startIndex):
'Parse gcode until the first word if there is one.'
for lineIndex in xrange(startIndex, len(lines)):
line = lines[lineIndex]
splitLine = getSplitLineBeforeBracketSemicolon(line)
if firstWord == getFirstWord(splitLine):
return True
return False
class BoundingRectangle:
'A class to get the corners of a gcode text.'
def getFromGcodeLines(self, lines, radius):
'Parse gcode text and get the minimum and maximum corners.'
self.cornerMaximum = complex(-987654321.0, -987654321.0)
self.cornerMinimum = complex(987654321.0, 987654321.0)
self.oldLocation = None
self.cornerRadius = complex(radius, radius)
for line in lines:
self.parseCorner(line)
return self
def isPointInside(self, point):
'Determine if the point is inside the bounding rectangle.'
return point.imag >= self.cornerMinimum.imag and point.imag <= self.cornerMaximum.imag and point.real >= self.cornerMinimum.real and point.real <= self.cornerMaximum.real
def parseCorner(self, line):
'Parse a gcode line and use the location to update the bounding corners.'
splitLine = getSplitLineBeforeBracketSemicolon(line)
firstWord = getFirstWord(splitLine)
if firstWord == '(<boundaryPoint>':
locationComplex = getLocationFromSplitLine(None, splitLine).dropAxis()
self.cornerMaximum = euclidean.getMaximum(self.cornerMaximum, locationComplex)
self.cornerMinimum = euclidean.getMinimum(self.cornerMinimum, locationComplex)
elif firstWord == 'G1':
location = getLocationFromSplitLine(self.oldLocation, splitLine)
locationComplex = location.dropAxis()
self.cornerMaximum = euclidean.getMaximum(self.cornerMaximum, locationComplex + self.cornerRadius)
self.cornerMinimum = euclidean.getMinimum(self.cornerMinimum, locationComplex - self.cornerRadius)
self.oldLocation = location
class DistanceFeedRate:
'A class to limit the z feed rate and round values.'
def __init__(self):
'Initialize.'
self.isAlteration = False
self.decimalPlacesCarried = 3
self.output = cStringIO.StringIO()
def addFlowRateLine(self, flowRate):
'Add a flow rate line.'
self.output.write('M108 S%s\n' % euclidean.getFourSignificantFigures(flowRate))
def addGcodeFromFeedRateThreadZ(self, feedRateMinute, thread, travelFeedRateMinute, z):
'Add a thread to the output.'
if len(thread) > 0:
self.addGcodeMovementZWithFeedRate(travelFeedRateMinute, thread[0], z)
else:
print('zero length vertex positions array which was skipped over, this should never happen.')
if len(thread) < 2:
print('thread of only one point in addGcodeFromFeedRateThreadZ in gcodec, this should never happen.')
print(thread)
return
self.output.write('M101\n') # Turn extruder on.
for point in thread[1 :]:
self.addGcodeMovementZWithFeedRate(feedRateMinute, point, z)
self.output.write('M103\n') # Turn extruder off.
def addGcodeFromLoop(self, loop, z):
'Add the gcode loop.'
euclidean.addNestedRingBeginning(self, loop, z)
self.addPerimeterBlock(loop, z)
self.addLine('(</boundaryPerimeter>)')
self.addLine('(</nestedRing>)')
def addGcodeFromThreadZ(self, thread, z):
'Add a thread to the output.'
if len(thread) > 0:
self.addGcodeMovementZ(thread[0], z)
else:
print('zero length vertex positions array which was skipped over, this should never happen.')
if len(thread) < 2:
print('thread of only one point in addGcodeFromThreadZ in gcodec, this should never happen.')
print(thread)
return
self.output.write('M101\n') # Turn extruder on.
for point in thread[1 :]:
self.addGcodeMovementZ(point, z)
self.output.write('M103\n') # Turn extruder off.
def addGcodeMovementZ(self, point, z):
'Add a movement to the output.'
self.output.write(self.getLinearGcodeMovement(point, z) + '\n')
def addGcodeMovementZWithFeedRate(self, feedRateMinute, point, z):
'Add a movement to the output.'
self.output.write(self.getLinearGcodeMovementWithFeedRate(feedRateMinute, point, z) + '\n')
def addGcodeMovementZWithFeedRateVector3(self, feedRateMinute, vector3):
'Add a movement to the output by Vector3.'
xRounded = self.getRounded(vector3.x)
yRounded = self.getRounded(vector3.y)
self.output.write('G1 X%s Y%s Z%s F%s\n' % (xRounded, yRounded, self.getRounded(vector3.z), self.getRounded(feedRateMinute)))
def addLine(self, line):
'Add a line of text and a newline to the output.'
if len(line) > 0:
self.output.write(line + '\n')
def addLineCheckAlteration(self, line):
'Add a line of text and a newline to the output and check to see if it is an alteration line.'
firstWord = getFirstWord(getSplitLineBeforeBracketSemicolon(line))
if firstWord == '(<alteration>)':
self.isAlteration = True
elif firstWord == '(</alteration>)':
self.isAlteration = False
if len(line) > 0:
self.output.write(line + '\n')
def addLines(self, lines):
'Add lines of text to the output.'
addLinesToCString(self.output, lines)
def addLinesSetAbsoluteDistanceMode(self, lines):
'Add lines of text to the output and ensure the absolute mode is set.'
if len(lines) < 1:
return
if len(lines[0]) < 1:
return
absoluteDistanceMode = True
self.addLine('(<alteration>)')
for line in lines:
splitLine = getSplitLineBeforeBracketSemicolon(line)
firstWord = getFirstWord(splitLine)
if firstWord == 'G90':
absoluteDistanceMode = True
elif firstWord == 'G91':
absoluteDistanceMode = False
self.addLine('(<alterationDeleteThisPrefix/>)' + line)
if not absoluteDistanceMode:
self.addLine('G90')
self.addLine('(</alteration>)')
def addParameter(self, firstWord, parameter):
'Add the parameter.'
self.addLine(firstWord + ' S' + euclidean.getRoundedToThreePlaces(parameter))
def addPerimeterBlock(self, loop, z):
'Add the edge gcode block for the loop.'
if len(loop) < 2:
return
if euclidean.isWiddershins(loop): # Indicate that an edge is beginning.
self.addLine('(<edge> outer )')
else:
self.addLine('(<edge> inner )')
self.addGcodeFromThreadZ(loop + [loop[0]], z)
self.addLine('(</edge>)') # Indicate that an edge is beginning.
def addTagBracketedLine(self, tagName, value):
'Add a begin tag, value and end tag.'
self.addLine(getTagBracketedLine(tagName, value))
def addTagRoundedLine(self, tagName, value):
'Add a begin tag, rounded value and end tag.'
self.addLine('(<%s> %s </%s>)' % (tagName, self.getRounded(value), tagName))
def addTagBracketedProcedure(self, procedure):
'Add a begin procedure tag, procedure and end procedure tag.'
self.addLine(getTagBracketedProcedure(procedure))
def getBoundaryLine(self, location):
'Get boundary gcode line.'
return '(<boundaryPoint> X%s Y%s Z%s </boundaryPoint>)' % (self.getRounded(location.x), self.getRounded(location.y), self.getRounded(location.z))
def getFirstWordMovement(self, firstWord, location):
'Get the start of the arc line.'
return '%s X%s Y%s Z%s' % (firstWord, self.getRounded(location.x), self.getRounded(location.y), self.getRounded(location.z))
def getInfillBoundaryLine(self, location):
'Get infill boundary gcode line.'
return '(<infillPoint> X%s Y%s Z%s </infillPoint>)' % (self.getRounded(location.x), self.getRounded(location.y), self.getRounded(location.z))
def getIsAlteration(self, line):
'Determine if it is an alteration.'
if self.isAlteration:
self.addLineCheckAlteration(line)
return True
return False
def getLinearGcodeMovement(self, point, z):
'Get a linear gcode movement.'
return 'G1 X%s Y%s Z%s' % (self.getRounded(point.real), self.getRounded(point.imag), self.getRounded(z))
def getLinearGcodeMovementWithFeedRate(self, feedRateMinute, point, z):
'Get a z limited gcode movement.'
linearGcodeMovement = self.getLinearGcodeMovement(point, z)
if feedRateMinute == None:
return linearGcodeMovement
return linearGcodeMovement + ' F' + self.getRounded(feedRateMinute)
def getLineWithFeedRate(self, feedRateMinute, line, splitLine):
'Get the line with a feed rate.'
return getLineWithValueString('F', line, splitLine, self.getRounded(feedRateMinute))
def getLineWithX(self, line, splitLine, x):
'Get the line with an x.'
return getLineWithValueString('X', line, splitLine, self.getRounded(x))
def getLineWithY(self, line, splitLine, y):
'Get the line with a y.'
return getLineWithValueString('Y', line, splitLine, self.getRounded(y))
def getLineWithZ(self, line, splitLine, z):
'Get the line with a z.'
return getLineWithValueString('Z', line, splitLine, self.getRounded(z))
def getRounded(self, number):
'Get number rounded to the number of carried decimal places as a string.'
return euclidean.getRoundedToPlacesString(self.decimalPlacesCarried, number)
def parseSplitLine(self, firstWord, splitLine):
'Parse gcode split line and store the parameters.'
if firstWord == '(<decimalPlacesCarried>':
self.decimalPlacesCarried = int(splitLine[1])

View File

@ -0,0 +1,12 @@
"""
This page is in the table of contents.
This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
"""
import os
import sys
numberOfLevelsDeepInPackageHierarchy = 2
packageFilePath = os.path.abspath(__file__)
for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ):
packageFilePath = os.path.dirname( packageFilePath )
if packageFilePath not in sys.path:
sys.path.insert( 0, packageFilePath )

View File

@ -0,0 +1,12 @@
"""
This page is in the table of contents.
This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
"""
import os
import sys
numberOfLevelsDeepInPackageHierarchy = 3
packageFilePath = os.path.abspath(__file__)
for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ):
packageFilePath = os.path.dirname( packageFilePath )
if packageFilePath not in sys.path:
sys.path.insert( 0, packageFilePath )

View File

@ -0,0 +1,59 @@
"""
Drill negative solid.
"""
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 fabmetheus_utilities.geometry.creation import extrude
from fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.creation import solid
from fabmetheus_utilities.geometry.creation import teardrop
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.solids import triangle_mesh
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getGeometryOutput(derivation, elementNode):
"Get vector3 vertexes from attribute dictionary."
if derivation == None:
derivation = DrillDerivation(elementNode)
negatives = []
teardrop.addNegativesByRadius(elementNode, derivation.end, negatives, derivation.radius, derivation.start)
return solid.getGeometryOutputByManipulation(elementNode, negatives[0])
def getGeometryOutputByArguments(arguments, elementNode):
"Get vector3 vertexes from attribute dictionary by arguments."
evaluate.setAttributesByArguments(['radius', 'start', 'end'], arguments, elementNode)
return getGeometryOutput(None, elementNode)
def getNewDerivation(elementNode):
'Get new derivation.'
return DrillDerivation(elementNode)
def processElementNode(elementNode):
"Process the xml element."
solid.processElementNodeByGeometry(elementNode, getGeometryOutput(None, elementNode))
class DrillDerivation:
"Class to hold drill variables."
def __init__(self, elementNode):
'Set defaults.'
self.elementNode = elementNode
self.end = evaluate.getVector3ByPrefix(Vector3(0.0, 0.0, 1.0), elementNode, 'end')
self.start = evaluate.getVector3ByPrefix(Vector3(), elementNode, 'start')
self.radius = lineation.getFloatByPrefixBeginEnd(elementNode, 'radius', 'diameter', 1.0)
size = evaluate.getEvaluatedFloat(None, elementNode, 'size')
if size != None:
self.radius = 0.5 * size

View File

@ -0,0 +1,60 @@
"""
Svg reader.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.geometry_tools import path
from fabmetheus_utilities import euclidean
from fabmetheus_utilities import svg_reader
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Nophead <http://hydraraptor.blogspot.com/>\nArt of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/21/04 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getGeometryOutput(derivation, elementNode):
"Get vector3 vertexes from attribute dictionary."
if derivation == None:
derivation = SVGDerivation(elementNode)
return getGeometryOutputBySVGReader(elementNode, derivation.svgReader)
def getGeometryOutputByArguments(arguments, elementNode):
"Get vector3 vertexes from attribute dictionary by arguments."
derivation = SVGDerivation()
derivation.svgReader.parseSVG('', arguments[0])
return getGeometryOutput(derivation, elementNode)
def getGeometryOutputBySVGReader(elementNode, svgReader):
"Get vector3 vertexes from svgReader."
geometryOutput = []
for loopLayer in svgReader.loopLayers:
for loop in loopLayer.loops:
vector3Path = euclidean.getVector3Path(loop, loopLayer.z)
sideLoop = lineation.SideLoop(vector3Path)
sideLoop.rotate(elementNode)
geometryOutput += lineation.getGeometryOutputByManipulation(elementNode, sideLoop)
return geometryOutput
def getNewDerivation(elementNode):
'Get new derivation.'
return SVGDerivation(elementNode)
def processElementNode(elementNode):
"Process the xml element."
path.convertElementNode(elementNode, getGeometryOutput(None, elementNode))
class SVGDerivation:
"Class to hold svg variables."
def __init__(self, elementNode):
'Set defaults.'
self.svgReader = svg_reader.SVGReader()
self.svgReader.parseSVGByElementNode(elementNode)

View File

@ -0,0 +1,76 @@
"""
Polygon path.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.geometry_tools import path
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getGeometryOutput(derivation, elementNode):
"Get vector3 vertexes from attribute dictionary."
if derivation == None:
derivation = CircleDerivation(elementNode)
angleTotal = math.radians(derivation.start)
loop = []
sidesCeiling = int(math.ceil(abs(derivation.sides) * derivation.extent / 360.0))
sideAngle = math.radians(derivation.extent) / sidesCeiling
if derivation.sides < 0.0:
sideAngle = -sideAngle
spiral = lineation.Spiral(derivation.spiral, 0.5 * sideAngle / math.pi)
for side in xrange(sidesCeiling + 1):
unitPolar = euclidean.getWiddershinsUnitPolar(angleTotal)
x = unitPolar.real * derivation.radiusArealized.real
y = unitPolar.imag * derivation.radiusArealized.imag
vertex = spiral.getSpiralPoint(unitPolar, Vector3(x, y))
angleTotal += sideAngle
loop.append(vertex)
radiusMaximum = 0.000001 * max(derivation.radiusArealized.real, derivation.radiusArealized.imag)
loop = euclidean.getLoopWithoutCloseEnds(radiusMaximum, loop)
lineation.setClosedAttribute(elementNode, derivation.revolutions)
return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop(loop, sideAngle))
def getGeometryOutputByArguments(arguments, elementNode):
"Get vector3 vertexes from attribute dictionary by arguments."
evaluate.setAttributesByArguments(['radius', 'start', 'end', 'revolutions'], arguments, elementNode)
return getGeometryOutput(None, elementNode)
def getNewDerivation(elementNode):
'Get new derivation.'
return CircleDerivation(elementNode)
def processElementNode(elementNode):
"Process the xml element."
path.convertElementNode(elementNode, getGeometryOutput(None, elementNode))
class CircleDerivation:
"Class to hold circle variables."
def __init__(self, elementNode):
'Set defaults.'
self.radius = lineation.getRadiusComplex(elementNode, complex(1.0, 1.0))
self.sides = evaluate.getEvaluatedFloat(None, elementNode, 'sides')
if self.sides == None:
radiusMaximum = max(self.radius.real, self.radius.imag)
self.sides = evaluate.getSidesMinimumThreeBasedOnPrecisionSides(elementNode, radiusMaximum)
self.radiusArealized = evaluate.getRadiusArealizedBasedOnAreaRadius(elementNode, self.radius, self.sides)
self.start = evaluate.getEvaluatedFloat(0.0, elementNode, 'start')
end = evaluate.getEvaluatedFloat(360.0, elementNode, 'end')
self.revolutions = evaluate.getEvaluatedFloat(1.0, elementNode, 'revolutions')
self.extent = evaluate.getEvaluatedFloat(end - self.start, elementNode, 'extent')
self.extent += 360.0 * (self.revolutions - 1.0)
self.spiral = evaluate.getVector3ByPrefix(None, elementNode, 'spiral')

View File

@ -0,0 +1,54 @@
"""
Boolean geometry concatenation.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.geometry_tools import path
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getGeometryOutput(derivation, elementNode):
'Get triangle mesh from attribute dictionary.'
if derivation == None:
derivation = ConcatenateDerivation(elementNode)
concatenatedList = euclidean.getConcatenatedList(derivation.target)[:]
if len(concatenatedList) == 0:
print('Warning, in concatenate there are no paths.')
print(elementNode.attributes)
return None
if 'closed' not in elementNode.attributes:
elementNode.attributes['closed'] = 'true'
return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop(concatenatedList))
def getGeometryOutputByArguments(arguments, elementNode):
'Get triangle mesh from attribute dictionary by arguments.'
return getGeometryOutput(None, elementNode)
def getNewDerivation(elementNode):
'Get new derivation.'
return ConcatenateDerivation(elementNode)
def processElementNode(elementNode):
'Process the xml element.'
path.convertElementNode(elementNode, getGeometryOutput(None, elementNode))
class ConcatenateDerivation:
'Class to hold concatenate variables.'
def __init__(self, elementNode):
'Initialize.'
self.target = evaluate.getTransformedPathsByKey([], elementNode, 'target')

View File

@ -0,0 +1,441 @@
"""
Boolean geometry extrusion.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.creation import solid
from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.solids import triangle_mesh
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities.vector3index import Vector3Index
from fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def addLoop(derivation, endMultiplier, loopLists, path, portionDirectionIndex, portionDirections, vertexes):
'Add an indexed loop to the vertexes.'
portionDirection = portionDirections[ portionDirectionIndex ]
if portionDirection.directionReversed == True:
loopLists.append([])
loops = loopLists[-1]
interpolationOffset = derivation.interpolationDictionary['offset']
offset = interpolationOffset.getVector3ByPortion( portionDirection )
if endMultiplier != None:
if portionDirectionIndex == 0:
setOffsetByMultiplier( interpolationOffset.path[1], interpolationOffset.path[0], endMultiplier, offset )
elif portionDirectionIndex == len( portionDirections ) - 1:
setOffsetByMultiplier( interpolationOffset.path[-2], interpolationOffset.path[-1], endMultiplier, offset )
scale = derivation.interpolationDictionary['scale'].getComplexByPortion( portionDirection )
twist = derivation.interpolationDictionary['twist'].getYByPortion( portionDirection )
projectiveSpace = euclidean.ProjectiveSpace()
if derivation.tiltTop == None:
tilt = derivation.interpolationDictionary['tilt'].getComplexByPortion( portionDirection )
projectiveSpace = projectiveSpace.getByTilt( tilt )
else:
normals = getNormals( interpolationOffset, offset, portionDirection )
normalFirst = normals[0]
normalAverage = getNormalAverage(normals)
if derivation.tiltFollow and derivation.oldProjectiveSpace != None:
projectiveSpace = derivation.oldProjectiveSpace.getNextSpace( normalAverage )
else:
projectiveSpace = projectiveSpace.getByBasisZTop( normalAverage, derivation.tiltTop )
derivation.oldProjectiveSpace = projectiveSpace
projectiveSpace.unbuckle( derivation.maximumUnbuckling, normalFirst )
projectiveSpace = projectiveSpace.getSpaceByXYScaleAngle( twist, scale )
loop = []
if ( abs( projectiveSpace.basisX ) + abs( projectiveSpace.basisY ) ) < 0.0001:
vector3Index = Vector3Index(len(vertexes))
addOffsetAddToLists( loop, offset, vector3Index, vertexes )
loops.append(loop)
return
for point in path:
vector3Index = Vector3Index(len(vertexes))
projectedVertex = projectiveSpace.getVector3ByPoint(point)
vector3Index.setToVector3( projectedVertex )
addOffsetAddToLists( loop, offset, vector3Index, vertexes )
loops.append(loop)
def addNegatives(derivation, negatives, paths):
'Add pillars output to negatives.'
portionDirections = getSpacedPortionDirections(derivation.interpolationDictionary)
for path in paths:
loopLists = getLoopListsByPath(derivation, 1.000001, path, portionDirections)
geometryOutput = triangle_mesh.getPillarsOutput(loopLists)
negatives.append(geometryOutput)
def addNegativesPositives(derivation, negatives, paths, positives):
'Add pillars output to negatives and positives.'
portionDirections = getSpacedPortionDirections(derivation.interpolationDictionary)
for path in paths:
endMultiplier = None
if not euclidean.getIsWiddershinsByVector3(path):
endMultiplier = 1.000001
loopLists = getLoopListsByPath(derivation, endMultiplier, path, portionDirections)
geometryOutput = triangle_mesh.getPillarsOutput(loopLists)
if endMultiplier == None:
positives.append(geometryOutput)
else:
negatives.append(geometryOutput)
def addOffsetAddToLists(loop, offset, vector3Index, vertexes):
'Add an indexed loop to the vertexes.'
vector3Index += offset
loop.append(vector3Index)
vertexes.append(vector3Index)
def addPositives(derivation, paths, positives):
'Add pillars output to positives.'
portionDirections = getSpacedPortionDirections(derivation.interpolationDictionary)
for path in paths:
loopLists = getLoopListsByPath(derivation, None, path, portionDirections)
geometryOutput = triangle_mesh.getPillarsOutput(loopLists)
positives.append(geometryOutput)
def addSpacedPortionDirection( portionDirection, spacedPortionDirections ):
'Add spaced portion directions.'
lastSpacedPortionDirection = spacedPortionDirections[-1]
if portionDirection.portion - lastSpacedPortionDirection.portion > 0.003:
spacedPortionDirections.append( portionDirection )
return
if portionDirection.directionReversed > lastSpacedPortionDirection.directionReversed:
spacedPortionDirections.append( portionDirection )
def addTwistPortions( interpolationTwist, remainderPortionDirection, twistPrecision ):
'Add twist portions.'
lastPortionDirection = interpolationTwist.portionDirections[-1]
if remainderPortionDirection.portion == lastPortionDirection.portion:
return
lastTwist = interpolationTwist.getYByPortion( lastPortionDirection )
remainderTwist = interpolationTwist.getYByPortion( remainderPortionDirection )
twistSegments = int( math.floor( abs( remainderTwist - lastTwist ) / twistPrecision ) )
if twistSegments < 1:
return
portionDifference = remainderPortionDirection.portion - lastPortionDirection.portion
twistSegmentsPlusOne = float( twistSegments + 1 )
for twistSegment in xrange( twistSegments ):
additionalPortion = portionDifference * float( twistSegment + 1 ) / twistSegmentsPlusOne
portionDirection = PortionDirection( lastPortionDirection.portion + additionalPortion )
interpolationTwist.portionDirections.append( portionDirection )
def comparePortionDirection( portionDirection, otherPortionDirection ):
'Comparison in order to sort portion directions in ascending order of portion then direction.'
if portionDirection.portion > otherPortionDirection.portion:
return 1
if portionDirection.portion < otherPortionDirection.portion:
return - 1
if portionDirection.directionReversed < otherPortionDirection.directionReversed:
return - 1
return portionDirection.directionReversed > otherPortionDirection.directionReversed
def getGeometryOutput(derivation, elementNode):
'Get triangle mesh from attribute dictionary.'
if derivation == None:
derivation = ExtrudeDerivation(elementNode)
if len(euclidean.getConcatenatedList(derivation.target)) == 0:
print('Warning, in extrude there are no paths.')
print(elementNode.attributes)
return None
return getGeometryOutputByLoops(derivation, derivation.target)
def getGeometryOutputByArguments(arguments, elementNode):
'Get triangle mesh from attribute dictionary by arguments.'
return getGeometryOutput(None, elementNode)
def getGeometryOutputByLoops(derivation, loops):
'Get geometry output by sorted, nested loops.'
loops.sort(key=euclidean.getAreaVector3LoopAbsolute, reverse=True)
complexLoops = euclidean.getComplexPaths(loops)
nestedRings = []
for loopIndex, loop in enumerate(loops):
complexLoop = complexLoops[loopIndex]
leftPoint = euclidean.getLeftPoint(complexLoop)
isInFilledRegion = euclidean.getIsInFilledRegion(complexLoops[: loopIndex] + complexLoops[loopIndex + 1 :], leftPoint)
if isInFilledRegion == euclidean.isWiddershins(complexLoop):
loop.reverse()
nestedRing = euclidean.NestedRing()
nestedRing.boundary = complexLoop
nestedRing.vector3Loop = loop
nestedRings.append(nestedRing)
nestedRings = euclidean.getOrderedNestedRings(nestedRings)
nestedRings = euclidean.getFlattenedNestedRings(nestedRings)
portionDirections = getSpacedPortionDirections(derivation.interpolationDictionary)
if len(nestedRings) < 1:
return {}
if len(nestedRings) == 1:
geometryOutput = getGeometryOutputByNestedRing(derivation, nestedRings[0], portionDirections)
return solid.getGeometryOutputByManipulation(derivation.elementNode, geometryOutput)
shapes = []
for nestedRing in nestedRings:
shapes.append(getGeometryOutputByNestedRing(derivation, nestedRing, portionDirections))
return solid.getGeometryOutputByManipulation(derivation.elementNode, {'union' : {'shapes' : shapes}})
def getGeometryOutputByNegativesPositives(elementNode, negatives, positives):
'Get triangle mesh from elementNode, negatives and positives.'
positiveOutput = triangle_mesh.getUnifiedOutput(positives)
if len(negatives) < 1:
return solid.getGeometryOutputByManipulation(elementNode, positiveOutput)
if len(positives) < 1:
negativeOutput = triangle_mesh.getUnifiedOutput(negatives)
return solid.getGeometryOutputByManipulation(elementNode, negativeOutput)
return solid.getGeometryOutputByManipulation(elementNode, {'difference' : {'shapes' : [positiveOutput] + negatives}})
def getGeometryOutputByNestedRing(derivation, nestedRing, portionDirections):
'Get geometry output by sorted, nested loops.'
loopLists = getLoopListsByPath(derivation, None, nestedRing.vector3Loop, portionDirections)
outsideOutput = triangle_mesh.getPillarsOutput(loopLists)
if len(nestedRing.innerNestedRings) < 1:
return outsideOutput
shapes = [outsideOutput]
for nestedRing.innerNestedRing in nestedRing.innerNestedRings:
loopLists = getLoopListsByPath(derivation, 1.000001, nestedRing.innerNestedRing.vector3Loop, portionDirections)
shapes.append(triangle_mesh.getPillarsOutput(loopLists))
return {'difference' : {'shapes' : shapes}}
def getLoopListsByPath(derivation, endMultiplier, path, portionDirections):
'Get loop lists from path.'
vertexes = []
loopLists = [[]]
derivation.oldProjectiveSpace = None
for portionDirectionIndex in xrange(len(portionDirections)):
addLoop(derivation, endMultiplier, loopLists, path, portionDirectionIndex, portionDirections, vertexes)
return loopLists
def getNewDerivation(elementNode):
'Get new derivation.'
return ExtrudeDerivation(elementNode)
def getNormalAverage(normals):
'Get normal.'
if len(normals) < 2:
return normals[0]
return (normals[0] + normals[1]).getNormalized()
def getNormals( interpolationOffset, offset, portionDirection ):
'Get normals.'
normals = []
portionFrom = portionDirection.portion - 0.0001
portionTo = portionDirection.portion + 0.0001
if portionFrom >= 0.0:
normals.append( ( offset - interpolationOffset.getVector3ByPortion( PortionDirection( portionFrom ) ) ).getNormalized() )
if portionTo <= 1.0:
normals.append( ( interpolationOffset.getVector3ByPortion( PortionDirection( portionTo ) ) - offset ).getNormalized() )
return normals
def getSpacedPortionDirections( interpolationDictionary ):
'Get sorted portion directions.'
portionDirections = []
for interpolationDictionaryValue in interpolationDictionary.values():
portionDirections += interpolationDictionaryValue.portionDirections
portionDirections.sort( comparePortionDirection )
if len( portionDirections ) < 1:
return []
spacedPortionDirections = [ portionDirections[0] ]
for portionDirection in portionDirections[1 :]:
addSpacedPortionDirection( portionDirection, spacedPortionDirections )
return spacedPortionDirections
def insertTwistPortions(derivation, elementNode):
'Insert twist portions and radian the twist.'
interpolationDictionary = derivation.interpolationDictionary
interpolationTwist = Interpolation().getByPrefixX(elementNode, derivation.twistPathDefault, 'twist')
interpolationDictionary['twist'] = interpolationTwist
for point in interpolationTwist.path:
point.y = math.radians(point.y)
remainderPortionDirections = interpolationTwist.portionDirections[1 :]
interpolationTwist.portionDirections = [interpolationTwist.portionDirections[0]]
if elementNode != None:
twistPrecision = setting.getTwistPrecisionRadians(elementNode)
for remainderPortionDirection in remainderPortionDirections:
addTwistPortions(interpolationTwist, remainderPortionDirection, twistPrecision)
interpolationTwist.portionDirections.append(remainderPortionDirection)
def processElementNode(elementNode):
'Process the xml element.'
solid.processElementNodeByGeometry(elementNode, getGeometryOutput(None, elementNode))
def setElementNodeToEndStart(elementNode, end, start):
'Set elementNode attribute dictionary to a tilt following path from the start to end.'
elementNode.attributes['path'] = [start, end]
elementNode.attributes['tiltFollow'] = 'true'
elementNode.attributes['tiltTop'] = Vector3(0.0, 0.0, 1.0)
def setOffsetByMultiplier(begin, end, multiplier, offset):
'Set the offset by the multiplier.'
segment = end - begin
delta = segment * multiplier - segment
offset.setToVector3(offset + delta)
class ExtrudeDerivation:
'Class to hold extrude variables.'
def __init__(self, elementNode):
'Initialize.'
self.elementNode = elementNode
self.interpolationDictionary = {}
self.tiltFollow = evaluate.getEvaluatedBoolean(True, elementNode, 'tiltFollow')
self.tiltTop = evaluate.getVector3ByPrefix(None, elementNode, 'tiltTop')
self.maximumUnbuckling = evaluate.getEvaluatedFloat(5.0, elementNode, 'maximumUnbuckling')
scalePathDefault = [Vector3(1.0, 1.0, 0.0), Vector3(1.0, 1.0, 1.0)]
self.interpolationDictionary['scale'] = Interpolation().getByPrefixZ(elementNode, scalePathDefault, 'scale')
self.target = evaluate.getTransformedPathsByKey([], elementNode, 'target')
if self.tiltTop == None:
offsetPathDefault = [Vector3(), Vector3(0.0, 0.0, 1.0)]
self.interpolationDictionary['offset'] = Interpolation().getByPrefixZ(elementNode, offsetPathDefault, '')
tiltPathDefault = [Vector3(), Vector3(0.0, 0.0, 1.0)]
self.interpolationDictionary['tilt'] = Interpolation().getByPrefixZ(elementNode, tiltPathDefault, 'tilt')
for point in self.interpolationDictionary['tilt'].path:
point.x = math.radians(point.x)
point.y = math.radians(point.y)
else:
offsetAlongDefault = [Vector3(), Vector3(1.0, 0.0, 0.0)]
self.interpolationDictionary['offset'] = Interpolation().getByPrefixAlong(elementNode, offsetAlongDefault, '')
self.twist = evaluate.getEvaluatedFloat(0.0, elementNode, 'twist')
self.twistPathDefault = [Vector3(), Vector3(1.0, self.twist) ]
insertTwistPortions(self, elementNode)
class Interpolation:
'Class to interpolate a path.'
def __init__(self):
'Set index.'
self.interpolationIndex = 0
def __repr__(self):
'Get the string representation of this Interpolation.'
return str(self.__dict__)
def getByDistances(self):
'Get by distances.'
beginDistance = self.distances[0]
self.interpolationLength = self.distances[-1] - beginDistance
self.close = abs(0.000001 * self.interpolationLength)
self.portionDirections = []
oldDistance = -self.interpolationLength # so the difference should not be close
for distance in self.distances:
deltaDistance = distance - beginDistance
portionDirection = PortionDirection(deltaDistance / self.interpolationLength)
if abs(deltaDistance - oldDistance) < self.close:
portionDirection.directionReversed = True
self.portionDirections.append(portionDirection)
oldDistance = deltaDistance
return self
def getByPrefixAlong(self, elementNode, path, prefix):
'Get interpolation from prefix and xml element along the path.'
if len(path) < 2:
print('Warning, path is too small in evaluate in Interpolation.')
return
if elementNode == None:
self.path = path
else:
self.path = evaluate.getTransformedPathByPrefix(elementNode, path, prefix)
self.distances = [0.0]
previousPoint = self.path[0]
for point in self.path[1 :]:
distanceDifference = abs(point - previousPoint)
self.distances.append(self.distances[-1] + distanceDifference)
previousPoint = point
return self.getByDistances()
def getByPrefixX(self, elementNode, path, prefix):
'Get interpolation from prefix and xml element in the z direction.'
if len(path) < 2:
print('Warning, path is too small in evaluate in Interpolation.')
return
if elementNode == None:
self.path = path
else:
self.path = evaluate.getTransformedPathByPrefix(elementNode, path, prefix)
self.distances = []
for point in self.path:
self.distances.append(point.x)
return self.getByDistances()
def getByPrefixZ(self, elementNode, path, prefix):
'Get interpolation from prefix and xml element in the z direction.'
if len(path) < 2:
print('Warning, path is too small in evaluate in Interpolation.')
return
if elementNode == None:
self.path = path
else:
self.path = evaluate.getTransformedPathByPrefix(elementNode, path, prefix)
self.distances = []
for point in self.path:
self.distances.append(point.z)
return self.getByDistances()
def getComparison( self, first, second ):
'Compare the first with the second.'
if abs( second - first ) < self.close:
return 0
if second > first:
return 1
return - 1
def getComplexByPortion( self, portionDirection ):
'Get complex from z portion.'
self.setInterpolationIndexFromTo( portionDirection )
return self.oneMinusInnerPortion * self.startVertex.dropAxis() + self.innerPortion * self.endVertex.dropAxis()
def getInnerPortion(self):
'Get inner x portion.'
fromDistance = self.distances[ self.interpolationIndex ]
innerLength = self.distances[ self.interpolationIndex + 1 ] - fromDistance
if abs( innerLength ) == 0.0:
return 0.0
return ( self.absolutePortion - fromDistance ) / innerLength
def getVector3ByPortion( self, portionDirection ):
'Get vector3 from z portion.'
self.setInterpolationIndexFromTo( portionDirection )
return self.oneMinusInnerPortion * self.startVertex + self.innerPortion * self.endVertex
def getYByPortion( self, portionDirection ):
'Get y from x portion.'
self.setInterpolationIndexFromTo( portionDirection )
return self.oneMinusInnerPortion * self.startVertex.y + self.innerPortion * self.endVertex.y
def setInterpolationIndex( self, portionDirection ):
'Set the interpolation index.'
self.absolutePortion = self.distances[0] + self.interpolationLength * portionDirection.portion
interpolationIndexes = range( 0, len( self.distances ) - 1 )
if portionDirection.directionReversed:
interpolationIndexes.reverse()
for self.interpolationIndex in interpolationIndexes:
begin = self.distances[ self.interpolationIndex ]
end = self.distances[ self.interpolationIndex + 1 ]
if self.getComparison( begin, self.absolutePortion ) != self.getComparison( end, self.absolutePortion ):
return
def setInterpolationIndexFromTo( self, portionDirection ):
'Set the interpolation index, the start vertex and the end vertex.'
self.setInterpolationIndex( portionDirection )
self.innerPortion = self.getInnerPortion()
self.oneMinusInnerPortion = 1.0 - self.innerPortion
self.startVertex = self.path[ self.interpolationIndex ]
self.endVertex = self.path[ self.interpolationIndex + 1 ]
class PortionDirection:
'Class to hold a portion and direction.'
def __init__( self, portion ):
'Initialize.'
self.directionReversed = False
self.portion = portion
def __repr__(self):
'Get the string representation of this PortionDirection.'
return '%s: %s' % ( self.portion, self.directionReversed )

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,166 @@
"""
Grid path points.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.geometry_tools import path
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
import math
import random
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def addGridRow(diameter, gridPath, loopsComplex, maximumComplex, rowIndex, x, y, zigzag):
'Add grid row.'
row = []
while x < maximumComplex.real:
point = complex(x, y)
if euclidean.getIsInFilledRegion(loopsComplex, point):
row.append(point)
x += diameter.real
if zigzag and rowIndex % 2 == 1:
row.reverse()
gridPath += row
def getGeometryOutput(elementNode):
'Get vector3 vertexes from attribute dictionary.'
derivation = GridDerivation(elementNode)
diameter = derivation.radius + derivation.radius
typeStringTwoCharacters = derivation.typeString.lower()[: 2]
typeStringFirstCharacter = typeStringTwoCharacters[: 1]
topRight = complex(derivation.demiwidth, derivation.demiheight)
loopsComplex = [euclidean.getSquareLoopWiddershins(-topRight, topRight)]
if len(derivation.target) > 0:
loopsComplex = euclidean.getComplexPaths(derivation.target)
maximumComplex = euclidean.getMaximumByComplexPaths(loopsComplex)
minimumComplex = euclidean.getMinimumByComplexPaths(loopsComplex)
gridPath = None
if typeStringTwoCharacters == 'he':
gridPath = getHexagonalGrid(diameter, loopsComplex, maximumComplex, minimumComplex, derivation.zigzag)
elif typeStringTwoCharacters == 'ra' or typeStringFirstCharacter == 'a':
gridPath = getRandomGrid(derivation, diameter, elementNode, loopsComplex, maximumComplex, minimumComplex)
elif typeStringTwoCharacters == 're' or typeStringFirstCharacter == 'e':
gridPath = getRectangularGrid(diameter, loopsComplex, maximumComplex, minimumComplex, derivation.zigzag)
if gridPath == None:
print('Warning, the step type was not one of (hexagonal, random or rectangular) in getGeometryOutput in grid for:')
print(derivation.typeString)
print(elementNode)
return []
loop = euclidean.getVector3Path(gridPath)
elementNode.attributes['closed'] = 'false'
return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop(loop, 0.5 * math.pi))
def getGeometryOutputByArguments(arguments, elementNode):
'Get vector3 vertexes from attribute dictionary by arguments.'
if len(arguments) < 1:
return getGeometryOutput(elementNode)
inradius = 0.5 * euclidean.getFloatFromValue(arguments[0])
elementNode.attributes['inradius.x'] = str(inradius)
if len(arguments) > 1:
inradius = 0.5 * euclidean.getFloatFromValue(arguments[1])
elementNode.attributes['inradius.y'] = str(inradius)
return getGeometryOutput(elementNode)
def getHexagonalGrid(diameter, loopsComplex, maximumComplex, minimumComplex, zigzag):
'Get hexagonal grid.'
diameter = complex(diameter.real, math.sqrt(0.75) * diameter.imag)
demiradius = 0.25 * diameter
xRadius = 0.5 * diameter.real
xStart = minimumComplex.real - demiradius.real
y = minimumComplex.imag - demiradius.imag
gridPath = []
rowIndex = 0
while y < maximumComplex.imag:
x = xStart
if rowIndex % 2 == 1:
x -= xRadius
addGridRow(diameter, gridPath, loopsComplex, maximumComplex, rowIndex, x, y, zigzag)
y += diameter.imag
rowIndex += 1
return gridPath
def getIsPointInsideZoneAwayOthers(diameterReciprocal, loopsComplex, point, pixelDictionary):
'Determine if the point is inside the loops zone and and away from the other points.'
if not euclidean.getIsInFilledRegion(loopsComplex, point):
return False
pointOverDiameter = complex(point.real * diameterReciprocal.real, point.imag * diameterReciprocal.imag)
squareValues = euclidean.getSquareValuesFromPoint(pixelDictionary, pointOverDiameter)
for squareValue in squareValues:
if abs(squareValue - pointOverDiameter) < 1.0:
return False
euclidean.addElementToPixelListFromPoint(pointOverDiameter, pixelDictionary, pointOverDiameter)
return True
def getNewDerivation(elementNode):
'Get new derivation.'
return GridDerivation(elementNode)
def getRandomGrid(derivation, diameter, elementNode, loopsComplex, maximumComplex, minimumComplex):
'Get rectangular grid.'
gridPath = []
diameterReciprocal = complex(1.0 / diameter.real, 1.0 / diameter.imag)
diameterSquared = diameter.real * diameter.real + diameter.imag * diameter.imag
elements = int(math.ceil(derivation.density * euclidean.getAreaLoops(loopsComplex) / diameterSquared / math.sqrt(0.75)))
elements = evaluate.getEvaluatedInt(elements, elementNode, 'elements')
failedPlacementAttempts = 0
pixelDictionary = {}
if derivation.seed != None:
random.seed(derivation.seed)
successfulPlacementAttempts = 0
while failedPlacementAttempts < 100:
point = euclidean.getRandomComplex(minimumComplex, maximumComplex)
if getIsPointInsideZoneAwayOthers(diameterReciprocal, loopsComplex, point, pixelDictionary):
gridPath.append(point)
euclidean.addElementToPixelListFromPoint(point, pixelDictionary, point)
successfulPlacementAttempts += 1
else:
failedPlacementAttempts += 1
if successfulPlacementAttempts >= elements:
return gridPath
return gridPath
def getRectangularGrid(diameter, loopsComplex, maximumComplex, minimumComplex, zigzag):
'Get rectangular grid.'
demiradius = 0.25 * diameter
xStart = minimumComplex.real - demiradius.real
y = minimumComplex.imag - demiradius.imag
gridPath = []
rowIndex = 0
while y < maximumComplex.imag:
addGridRow(diameter, gridPath, loopsComplex, maximumComplex, rowIndex, xStart, y, zigzag)
y += diameter.imag
rowIndex += 1
return gridPath
def processElementNode(elementNode):
'Process the xml element.'
path.convertElementNode(elementNode, getGeometryOutput(elementNode))
class GridDerivation:
'Class to hold grid variables.'
def __init__(self, elementNode):
'Set defaults.'
self.inradius = lineation.getInradius(complex(10.0, 10.0), elementNode)
self.demiwidth = lineation.getFloatByPrefixBeginEnd(elementNode, 'demiwidth', 'width', self.inradius.real)
self.demiheight = lineation.getFloatByPrefixBeginEnd(elementNode, 'demiheight', 'height', self.inradius.imag)
self.density = evaluate.getEvaluatedFloat(0.2, elementNode, 'density')
self.radius = lineation.getComplexByPrefixBeginEnd(elementNode, 'elementRadius', 'elementDiameter', complex(1.0, 1.0))
self.radius = lineation.getComplexByPrefixBeginEnd(elementNode, 'radius', 'diameter', self.radius)
self.seed = evaluate.getEvaluatedInt(None, elementNode, 'seed')
self.target = evaluate.getTransformedPathsByKey([], elementNode, 'target')
self.typeMenuRadioStrings = 'hexagonal random rectangular'.split()
self.typeString = evaluate.getEvaluatedString('rectangular', elementNode, 'type')
self.zigzag = evaluate.getEvaluatedBoolean(True, elementNode, 'zigzag')

View File

@ -0,0 +1,208 @@
"""
Heightmap.
http://www.cs.otago.ac.nz/graphics/Mirage/node59.html
http://en.wikipedia.org/wiki/Heightmap
http://en.wikipedia.org/wiki/Netpbm_format
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.creation import solid
from fabmetheus_utilities.geometry.geometry_tools import path
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.solids import triangle_mesh
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities.vector3index import Vector3Index
from fabmetheus_utilities import archive
from fabmetheus_utilities import euclidean
import math
import random
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def addHeightsByBitmap(heights, textLines):
'Add heights by bitmap.'
for line in textLines[3:]:
for integerWord in line.split():
heights.append(float(integerWord))
def addHeightsByGraymap(heights, textLines):
'Add heights by graymap.'
divisor = float(textLines[3])
for line in textLines[4:]:
for integerWord in line.split():
heights.append(float(integerWord) / divisor)
def getAddIndexedHeightGrid(heightGrid, minimumXY, step, top, vertexes):
'Get and add an indexed heightGrid.'
indexedHeightGrid = []
for rowIndex, row in enumerate(heightGrid):
indexedRow = []
indexedHeightGrid.append(indexedRow)
rowOffset = step.imag * float(rowIndex) + minimumXY.imag
for columnIndex, element in enumerate(row):
columnOffset = step.real * float(columnIndex) + minimumXY.real
vector3index = Vector3Index(len(vertexes), columnOffset, rowOffset, top * element)
indexedRow.append(vector3index)
vertexes.append(vector3index)
return indexedHeightGrid
def getAddIndexedSegmentedPerimeter(heightGrid, maximumXY, minimumXY, step, vertexes, z=0.0):
'Get and add an indexed segmented perimeter.'
indexedSegmentedPerimeter = []
firstRow = heightGrid[0]
columnOffset = minimumXY.real
numberOfRowsMinusTwo = len(heightGrid) - 2
for column in firstRow:
vector3index = Vector3Index(len(vertexes), columnOffset, minimumXY.imag, z)
vertexes.append(vector3index)
indexedSegmentedPerimeter.append(vector3index)
columnOffset += step.real
rowOffset = minimumXY.imag
for rowIndex in xrange(numberOfRowsMinusTwo):
rowOffset += step.imag
vector3index = Vector3Index(len(vertexes), maximumXY.real, rowOffset, z)
vertexes.append(vector3index)
indexedSegmentedPerimeter.append(vector3index)
columnOffset = maximumXY.real
for column in firstRow:
vector3index = Vector3Index(len(vertexes), columnOffset, maximumXY.imag, z)
vertexes.append(vector3index)
indexedSegmentedPerimeter.append(vector3index)
columnOffset -= step.real
rowOffset = maximumXY.imag
for rowIndex in xrange(numberOfRowsMinusTwo):
rowOffset -= step.imag
vector3index = Vector3Index(len(vertexes), minimumXY.real, rowOffset, z)
vertexes.append(vector3index)
indexedSegmentedPerimeter.append(vector3index)
return indexedSegmentedPerimeter
def getGeometryOutput(elementNode):
'Get vector3 vertexes from attribute dictionary.'
derivation = HeightmapDerivation(elementNode)
heightGrid = derivation.heightGrid
if derivation.fileName != '':
heightGrid = getHeightGrid(archive.getAbsoluteFolderPath(elementNode.getOwnerDocument().fileName, derivation.fileName))
return getGeometryOutputByHeightGrid(derivation, elementNode, heightGrid)
def getGeometryOutputByArguments(arguments, elementNode):
'Get vector3 vertexes from attribute dictionary by arguments.'
evaluate.setAttributesByArguments(['file', 'start'], arguments, elementNode)
return getGeometryOutput(elementNode)
def getGeometryOutputByHeightGrid(derivation, elementNode, heightGrid):
'Get vector3 vertexes from attribute dictionary.'
numberOfColumns = len(heightGrid)
if numberOfColumns < 2:
print('Warning, in getGeometryOutputByHeightGrid in heightmap there are fewer than two rows for:')
print(heightGrid)
print(elementNode)
return None
numberOfRows = len(heightGrid[0])
if numberOfRows < 2:
print('Warning, in getGeometryOutputByHeightGrid in heightmap there are fewer than two columns for:')
print(heightGrid)
print(elementNode)
return None
for row in heightGrid:
if len(row) != numberOfRows:
print('Warning, in getGeometryOutputByHeightGrid in heightmap the heightgrid is not rectangular for:')
print(heightGrid)
print(elementNode)
return None
inradiusComplex = derivation.inradius.dropAxis()
minimumXY = -inradiusComplex
step = complex(derivation.inradius.x / float(numberOfRows - 1), derivation.inradius.y / float(numberOfColumns - 1))
step += step
faces = []
heightGrid = getRaisedHeightGrid(heightGrid, derivation.start)
top = derivation.inradius.z + derivation.inradius.z
vertexes = []
indexedBottomLoop = getAddIndexedSegmentedPerimeter(heightGrid, inradiusComplex, minimumXY, step, vertexes)
indexedLoops = [indexedBottomLoop]
indexedGridTop = getAddIndexedHeightGrid(heightGrid, minimumXY, step, top, vertexes)
indexedLoops.append(triangle_mesh.getIndexedLoopFromIndexedGrid(indexedGridTop))
vertexes = triangle_mesh.getUniqueVertexes(indexedLoops + indexedGridTop)
triangle_mesh.addPillarFromConvexLoopsGridTop(faces, indexedGridTop, indexedLoops)
return triangle_mesh.getGeometryOutputByFacesVertexes(faces, vertexes)
def getHeightGrid(fileName):
'Get heightGrid by fileName.'
if 'models/' not in fileName:
print('Warning, models/ was not in the absolute file path, so for security nothing will be done for:')
print(fileName)
print('The heightmap tool can only read a file which has models/ in the file path.')
print('To import the file, move the file into a folder called model/ or a subfolder which is inside the model folder tree.')
return
pgmText = archive.getFileText(fileName)
textLines = archive.getTextLines(pgmText)
format = textLines[0].lower()
sizeWords = textLines[2].split()
numberOfColumns = int(sizeWords[0])
numberOfRows = int(sizeWords[1])
heights = []
if format == 'p1':
addHeightsByBitmap(heights, textLines)
elif format == 'p2':
addHeightsByGraymap(heights, textLines)
else:
print('Warning, the file format was not recognized for:')
print(fileName)
print('Heightmap can only read the Netpbm Portable bitmap format and the Netpbm Portable graymap format.')
print('The Netpbm formats are described at:')
print('http://en.wikipedia.org/wiki/Netpbm_format')
return []
heightGrid = []
heightIndex = 0
for rowIndex in xrange(numberOfRows):
row = []
heightGrid.append(row)
for columnIndex in xrange(numberOfColumns):
row.append(heights[heightIndex])
heightIndex += 1
return heightGrid
def getNewDerivation(elementNode):
'Get new derivation.'
return HeightmapDerivation(elementNode)
def getRaisedHeightGrid(heightGrid, start):
'Get heightGrid raised above start.'
raisedHeightGrid = []
remainingHeight = 1.0 - start
for row in heightGrid:
raisedRow = []
raisedHeightGrid.append(raisedRow)
for element in row:
raisedElement = remainingHeight * element + start
raisedRow.append(raisedElement)
return raisedHeightGrid
def processElementNode(elementNode):
'Process the xml element.'
solid.processElementNodeByGeometry(elementNode, getGeometryOutput(elementNode))
class HeightmapDerivation:
'Class to hold heightmap variables.'
def __init__(self, elementNode):
'Set defaults.'
self.fileName = evaluate.getEvaluatedString('', elementNode, 'file')
self.heightGrid = evaluate.getEvaluatedValue([], elementNode, 'heightGrid')
self.inradius = evaluate.getVector3ByPrefixes(elementNode, ['demisize', 'inradius'], Vector3(10.0, 10.0, 5.0))
self.inradius = evaluate.getVector3ByMultiplierPrefix(elementNode, 2.0, 'size', self.inradius)
self.start = evaluate.getEvaluatedFloat(0.0, elementNode, 'start')
def __repr__(self):
'Get the string representation of this HeightmapDerivation.'
return euclidean.getDictionaryString(self.__dict__)

View File

@ -0,0 +1,176 @@
"""
Boolean geometry extrusion.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.creation import solid
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.solids import triangle_mesh
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def addLoopByComplex(derivation, endMultiplier, loopLists, path, pointComplex, vertexes):
"Add an indexed loop to the vertexes."
loops = loopLists[-1]
loop = []
loops.append(loop)
for point in path:
pointMinusBegin = point - derivation.axisStart
dotVector3 = derivation.axisProjectiveSpace.getDotVector3(pointMinusBegin)
dotVector3Complex = dotVector3.dropAxis()
dotPointComplex = pointComplex * dotVector3Complex
dotPoint = Vector3(dotPointComplex.real, dotPointComplex.imag, dotVector3.z)
projectedVector3 = derivation.axisProjectiveSpace.getVector3ByPoint(dotPoint) + derivation.axisStart
loop.append(projectedVector3)
def addNegatives(derivation, negatives, paths):
"Add pillars output to negatives."
for path in paths:
loopListsByPath = getLoopListsByPath(derivation, 1.000001, path)
geometryOutput = triangle_mesh.getPillarsOutput(loopListsByPath)
negatives.append(geometryOutput)
def addNegativesPositives(derivation, negatives, paths, positives):
"Add pillars output to negatives and positives."
for path in paths:
endMultiplier = None
normal = euclidean.getNormalByPath(path)
if normal.dot(derivation.normal) < 0.0:
endMultiplier = 1.000001
loopListsByPath = getLoopListsByPath(derivation, endMultiplier, path)
geometryOutput = triangle_mesh.getPillarsOutput(loopListsByPath)
if endMultiplier == None:
positives.append(geometryOutput)
else:
negatives.append(geometryOutput)
def addOffsetAddToLists( loop, offset, vector3Index, vertexes ):
"Add an indexed loop to the vertexes."
vector3Index += offset
loop.append( vector3Index )
vertexes.append( vector3Index )
def addPositives(derivation, paths, positives):
"Add pillars output to positives."
for path in paths:
loopListsByPath = getLoopListsByPath(derivation, None, path)
geometryOutput = triangle_mesh.getPillarsOutput(loopListsByPath)
positives.append(geometryOutput)
def getGeometryOutput(derivation, elementNode):
"Get triangle mesh from attribute dictionary."
if derivation == None:
derivation = LatheDerivation(elementNode)
if len(euclidean.getConcatenatedList(derivation.target)) == 0:
print('Warning, in lathe there are no paths.')
print(elementNode.attributes)
return None
negatives = []
positives = []
addNegativesPositives(derivation, negatives, derivation.target, positives)
return getGeometryOutputByNegativesPositives(derivation, elementNode, negatives, positives)
def getGeometryOutputByArguments(arguments, elementNode):
"Get triangle mesh from attribute dictionary by arguments."
return getGeometryOutput(None, elementNode)
def getGeometryOutputByNegativesPositives(derivation, elementNode, negatives, positives):
"Get triangle mesh from derivation, elementNode, negatives and positives."
positiveOutput = triangle_mesh.getUnifiedOutput(positives)
if len(negatives) < 1:
return solid.getGeometryOutputByManipulation(elementNode, positiveOutput)
return solid.getGeometryOutputByManipulation(elementNode, {'difference' : {'shapes' : [positiveOutput] + negatives}})
def getLoopListsByPath(derivation, endMultiplier, path):
"Get loop lists from path."
vertexes = []
loopLists = [[]]
if len(derivation.loop) < 2:
return loopLists
for pointIndex, pointComplex in enumerate(derivation.loop):
if endMultiplier != None and not derivation.isEndCloseToStart:
if pointIndex == 0:
nextPoint = derivation.loop[1]
pointComplex = endMultiplier * (pointComplex - nextPoint) + nextPoint
elif pointIndex == len(derivation.loop) - 1:
previousPoint = derivation.loop[pointIndex - 1]
pointComplex = endMultiplier * (pointComplex - previousPoint) + previousPoint
addLoopByComplex(derivation, endMultiplier, loopLists, path, pointComplex, vertexes)
if derivation.isEndCloseToStart:
loopLists[-1].append([])
return loopLists
def getNewDerivation(elementNode):
'Get new derivation.'
return LatheDerivation(elementNode)
def processElementNode(elementNode):
"Process the xml element."
solid.processElementNodeByGeometry(elementNode, getGeometryOutput(None, elementNode))
class LatheDerivation:
"Class to hold lathe variables."
def __init__(self, elementNode):
'Set defaults.'
self.axisEnd = evaluate.getVector3ByPrefix(None, elementNode, 'axisEnd')
self.axisStart = evaluate.getVector3ByPrefix(None, elementNode, 'axisStart')
self.end = evaluate.getEvaluatedFloat(360.0, elementNode, 'end')
self.loop = evaluate.getTransformedPathByKey([], elementNode, 'loop')
self.sides = evaluate.getEvaluatedInt(None, elementNode, 'sides')
self.start = evaluate.getEvaluatedFloat(0.0, elementNode, 'start')
self.target = evaluate.getTransformedPathsByKey([], elementNode, 'target')
if len(self.target) < 1:
print('Warning, no target in derive in lathe for:')
print(elementNode)
return
firstPath = self.target[0]
if len(firstPath) < 3:
print('Warning, firstPath length is less than three in derive in lathe for:')
print(elementNode)
self.target = []
return
if self.axisStart == None:
if self.axisEnd == None:
self.axisStart = firstPath[0]
self.axisEnd = firstPath[-1]
else:
self.axisStart = Vector3()
self.axis = self.axisEnd - self.axisStart
axisLength = abs(self.axis)
if axisLength <= 0.0:
print('Warning, axisLength is zero in derive in lathe for:')
print(elementNode)
self.target = []
return
self.axis /= axisLength
firstVector3 = firstPath[1] - self.axisStart
firstVector3Length = abs(firstVector3)
if firstVector3Length <= 0.0:
print('Warning, firstVector3Length is zero in derive in lathe for:')
print(elementNode)
self.target = []
return
firstVector3 /= firstVector3Length
self.axisProjectiveSpace = euclidean.ProjectiveSpace().getByBasisZFirst(self.axis, firstVector3)
if self.sides == None:
distanceToLine = euclidean.getDistanceToLineByPaths(self.axisStart, self.axisEnd, self.target)
self.sides = evaluate.getSidesMinimumThreeBasedOnPrecisionSides(elementNode, distanceToLine)
endRadian = math.radians(self.end)
startRadian = math.radians(self.start)
self.isEndCloseToStart = euclidean.getIsRadianClose(endRadian, startRadian)
if len(self.loop) < 1:
self.loop = euclidean.getComplexPolygonByStartEnd(endRadian, 1.0, self.sides, startRadian)
self.normal = euclidean.getNormalByPath(firstPath)

View File

@ -0,0 +1,104 @@
"""
Square path.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.geometry_tools import path
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getGeometryOutput(derivation, elementNode):
"Get vector3 vertexes from attribute dictionary."
if derivation == None:
derivation = LineDerivation(elementNode)
endMinusStart = derivation.end - derivation.start
endMinusStartLength = abs(endMinusStart)
if endMinusStartLength <= 0.0:
print('Warning, end is the same as start in getGeometryOutput in line for:')
print(derivation.start)
print(derivation.end)
print(elementNode)
return None
typeStringTwoCharacters = derivation.typeString.lower()[: 2]
elementNode.attributes['closed'] = str(derivation.closed)
if derivation.step == None and derivation.steps == None:
return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop([derivation.start, derivation.end]))
loop = [derivation.start]
if derivation.step != None and derivation.steps != None:
stepVector = derivation.step / endMinusStartLength * endMinusStart
derivation.end = derivation.start + stepVector * derivation.steps
return getGeometryOutputByStep(elementNode, derivation.end, loop, derivation.steps, stepVector)
if derivation.step == None:
stepVector = endMinusStart / derivation.steps
return getGeometryOutputByStep(elementNode, derivation.end, loop, derivation.steps, stepVector)
endMinusStartLengthOverStep = endMinusStartLength / derivation.step
if typeStringTwoCharacters == 'av':
derivation.steps = max(1.0, round(endMinusStartLengthOverStep))
stepVector = derivation.step / endMinusStartLength * endMinusStart
derivation.end = derivation.start + stepVector * derivation.steps
return getGeometryOutputByStep(elementNode, derivation.end, loop, derivation.steps, stepVector)
if typeStringTwoCharacters == 'ma':
derivation.steps = math.ceil(endMinusStartLengthOverStep)
if derivation.steps < 1.0:
return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop([derivation.start, derivation.end]))
stepVector = endMinusStart / derivation.steps
return getGeometryOutputByStep(elementNode, derivation.end, loop, derivation.steps, stepVector)
if typeStringTwoCharacters == 'mi':
derivation.steps = math.floor(endMinusStartLengthOverStep)
if derivation.steps < 1.0:
return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop(loop))
stepVector = endMinusStart / derivation.steps
return getGeometryOutputByStep(elementNode, derivation.end, loop, derivation.steps, stepVector)
print('Warning, the step type was not one of (average, maximum or minimum) in getGeometryOutput in line for:')
print(derivation.typeString)
print(elementNode)
loop.append(derivation.end)
return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop(loop))
def getGeometryOutputByArguments(arguments, elementNode):
"Get vector3 vertexes from attribute dictionary by arguments."
evaluate.setAttributesByArguments(['start', 'end', 'step'], arguments, elementNode)
return getGeometryOutput(None, elementNode)
def getGeometryOutputByStep(elementNode, end, loop, steps, stepVector):
"Get line geometry output by the end, loop, steps and stepVector."
stepsFloor = int(math.floor(abs(steps)))
for stepIndex in xrange(1, stepsFloor):
loop.append(loop[stepIndex - 1] + stepVector)
loop.append(end)
return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop(loop))
def getNewDerivation(elementNode):
'Get new derivation.'
return LineDerivation(elementNode)
def processElementNode(elementNode):
"Process the xml element."
path.convertElementNode(elementNode, getGeometryOutput(None, elementNode))
class LineDerivation:
"Class to hold line variables."
def __init__(self, elementNode):
'Set defaults.'
self.closed = evaluate.getEvaluatedBoolean(False, elementNode, 'closed')
self.end = evaluate.getVector3ByPrefix(Vector3(), elementNode, 'end')
self.start = evaluate.getVector3ByPrefix(Vector3(), elementNode, 'start')
self.step = evaluate.getEvaluatedFloat(None, elementNode, 'step')
self.steps = evaluate.getEvaluatedFloat(None, elementNode, 'steps')
self.typeMenuRadioStrings = 'average maximum minimum'.split()
self.typeString = evaluate.getEvaluatedString('minimum', elementNode, 'type')

View File

@ -0,0 +1,247 @@
"""
Linear bearing cage.
"""
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 fabmetheus_utilities.geometry.creation import extrude
from fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.creation import peg
from fabmetheus_utilities.geometry.creation import solid
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities.geometry.manipulation_matrix import translate
from fabmetheus_utilities.geometry.solids import cylinder
from fabmetheus_utilities.geometry.solids import sphere
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def addAssemblyCage(derivation, negatives, positives):
'Add assembly linear bearing cage.'
addCageGroove(derivation, negatives, positives)
for pegCenterX in derivation.pegCenterXs:
addPositivePeg(derivation, positives, pegCenterX, -derivation.pegY)
addPositivePeg(derivation, positives, pegCenterX, derivation.pegY)
translate.translateNegativesPositives(negatives, positives, Vector3(0.0, -derivation.halfSeparationWidth))
femaleNegatives = []
femalePositives = []
addCageGroove(derivation, femaleNegatives, femalePositives)
for pegCenterX in derivation.pegCenterXs:
addNegativePeg(derivation, femaleNegatives, pegCenterX, -derivation.pegY)
addNegativePeg(derivation, femaleNegatives, pegCenterX, derivation.pegY)
translate.translateNegativesPositives(femaleNegatives, femalePositives, Vector3(0.0, derivation.halfSeparationWidth))
negatives += femaleNegatives
positives += femalePositives
def addCage(derivation, height, negatives, positives):
'Add linear bearing cage.'
copyShallow = derivation.elementNode.getCopyShallow()
copyShallow.attributes['path'] = [Vector3(), Vector3(0.0, 0.0, height)]
extrudeDerivation = extrude.ExtrudeDerivation(copyShallow)
roundedExtendedRectangle = getRoundedExtendedRectangle(derivation.demiwidth, derivation.rectangleCenterX, 14)
outsidePath = euclidean.getVector3Path(roundedExtendedRectangle)
extrude.addPositives(extrudeDerivation, [outsidePath], positives)
for bearingCenterX in derivation.bearingCenterXs:
addNegativeSphere(derivation, negatives, bearingCenterX)
def addCageGroove(derivation, negatives, positives):
'Add cage and groove.'
addCage(derivation, derivation.demiheight, negatives, positives)
addGroove(derivation, negatives)
def addGroove(derivation, negatives):
'Add groove on each side of cage.'
copyShallow = derivation.elementNode.getCopyShallow()
extrude.setElementNodeToEndStart(copyShallow, Vector3(-derivation.demilength), Vector3(derivation.demilength))
extrudeDerivation = extrude.ExtrudeDerivation(copyShallow)
bottom = derivation.demiheight - 0.5 * derivation.grooveWidth
outside = derivation.demiwidth
top = derivation.demiheight
leftGroove = [
complex(-outside, bottom),
complex(-derivation.innerDemiwidth, derivation.demiheight),
complex(-outside, top)]
rightGroove = [
complex(outside, top),
complex(derivation.innerDemiwidth, derivation.demiheight),
complex(outside, bottom)]
extrude.addNegatives(extrudeDerivation, negatives, euclidean.getVector3Paths([leftGroove, rightGroove]))
def addNegativePeg(derivation, negatives, x, y):
'Add negative cylinder at x and y.'
negativePegRadius = derivation.pegRadiusArealized + derivation.halfPegClearance
inradius = complex(negativePegRadius, negativePegRadius)
copyShallow = derivation.elementNode.getCopyShallow()
start = Vector3(x, y, derivation.height)
sides = evaluate.getSidesMinimumThreeBasedOnPrecision(copyShallow, negativePegRadius)
cylinder.addCylinderOutputByEndStart(0.0, inradius, negatives, sides, start, derivation.topOverBottom)
def addNegativeSphere(derivation, negatives, x):
'Add negative sphere at x.'
radius = Vector3(derivation.radiusPlusClearance, derivation.radiusPlusClearance, derivation.radiusPlusClearance)
sphereOutput = sphere.getGeometryOutput(derivation.elementNode.getCopyShallow(), radius)
euclidean.translateVector3Path(matrix.getVertexes(sphereOutput), Vector3(x, 0.0, derivation.demiheight))
negatives.append(sphereOutput)
def addPositivePeg(derivation, positives, x, y):
'Add positive cylinder at x and y.'
positivePegRadius = derivation.pegRadiusArealized - derivation.halfPegClearance
radiusArealized = complex(positivePegRadius, positivePegRadius)
copyShallow = derivation.elementNode.getCopyShallow()
start = Vector3(x, y, derivation.demiheight)
endZ = derivation.height
peg.addPegOutput(derivation.pegBevel, endZ, positives, radiusArealized, derivation.sides, start, derivation.topOverBottom)
def getBearingCenterXs(bearingCenterX, numberOfSteps, stepX):
'Get the bearing center x list.'
bearingCenterXs = []
for stepIndex in xrange(numberOfSteps + 1):
bearingCenterXs.append(bearingCenterX)
bearingCenterX += stepX
return bearingCenterXs
def getGeometryOutput(elementNode):
'Get vector3 vertexes from attribute dictionary.'
derivation = LinearBearingCageDerivation(elementNode)
negatives = []
positives = []
if derivation.typeStringFirstCharacter == 'a':
addAssemblyCage(derivation, negatives, positives)
else:
addCage(derivation, derivation.height, negatives, positives)
return extrude.getGeometryOutputByNegativesPositives(elementNode, negatives, positives)
def getGeometryOutputByArguments(arguments, elementNode):
'Get vector3 vertexes from attribute dictionary by arguments.'
evaluate.setAttributesByArguments(['length', 'radius'], arguments, elementNode)
return getGeometryOutput(elementNode)
def getNewDerivation(elementNode):
'Get new derivation.'
return LinearBearingCageDerivation(elementNode)
def getPegCenterXs(numberOfSteps, pegCenterX, stepX):
'Get the peg center x list.'
pegCenterXs = []
for stepIndex in xrange(numberOfSteps):
pegCenterXs.append(pegCenterX)
pegCenterX += stepX
return pegCenterXs
def getRoundedExtendedRectangle(radius, rectangleCenterX, sides):
'Get the rounded extended rectangle.'
roundedExtendedRectangle = []
halfSides = int(sides / 2)
halfSidesPlusOne = abs(halfSides + 1)
sideAngle = math.pi / float(halfSides)
extensionMultiplier = 1.0 / math.cos(0.5 * sideAngle)
center = complex(rectangleCenterX, 0.0)
startAngle = 0.5 * math.pi
for halfSide in xrange(halfSidesPlusOne):
unitPolar = euclidean.getWiddershinsUnitPolar(startAngle)
unitPolarExtended = complex(unitPolar.real * extensionMultiplier, unitPolar.imag)
roundedExtendedRectangle.append(unitPolarExtended * radius + center)
startAngle += sideAngle
center = complex(-rectangleCenterX, 0.0)
startAngle = -0.5 * math.pi
for halfSide in xrange(halfSidesPlusOne):
unitPolar = euclidean.getWiddershinsUnitPolar(startAngle)
unitPolarExtended = complex(unitPolar.real * extensionMultiplier, unitPolar.imag)
roundedExtendedRectangle.append(unitPolarExtended * radius + center)
startAngle += sideAngle
return roundedExtendedRectangle
def processElementNode(elementNode):
'Process the xml element.'
solid.processElementNodeByGeometry(elementNode, getGeometryOutput(elementNode))
class LinearBearingCageDerivation:
'Class to hold linear bearing cage variables.'
def __init__(self, elementNode):
'Set defaults.'
self.length = evaluate.getEvaluatedFloat(50.0, elementNode, 'length')
self.demilength = 0.5 * self.length
self.elementNode = elementNode
self.radius = lineation.getFloatByPrefixBeginEnd(elementNode, 'radius', 'diameter', 5.0)
self.cageClearanceOverRadius = evaluate.getEvaluatedFloat(0.05, elementNode, 'cageClearanceOverRadius')
self.cageClearance = self.cageClearanceOverRadius * self.radius
self.cageClearance = evaluate.getEvaluatedFloat(self.cageClearance, elementNode, 'cageClearance')
self.racewayClearanceOverRadius = evaluate.getEvaluatedFloat(0.1, elementNode, 'racewayClearanceOverRadius')
self.racewayClearance = self.racewayClearanceOverRadius * self.radius
self.racewayClearance = evaluate.getEvaluatedFloat(self.racewayClearance, elementNode, 'racewayClearance')
self.typeMenuRadioStrings = 'assembly integral'.split()
self.typeString = evaluate.getEvaluatedString('assembly', elementNode, 'type')
self.typeStringFirstCharacter = self.typeString[: 1 ].lower()
self.wallThicknessOverRadius = evaluate.getEvaluatedFloat(0.5, elementNode, 'wallThicknessOverRadius')
self.wallThickness = self.wallThicknessOverRadius * self.radius
self.wallThickness = evaluate.getEvaluatedFloat(self.wallThickness, elementNode, 'wallThickness')
self.zenithAngle = evaluate.getEvaluatedFloat(45.0, elementNode, 'zenithAngle')
self.zenithRadian = math.radians(self.zenithAngle)
self.demiheight = self.radius * math.cos(self.zenithRadian) - self.racewayClearance
self.height = self.demiheight + self.demiheight
self.radiusPlusClearance = self.radius + self.cageClearance
self.cageRadius = self.radiusPlusClearance + self.wallThickness
self.demiwidth = self.cageRadius
self.bearingCenterX = self.cageRadius - self.demilength
separation = self.cageRadius + self.radiusPlusClearance
bearingLength = -self.bearingCenterX - self.bearingCenterX
self.numberOfSteps = int(math.floor(bearingLength / separation))
self.stepX = bearingLength / float(self.numberOfSteps)
self.bearingCenterXs = getBearingCenterXs(self.bearingCenterX, self.numberOfSteps, self.stepX)
if self.typeStringFirstCharacter == 'a':
self.setAssemblyCage()
self.rectangleCenterX = self.demiwidth - self.demilength
def setAssemblyCage(self):
'Set two piece assembly parameters.'
self.grooveDepthOverRadius = evaluate.getEvaluatedFloat(0.15, self.elementNode, 'grooveDepthOverRadius')
self.grooveDepth = self.grooveDepthOverRadius * self.radius
self.grooveDepth = evaluate.getEvaluatedFloat(self.grooveDepth, self.elementNode, 'grooveDepth')
self.grooveWidthOverRadius = evaluate.getEvaluatedFloat(0.6, self.elementNode, 'grooveWidthOverRadius')
self.grooveWidth = self.grooveWidthOverRadius * self.radius
self.grooveWidth = evaluate.getEvaluatedFloat(self.grooveWidth, self.elementNode, 'grooveWidth')
self.pegClearanceOverRadius = evaluate.getEvaluatedFloat(0.0, self.elementNode, 'pegClearanceOverRadius')
self.pegClearance = self.pegClearanceOverRadius * self.radius
self.pegClearance = evaluate.getEvaluatedFloat(self.pegClearance, self.elementNode, 'pegClearance')
self.halfPegClearance = 0.5 * self.pegClearance
self.pegRadiusOverRadius = evaluate.getEvaluatedFloat(0.5, self.elementNode, 'pegRadiusOverRadius')
self.pegRadius = self.pegRadiusOverRadius * self.radius
self.pegRadius = evaluate.getEvaluatedFloat(self.pegRadius, self.elementNode, 'pegRadius')
self.sides = evaluate.getSidesMinimumThreeBasedOnPrecision(self.elementNode, self.pegRadius)
self.pegRadiusArealized = evaluate.getRadiusArealizedBasedOnAreaRadius(self.elementNode, self.pegRadius, self.sides)
self.pegBevelOverPegRadius = evaluate.getEvaluatedFloat(0.25, self.elementNode, 'pegBevelOverPegRadius')
self.pegBevel = self.pegBevelOverPegRadius * self.pegRadiusArealized
self.pegBevel = evaluate.getEvaluatedFloat(self.pegBevel, self.elementNode, 'pegBevel')
self.pegMaximumRadius = self.pegRadiusArealized + abs(self.halfPegClearance)
self.separationOverRadius = evaluate.getEvaluatedFloat(0.5, self.elementNode, 'separationOverRadius')
self.separation = self.separationOverRadius * self.radius
self.separation = evaluate.getEvaluatedFloat(self.separation, self.elementNode, 'separation')
self.topOverBottom = evaluate.getEvaluatedFloat(0.8, self.elementNode, 'topOverBottom')
peg.setTopOverBottomByRadius(self, 0.0, self.pegRadiusArealized, self.height)
self.quarterHeight = 0.5 * self.demiheight
self.pegY = 0.5 * self.wallThickness + self.pegMaximumRadius
cagePegRadius = self.cageRadius + self.pegMaximumRadius
halfStepX = 0.5 * self.stepX
pegHypotenuse = math.sqrt(self.pegY * self.pegY + halfStepX * halfStepX)
if cagePegRadius > pegHypotenuse:
self.pegY = math.sqrt(cagePegRadius * cagePegRadius - halfStepX * halfStepX)
self.demiwidth = max(self.pegY + self.pegMaximumRadius + self.wallThickness, self.demiwidth)
self.innerDemiwidth = self.demiwidth
self.demiwidth += self.grooveDepth
self.halfSeparationWidth = self.demiwidth + 0.5 * self.separation
if self.pegRadiusArealized <= 0.0:
self.pegCenterXs = []
else:
self.pegCenterXs = getPegCenterXs(self.numberOfSteps, self.bearingCenterX + halfStepX, self.stepX)

View File

@ -0,0 +1,308 @@
"""
Polygon path.
"""
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 fabmetheus_utilities.geometry.geometry_tools import path
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getComplexByDictionary(dictionary, valueComplex):
'Get complex by dictionary.'
if 'x' in dictionary:
valueComplex = complex(euclidean.getFloatFromValue(dictionary['x']),valueComplex.imag)
if 'y' in dictionary:
valueComplex = complex(valueComplex.real, euclidean.getFloatFromValue(dictionary['y']))
return valueComplex
def getComplexByDictionaryListValue(value, valueComplex):
'Get complex by dictionary, list or value.'
if value.__class__ == complex:
return value
if value.__class__ == dict:
return getComplexByDictionary(value, valueComplex)
if value.__class__ == list:
return getComplexByFloatList(value, valueComplex)
floatFromValue = euclidean.getFloatFromValue(value)
if floatFromValue == None:
return valueComplex
return complex( floatFromValue, floatFromValue )
def getComplexByFloatList( floatList, valueComplex ):
'Get complex by float list.'
if len(floatList) > 0:
valueComplex = complex(euclidean.getFloatFromValue(floatList[0]), valueComplex.imag)
if len(floatList) > 1:
valueComplex = complex(valueComplex.real, euclidean.getFloatFromValue(floatList[1]))
return valueComplex
def getComplexByMultiplierPrefix(elementNode, multiplier, prefix, valueComplex):
'Get complex from multiplier, prefix and xml element.'
if multiplier == 0.0:
return valueComplex
oldMultipliedValueComplex = valueComplex * multiplier
complexByPrefix = getComplexByPrefix(elementNode, prefix, oldMultipliedValueComplex)
if complexByPrefix == oldMultipliedValueComplex:
return valueComplex
return complexByPrefix / multiplier
def getComplexByMultiplierPrefixes(elementNode, multiplier, prefixes, valueComplex):
'Get complex from multiplier, prefixes and xml element.'
for prefix in prefixes:
valueComplex = getComplexByMultiplierPrefix(elementNode, multiplier, prefix, valueComplex)
return valueComplex
def getComplexByPrefix(elementNode, prefix, valueComplex):
'Get complex from prefix and xml element.'
value = evaluate.getEvaluatedValue(None, elementNode, prefix)
if value != None:
valueComplex = getComplexByDictionaryListValue(value, valueComplex)
x = evaluate.getEvaluatedFloat(None, elementNode, prefix + '.x')
if x != None:
valueComplex = complex( x, getComplexIfNone( valueComplex ).imag )
y = evaluate.getEvaluatedFloat(None, elementNode, prefix + '.y')
if y != None:
valueComplex = complex( getComplexIfNone( valueComplex ).real, y )
return valueComplex
def getComplexByPrefixBeginEnd(elementNode, prefixBegin, prefixEnd, valueComplex):
'Get complex from element node, prefixBegin and prefixEnd.'
valueComplex = getComplexByPrefix(elementNode, prefixBegin, valueComplex)
if prefixEnd in elementNode.attributes:
return 0.5 * getComplexByPrefix(elementNode, valueComplex + valueComplex, prefixEnd)
else:
return valueComplex
def getComplexByPrefixes(elementNode, prefixes, valueComplex):
'Get complex from prefixes and xml element.'
for prefix in prefixes:
valueComplex = getComplexByPrefix(elementNode, prefix, valueComplex)
return valueComplex
def getComplexIfNone( valueComplex ):
'Get new complex if the original complex is none.'
if valueComplex == None:
return complex()
return valueComplex
def getFloatByPrefixBeginEnd(elementNode, prefixBegin, prefixEnd, valueFloat):
'Get float from prefixBegin, prefixEnd and xml element.'
valueFloat = evaluate.getEvaluatedFloat(valueFloat, elementNode, prefixBegin)
if prefixEnd in elementNode.attributes:
return 0.5 * evaluate.getEvaluatedFloat(valueFloat + valueFloat, elementNode, prefixEnd)
return valueFloat
def getFloatByPrefixSide(defaultValue, elementNode, prefix, side):
'Get float by prefix and side.'
if elementNode == None:
return defaultValue
if side != None:
key = prefix + 'OverSide'
if key in elementNode.attributes:
defaultValue = euclidean.getFloatFromValue(evaluate.getEvaluatedValueObliviously(elementNode, key)) * side
return evaluate.getEvaluatedFloat(defaultValue, elementNode, prefix)
def getGeometryOutput(derivation, elementNode):
'Get geometry output from paths.'
if derivation == None:
derivation = LineationDerivation(elementNode)
geometryOutput = []
for path in derivation.target:
sideLoop = SideLoop(path)
geometryOutput += getGeometryOutputByLoop(elementNode, sideLoop)
return geometryOutput
def getGeometryOutputByArguments(arguments, elementNode):
'Get vector3 vertexes from attribute dictionary by arguments.'
return getGeometryOutput(None, elementNode)
def getGeometryOutputByLoop(elementNode, sideLoop):
'Get geometry output by side loop.'
sideLoop.rotate(elementNode)
return getGeometryOutputByManipulation(elementNode, sideLoop)
def getGeometryOutputByManipulation(elementNode, sideLoop):
'Get geometry output by manipulation.'
sideLoop.loop = euclidean.getLoopWithoutCloseSequentialPoints( sideLoop.close, sideLoop.loop )
return sideLoop.getManipulationPluginLoops(elementNode)
def getInradius(defaultInradius, elementNode):
'Get inradius.'
defaultInradius = getComplexByPrefixes(elementNode, ['demisize', 'inradius'], defaultInradius)
return getComplexByMultiplierPrefix(elementNode, 2.0, 'size', defaultInradius)
def getMinimumRadius(beginComplexSegmentLength, endComplexSegmentLength, radius):
'Get minimum radius.'
return min(abs(radius), 0.5 * min(beginComplexSegmentLength, endComplexSegmentLength))
def getNewDerivation(elementNode):
'Get new derivation.'
return LineationDerivation(elementNode)
def getNumberOfBezierPoints(begin, elementNode, end):
'Get the numberOfBezierPoints.'
numberOfBezierPoints = int(math.ceil(0.5 * evaluate.getSidesMinimumThreeBasedOnPrecision(elementNode, abs(end - begin))))
return evaluate.getEvaluatedInt(numberOfBezierPoints, elementNode, 'sides')
def getPackedGeometryOutputByLoop(elementNode, sideLoop):
'Get packed geometry output by side loop.'
sideLoop.rotate(elementNode)
return getGeometryOutputByManipulation(elementNode, sideLoop)
def getRadiusAverage(radiusComplex):
'Get average radius from radiusComplex.'
return math.sqrt(radiusComplex.real * radiusComplex.imag)
def getRadiusComplex(elementNode, radius):
'Get radius complex for elementNode.'
radius = getComplexByPrefixes(elementNode, ['demisize', 'radius'], radius)
return getComplexByMultiplierPrefixes(elementNode, 2.0, ['diameter', 'size'], radius)
def getStrokeRadiusByPrefix(elementNode, prefix):
'Get strokeRadius by prefix.'
strokeRadius = getFloatByPrefixBeginEnd(elementNode, prefix + 'strokeRadius', prefix + 'strokeWidth', 1.0)
return getFloatByPrefixBeginEnd(elementNode, prefix + 'radius', prefix + 'diameter', strokeRadius)
def processElementNode(elementNode):
'Process the xml element.'
path.convertElementNode(elementNode, getGeometryOutput(None, elementNode))
def processElementNodeByFunction(elementNode, manipulationFunction):
'Process the xml element by the manipulationFunction.'
elementAttributesCopy = elementNode.attributes.copy()
targets = evaluate.getElementNodesByKey(elementNode, 'target')
for target in targets:
targetAttributesCopy = target.attributes.copy()
target.attributes = elementAttributesCopy
processTargetByFunction(manipulationFunction, target)
target.attributes = targetAttributesCopy
def processTargetByFunction(manipulationFunction, target):
'Process the target by the manipulationFunction.'
if target.xmlObject == None:
print('Warning, there is no object in processTargetByFunction in lineation for:')
print(target)
return
geometryOutput = []
transformedPaths = target.xmlObject.getTransformedPaths()
for transformedPath in transformedPaths:
sideLoop = SideLoop(transformedPath)
sideLoop.rotate(target)
sideLoop.loop = euclidean.getLoopWithoutCloseSequentialPoints( sideLoop.close, sideLoop.loop )
geometryOutput += manipulationFunction(sideLoop.close, target, sideLoop.loop, '', sideLoop.sideLength)
if len(geometryOutput) < 1:
print('Warning, there is no geometryOutput in processTargetByFunction in lineation for:')
print(target)
return
removeChildNodesFromElementObject(target)
path.convertElementNode(target, geometryOutput)
def removeChildNodesFromElementObject(elementNode):
'Process the xml element by manipulationFunction.'
elementNode.removeChildNodesFromIDNameParent()
if elementNode.xmlObject != None:
if elementNode.parentNode.xmlObject != None:
if elementNode.xmlObject in elementNode.parentNode.xmlObject.archivableObjects:
elementNode.parentNode.xmlObject.archivableObjects.remove(elementNode.xmlObject)
def setClosedAttribute(elementNode, revolutions):
'Set the closed attribute of the elementNode.'
closedBoolean = evaluate.getEvaluatedBoolean(revolutions <= 1, elementNode, 'closed')
elementNode.attributes['closed'] = str(closedBoolean).lower()
class LineationDerivation:
'Class to hold lineation variables.'
def __init__(self, elementNode):
'Set defaults.'
self.target = evaluate.getTransformedPathsByKey([], elementNode, 'target')
class SideLoop:
'Class to handle loop, side angle and side length.'
def __init__(self, loop, sideAngle=None, sideLength=None):
'Initialize.'
if sideAngle == None:
if len(loop) > 0:
sideAngle = 2.0 * math.pi / float(len(loop))
else:
sideAngle = 1.0
print('Warning, loop has no sides in SideLoop in lineation.')
if sideLength == None:
if len(loop) > 0:
sideLength = euclidean.getLoopLength(loop) / float(len(loop))
else:
sideLength = 1.0
print('Warning, loop has no length in SideLoop in lineation.')
self.loop = loop
self.sideAngle = abs(sideAngle)
self.sideLength = abs(sideLength)
self.close = 0.001 * sideLength
def getManipulationPluginLoops(self, elementNode):
'Get loop manipulated by the plugins in the manipulation paths folder.'
xmlProcessor = elementNode.getXMLProcessor()
matchingPlugins = evaluate.getMatchingPlugins(elementNode, xmlProcessor.manipulationMatrixDictionary)
matchingPlugins += evaluate.getMatchingPlugins(elementNode, xmlProcessor.manipulationPathDictionary)
matchingPlugins += evaluate.getMatchingPlugins(elementNode, xmlProcessor.manipulationShapeDictionary)
matchingPlugins.sort(evaluate.compareExecutionOrderAscending)
loops = [self.loop]
for matchingPlugin in matchingPlugins:
matchingLoops = []
prefix = matchingPlugin.__name__.replace('_', '') + '.'
for loop in loops:
matchingLoops += matchingPlugin.getManipulatedPaths(self.close, elementNode, loop, prefix, self.sideLength)
loops = matchingLoops
return loops
def rotate(self, elementNode):
'Rotate.'
rotation = math.radians(evaluate.getEvaluatedFloat(0.0, elementNode, 'rotation'))
rotation += evaluate.getEvaluatedFloat(0.0, elementNode, 'rotationOverSide') * self.sideAngle
if rotation != 0.0:
planeRotation = euclidean.getWiddershinsUnitPolar( rotation )
for vertex in self.loop:
rotatedComplex = vertex.dropAxis() * planeRotation
vertex.x = rotatedComplex.real
vertex.y = rotatedComplex.imag
if 'clockwise' in elementNode.attributes:
isClockwise = euclidean.getBooleanFromValue(evaluate.getEvaluatedValueObliviously(elementNode, 'clockwise'))
if isClockwise == euclidean.getIsWiddershinsByVector3( self.loop ):
self.loop.reverse()
class Spiral:
'Class to add a spiral.'
def __init__(self, spiral, stepRatio):
'Initialize.'
self.spiral = spiral
if self.spiral == None:
return
self.spiralIncrement = self.spiral * stepRatio
self.spiralTotal = Vector3()
def __repr__(self):
'Get the string representation of this Spiral.'
return self.spiral
def getSpiralPoint(self, unitPolar, vector3):
'Add spiral to the vector.'
if self.spiral == None:
return vector3
vector3 += Vector3(unitPolar.real * self.spiralTotal.x, unitPolar.imag * self.spiralTotal.y, self.spiralTotal.z)
self.spiralTotal += self.spiralIncrement
return vector3

View File

@ -0,0 +1,259 @@
"""
Mechaslab.
"""
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 fabmetheus_utilities.geometry.creation import extrude
from fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.creation import peg
from fabmetheus_utilities.geometry.creation import solid
from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities.geometry.manipulation_matrix import translate
from fabmetheus_utilities.geometry.solids import cylinder
from fabmetheus_utilities.geometry.solids import triangle_mesh
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def addAlongWay(begin, distance, end, loop):
'Get the beveled rectangle.'
endMinusBegin = end - begin
endMinusBeginLength = abs(endMinusBegin)
if endMinusBeginLength <= 0.0:
return
alongWayMultiplier = distance / endMinusBeginLength
loop.append(begin + alongWayMultiplier * endMinusBegin)
def addGroove(derivation, negatives):
'Add groove on each side of cage.'
copyShallow = derivation.elementNode.getCopyShallow()
extrude.setElementNodeToEndStart(copyShallow, Vector3(-derivation.demilength), Vector3(derivation.demilength))
extrudeDerivation = extrude.ExtrudeDerivation(copyShallow)
bottom = derivation.demiheight - 0.5 * derivation.grooveWidth
outside = derivation.demiwidth
top = derivation.demiheight
leftGroove = [
complex(-outside, bottom),
complex(-derivation.innerDemiwidth, derivation.demiheight),
complex(-outside, top)]
rightGroove = [
complex(outside, top),
complex(derivation.innerDemiwidth, derivation.demiheight),
complex(outside, bottom)]
groovesComplex = [leftGroove, rightGroove]
groovesVector3 = euclidean.getVector3Paths(groovesComplex)
extrude.addPositives(extrudeDerivation, groovesVector3, negatives)
def addHollowPegSocket(derivation, hollowPegSocket, negatives, positives):
'Add the socket and hollow peg.'
pegHeight = derivation.pegHeight
pegRadians = derivation.pegRadians
pegRadiusComplex = complex(derivation.pegRadiusArealized, derivation.pegRadiusArealized)
pegTip = 0.8 * derivation.pegRadiusArealized
sides = derivation.pegSides
start = Vector3(hollowPegSocket.center.real, hollowPegSocket.center.imag, derivation.height)
tinyHeight = 0.0001 * pegHeight
topRadians = 0.25 * math.pi
boltTop = derivation.height
if hollowPegSocket.shouldAddPeg:
boltTop = peg.getTopAddBiconicOutput(
pegRadians, pegHeight, positives, pegRadiusComplex, sides, start, pegTip, topRadians)
sides = derivation.socketSides
socketHeight = 1.05 * derivation.pegHeight
socketRadiusComplex = complex(derivation.socketRadiusArealized, derivation.socketRadiusArealized)
socketTip = 0.5 * derivation.overhangSpan
start = Vector3(hollowPegSocket.center.real, hollowPegSocket.center.imag, -tinyHeight)
topRadians = derivation.interiorOverhangRadians
if hollowPegSocket.shouldAddSocket:
peg.getTopAddBiconicOutput(pegRadians, socketHeight, negatives, socketRadiusComplex, sides, start, socketTip, topRadians)
if derivation.boltRadius <= 0.0:
return
if (not hollowPegSocket.shouldAddPeg) and (not hollowPegSocket.shouldAddSocket):
return
boltRadiusComplex = complex(derivation.boltRadius, derivation.boltRadius)
cylinder.addCylinderOutputByEndStart(boltTop + tinyHeight, boltRadiusComplex, negatives, derivation.boltSides, start)
def addSlab(derivation, positives):
'Add slab.'
copyShallow = derivation.elementNode.getCopyShallow()
copyShallow.attributes['path'] = [Vector3(), Vector3(0.0, 0.0, derivation.height)]
extrudeDerivation = extrude.ExtrudeDerivation(copyShallow)
beveledRectangle = getBeveledRectangle(derivation.bevel, -derivation.topRight)
outsidePath = euclidean.getVector3Path(beveledRectangle)
extrude.addPositives(extrudeDerivation, [outsidePath], positives)
def addXGroove(derivation, negatives, y):
'Add x groove.'
if derivation.topBevel <= 0.0:
return
bottom = derivation.height - derivation.topBevel
top = derivation.height
groove = [complex(y, bottom), complex(y - derivation.topBevel, top), complex(y + derivation.topBevel, top)]
triangle_mesh.addSymmetricXPath(negatives, groove, 1.0001 * derivation.topRight.real)
def addYGroove(derivation, negatives, x):
'Add y groove'
if derivation.topBevel <= 0.0:
return
bottom = derivation.height - derivation.topBevel
top = derivation.height
groove = [complex(x, bottom), complex(x - derivation.topBevel, top), complex(x + derivation.topBevel, top)]
triangle_mesh.addSymmetricYPath(negatives, groove, 1.0001 * derivation.topRight.imag)
def getBeveledRectangle(bevel, bottomLeft):
'Get the beveled rectangle.'
bottomRight = complex(-bottomLeft.real, bottomLeft.imag)
rectangle = [bottomLeft, bottomRight, -bottomLeft, -bottomRight]
if bevel <= 0.0:
return rectangle
beveledRectangle = []
for pointIndex, point in enumerate(rectangle):
begin = rectangle[(pointIndex + len(rectangle) - 1) % len(rectangle)]
end = rectangle[(pointIndex + 1) % len(rectangle)]
addAlongWay(point, bevel, begin, beveledRectangle)
addAlongWay(point, bevel, end, beveledRectangle)
return beveledRectangle
def getGeometryOutput(elementNode):
'Get vector3 vertexes from attribute dictionary.'
derivation = MechaslabDerivation(elementNode)
negatives = []
positives = []
addSlab(derivation, positives)
for hollowPegSocket in derivation.hollowPegSockets:
addHollowPegSocket(derivation, hollowPegSocket, negatives, positives)
if 's' in derivation.topBevelPositions:
addXGroove(derivation, negatives, -derivation.topRight.imag)
if 'n' in derivation.topBevelPositions:
addXGroove(derivation, negatives, derivation.topRight.imag)
if 'w' in derivation.topBevelPositions:
addYGroove(derivation, negatives, -derivation.topRight.real)
if 'e' in derivation.topBevelPositions:
addYGroove(derivation, negatives, derivation.topRight.real)
return extrude.getGeometryOutputByNegativesPositives(elementNode, negatives, positives)
def getGeometryOutputByArguments(arguments, elementNode):
'Get vector3 vertexes from attribute dictionary by arguments.'
evaluate.setAttributesByArguments(['length', 'radius'], arguments, elementNode)
return getGeometryOutput(elementNode)
def getNewDerivation(elementNode):
'Get new derivation.'
return MechaslabDerivation(elementNode)
def processElementNode(elementNode):
'Process the xml element.'
solid.processElementNodeByGeometry(elementNode, getGeometryOutput(elementNode))
class CellExistence:
'Class to determine if a cell exists.'
def __init__(self, columns, rows, value):
'Initialize.'
self.existenceSet = None
if value == None:
return
self.existenceSet = set()
for element in value:
if element.__class__ == int:
columnIndex = (element + columns) % columns
for rowIndex in xrange(rows):
keyTuple = (columnIndex, rowIndex)
self.existenceSet.add(keyTuple)
else:
keyTuple = (element[0], element[1])
self.existenceSet.add(keyTuple)
def __repr__(self):
'Get the string representation of this CellExistence.'
return euclidean.getDictionaryString(self.__dict__)
def getIsInExistence(self, columnIndex, rowIndex):
'Detremine if the cell at the column and row exists.'
if self.existenceSet == None:
return True
return (columnIndex, rowIndex) in self.existenceSet
class HollowPegSocket:
'Class to hold hollow peg socket variables.'
def __init__(self, center):
'Initialize.'
self.center = center
self.shouldAddPeg = True
self.shouldAddSocket = True
def __repr__(self):
'Get the string representation of this HollowPegSocket.'
return euclidean.getDictionaryString(self.__dict__)
class MechaslabDerivation:
'Class to hold mechaslab variables.'
def __init__(self, elementNode):
'Set defaults.'
self.bevelOverRadius = evaluate.getEvaluatedFloat(0.2, elementNode, 'bevelOverRadius')
self.boltRadiusOverRadius = evaluate.getEvaluatedFloat(0.0, elementNode, 'boltRadiusOverRadius')
self.columns = evaluate.getEvaluatedInt(2, elementNode, 'columns')
self.elementNode = elementNode
self.heightOverRadius = evaluate.getEvaluatedFloat(2.0, elementNode, 'heightOverRadius')
self.interiorOverhangRadians = setting.getInteriorOverhangRadians(elementNode)
self.overhangSpan = setting.getOverhangSpan(elementNode)
self.pegClearanceOverRadius = evaluate.getEvaluatedFloat(0.0, elementNode, 'pegClearanceOverRadius')
self.pegRadians = math.radians(evaluate.getEvaluatedFloat(2.0, elementNode, 'pegAngle'))
self.pegHeightOverHeight = evaluate.getEvaluatedFloat(0.4, elementNode, 'pegHeightOverHeight')
self.pegRadiusOverRadius = evaluate.getEvaluatedFloat(0.7, elementNode, 'pegRadiusOverRadius')
self.radius = lineation.getFloatByPrefixBeginEnd(elementNode, 'radius', 'width', 5.0)
self.rows = evaluate.getEvaluatedInt(1, elementNode, 'rows')
self.topBevelOverRadius = evaluate.getEvaluatedFloat(0.2, elementNode, 'topBevelOverRadius')
# Set derived values.
self.bevel = evaluate.getEvaluatedFloat(self.bevelOverRadius * self.radius, elementNode, 'bevel')
self.boltRadius = evaluate.getEvaluatedFloat(self.boltRadiusOverRadius * self.radius, elementNode, 'boltRadius')
self.boltSides = evaluate.getSidesMinimumThreeBasedOnPrecision(elementNode, self.boltRadius)
self.bottomLeftCenter = complex(-float(self.columns - 1), -float(self.rows - 1)) * self.radius
self.height = evaluate.getEvaluatedFloat(self.heightOverRadius * self.radius, elementNode, 'height')
self.hollowPegSockets = []
centerY = self.bottomLeftCenter.imag
diameter = self.radius + self.radius
self.pegExistence = CellExistence(self.columns, self.rows, evaluate.getEvaluatedValue(None, elementNode, 'pegs'))
self.socketExistence = CellExistence(self.columns, self.rows, evaluate.getEvaluatedValue(None, elementNode, 'sockets'))
for rowIndex in xrange(self.rows):
centerX = self.bottomLeftCenter.real
for columnIndex in xrange(self.columns):
hollowPegSocket = HollowPegSocket(complex(centerX, centerY))
hollowPegSocket.shouldAddPeg = self.pegExistence.getIsInExistence(columnIndex, rowIndex)
hollowPegSocket.shouldAddSocket = self.socketExistence.getIsInExistence(columnIndex, rowIndex)
self.hollowPegSockets.append(hollowPegSocket)
centerX += diameter
centerY += diameter
self.pegClearance = evaluate.getEvaluatedFloat(self.pegClearanceOverRadius * self.radius, elementNode, 'pegClearance')
halfPegClearance = 0.5 * self.pegClearance
self.pegHeight = evaluate.getEvaluatedFloat(self.pegHeightOverHeight * self.height, elementNode, 'pegHeight')
self.pegRadius = evaluate.getEvaluatedFloat(self.pegRadiusOverRadius * self.radius, elementNode, 'pegRadius')
sides = 24 * max(1, math.floor(evaluate.getSidesBasedOnPrecision(elementNode, self.pegRadius) / 24))
self.socketRadius = self.pegRadius + halfPegClearance
self.pegSides = evaluate.getEvaluatedInt(sides, elementNode, 'pegSides')
self.pegRadius -= halfPegClearance
self.pegRadiusArealized = evaluate.getRadiusArealizedBasedOnAreaRadius(elementNode, self.pegRadius, self.pegSides)
self.socketSides = evaluate.getEvaluatedInt(sides, elementNode, 'socketSides')
self.socketRadiusArealized = evaluate.getRadiusArealizedBasedOnAreaRadius(elementNode, self.socketRadius, self.socketSides)
self.topBevel = evaluate.getEvaluatedFloat(self.topBevelOverRadius * self.radius, elementNode, 'topBevel')
self.topBevelPositions = evaluate.getEvaluatedString('nwse', elementNode, 'topBevelPositions').lower()
self.topRight = complex(float(self.columns), float(self.rows)) * self.radius
def __repr__(self):
'Get the string representation of this MechaslabDerivation.'
return euclidean.getDictionaryString(self.__dict__)

View File

@ -0,0 +1,103 @@
"""
Peg.
"""
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 fabmetheus_utilities.geometry.creation import extrude
from fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.creation import solid
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.solids import cylinder
from fabmetheus_utilities.vector3 import Vector3
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def addPegOutput(bevel, endZ, outputs, radiusArealized, sides, start, topOverBottom):
'Add beveled cylinder to outputs given bevel, endZ, radiusArealized and start.'
height = abs(start.z - endZ)
bevelStartRatio = max(1.0 - bevel / height, 0.5)
oneMinusBevelStartRatio = 1.0 - bevelStartRatio
trunkEndZ = bevelStartRatio * endZ + oneMinusBevelStartRatio * start.z
trunkTopOverBottom = bevelStartRatio * topOverBottom + oneMinusBevelStartRatio
cylinder.addCylinderOutputByEndStart(trunkEndZ, radiusArealized, outputs, sides, start, trunkTopOverBottom)
capRadius = radiusArealized * trunkTopOverBottom
capStart = bevelStartRatio * Vector3(start.x, start.y, endZ) + oneMinusBevelStartRatio * start
radiusMaximum = max(radiusArealized.real, radiusArealized.imag)
endRadiusMaximum = radiusMaximum * topOverBottom - bevel
trunkRadiusMaximum = radiusMaximum * trunkTopOverBottom
capTopOverBottom = endRadiusMaximum / trunkRadiusMaximum
cylinder.addCylinderOutputByEndStart(endZ, capRadius, outputs, sides, capStart, capTopOverBottom)
def getGeometryOutput(derivation, elementNode):
'Get vector3 vertexes from attribute dictionary.'
if derivation == None:
derivation = PegDerivation(elementNode)
positives = []
radiusArealized = complex(derivation.radiusArealized, derivation.radiusArealized)
addPegOutput(derivation.bevel, derivation.endZ, positives, radiusArealized, derivation.sides, derivation.start, derivation.topOverBottom)
return extrude.getGeometryOutputByNegativesPositives(elementNode, [], positives)
def getGeometryOutputByArguments(arguments, elementNode):
'Get vector3 vertexes from attribute dictionary by arguments.'
evaluate.setAttributesByArguments(['radius', 'endZ', 'start'], arguments, elementNode)
return getGeometryOutput(None, elementNode)
def getNewDerivation(elementNode):
'Get new derivation.'
return PegDerivation(elementNode)
def getTopAddBiconicOutput(bottomRadians, height, outputs, radius, sides, start, tipRadius, topRadians):
'Get top and add biconic cylinder to outputs.'
radiusMaximum = max(radius.real, radius.imag)
topRadiusMaximum = radiusMaximum - height * math.tan(bottomRadians)
trunkEndZ = start.z + height
trunkTopOverBottom = topRadiusMaximum / radiusMaximum
topRadiusComplex = trunkTopOverBottom * radius
cylinder.addCylinderOutputByEndStart(trunkEndZ, radius, outputs, sides, start, trunkTopOverBottom)
tipOverTop = tipRadius / topRadiusMaximum
if tipOverTop >= 1.0:
return trunkEndZ
capStart = Vector3(start.x, start.y, trunkEndZ)
capEndZ = trunkEndZ + (topRadiusMaximum - tipRadius) / math.tan(topRadians)
cylinder.addCylinderOutputByEndStart(capEndZ, topRadiusComplex, outputs, sides, capStart, tipOverTop)
return capEndZ
def processElementNode(elementNode):
'Process the xml element.'
solid.processElementNodeByGeometry(elementNode, getGeometryOutput(None, elementNode))
def setTopOverBottomByRadius(derivation, endZ, radius, startZ):
'Set the derivation topOverBottom by the angle of the elementNode, the endZ, float radius and startZ.'
angleDegrees = evaluate.getEvaluatedFloat(None, derivation.elementNode, 'angle')
if angleDegrees != None:
derivation.topOverBottom = cylinder.getTopOverBottom(math.radians(angleDegrees), endZ, complex(radius, radius), startZ)
class PegDerivation:
'Class to hold peg variables.'
def __init__(self, elementNode):
'Set defaults.'
self.bevelOverRadius = evaluate.getEvaluatedFloat(0.25, elementNode, 'bevelOverRadius')
self.clearanceOverRadius = evaluate.getEvaluatedFloat(0.0, elementNode, 'clearanceOverRadius')
self.elementNode = elementNode
self.endZ = evaluate.getEvaluatedFloat(10.0, elementNode, 'endZ')
self.start = evaluate.getVector3ByPrefix(Vector3(), elementNode, 'start')
self.radius = lineation.getFloatByPrefixBeginEnd(elementNode, 'radius', 'diameter', 2.0)
self.sides = evaluate.getSidesMinimumThreeBasedOnPrecision(elementNode, max(self.radius.real, self.radius.imag))
self.radiusArealized = evaluate.getRadiusArealizedBasedOnAreaRadius(elementNode, self.radius, self.sides)
self.topOverBottom = evaluate.getEvaluatedFloat(0.8, elementNode, 'topOverBottom')
setTopOverBottomByRadius(self, self.endZ, self.radiusArealized, self.start.z)
# Set derived variables.
self.bevel = evaluate.getEvaluatedFloat(self.bevelOverRadius * self.radiusArealized, elementNode, 'bevel')
self.clearance = evaluate.getEvaluatedFloat(self.clearanceOverRadius * self.radiusArealized, elementNode, 'clearance')

View File

@ -0,0 +1,69 @@
"""
Polygon path.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.geometry_tools import path
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getGeometryOutput(derivation, elementNode):
"Get vector3 vertexes from attribute dictionary."
if derivation == None:
derivation = PolygonDerivation(elementNode)
loop = []
spiral = lineation.Spiral(derivation.spiral, 0.5 * derivation.sideAngle / math.pi)
for side in xrange(derivation.start, derivation.start + derivation.extent + 1):
angle = float(side) * derivation.sideAngle
unitPolar = euclidean.getWiddershinsUnitPolar(angle)
vertex = spiral.getSpiralPoint(unitPolar, Vector3(unitPolar.real * derivation.radius.real, unitPolar.imag * derivation.radius.imag))
loop.append(vertex)
loop = euclidean.getLoopWithoutCloseEnds(0.000001 * max(derivation.radius.real, derivation.radius.imag), loop)
lineation.setClosedAttribute(elementNode, derivation.revolutions)
return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop(loop, derivation.sideAngle))
def getGeometryOutputByArguments(arguments, elementNode):
"Get vector3 vertexes from attribute dictionary by arguments."
evaluate.setAttributesByArguments(['sides', 'radius'], arguments, elementNode)
return getGeometryOutput(None, elementNode)
def getNewDerivation(elementNode):
'Get new derivation.'
return PolygonDerivation(elementNode)
def processElementNode(elementNode):
"Process the xml element."
path.convertElementNode(elementNode, getGeometryOutput(None, elementNode))
class PolygonDerivation:
"Class to hold polygon variables."
def __init__(self, elementNode):
'Set defaults.'
self.sides = evaluate.getEvaluatedFloat(4.0, elementNode, 'sides')
self.sideAngle = 2.0 * math.pi / self.sides
cosSide = math.cos(0.5 * self.sideAngle)
self.radius = lineation.getComplexByMultiplierPrefixes(elementNode, cosSide, ['apothem', 'inradius'], complex(1.0, 1.0))
self.radius = lineation.getComplexByPrefixes(elementNode, ['demisize', 'radius'], self.radius)
self.radius = lineation.getComplexByMultiplierPrefixes(elementNode, 2.0, ['diameter', 'size'], self.radius)
self.sidesCeiling = int(math.ceil(abs(self.sides)))
self.start = evaluate.getEvaluatedInt(0, elementNode, 'start')
end = evaluate.getEvaluatedInt(self.sidesCeiling, elementNode, 'end')
self.revolutions = evaluate.getEvaluatedInt(1, elementNode, 'revolutions')
self.extent = evaluate.getEvaluatedInt(end - self.start, elementNode, 'extent')
self.extent += self.sidesCeiling * (self.revolutions - 1)
self.spiral = evaluate.getVector3ByPrefix(None, elementNode, 'spiral')

View File

@ -0,0 +1,82 @@
"""
Shaft path.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.geometry_tools import path
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getGeometryOutput(derivation, elementNode):
"Get vector3 vertexes from attribute dictionary."
if derivation == None:
derivation = ShaftDerivation(elementNode)
shaftPath = getShaftPath(derivation.depthBottom, derivation.depthTop, derivation.radius, derivation.sides)
return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop(shaftPath))
def getGeometryOutputByArguments(arguments, elementNode):
"Get vector3 vertexes from attribute dictionary by arguments."
evaluate.setAttributesByArguments(['radius', 'sides'], arguments, elementNode)
return getGeometryOutput(None, elementNode)
def getNewDerivation(elementNode):
'Get new derivation.'
return ShaftDerivation(elementNode)
def getShaftPath(depthBottom, depthTop, radius, sides):
'Get shaft with the option of a flat on the top and/or bottom.'
if radius <= 0.0:
return []
sideAngle = 2.0 * math.pi / float(abs(sides))
startAngle = 0.5 * sideAngle
endAngle = math.pi - 0.1 * sideAngle
shaftProfile = []
while startAngle < endAngle:
unitPolar = euclidean.getWiddershinsUnitPolar(startAngle)
shaftProfile.append(unitPolar * radius)
startAngle += sideAngle
if abs(sides) % 2 == 1:
shaftProfile.append(complex(-radius, 0.0))
horizontalBegin = radius - depthTop
horizontalEnd = depthBottom - radius
shaftProfile = euclidean.getHorizontallyBoundedPath(horizontalBegin, horizontalEnd, shaftProfile)
for shaftPointIndex, shaftPoint in enumerate(shaftProfile):
shaftProfile[shaftPointIndex] = complex(shaftPoint.imag, shaftPoint.real)
shaftPath = euclidean.getVector3Path(euclidean.getMirrorPath(shaftProfile))
if sides > 0:
shaftPath.reverse()
return shaftPath
def processElementNode(elementNode):
"Process the xml element."
path.convertElementNode(elementNode, getGeometryOutput(None, elementNode))
class ShaftDerivation:
"Class to hold shaft variables."
def __init__(self, elementNode):
'Set defaults.'
self.depthBottomOverRadius = evaluate.getEvaluatedFloat(0.0, elementNode, 'depthBottomOverRadius')
self.depthTopOverRadius = evaluate.getEvaluatedFloat(0.0, elementNode, 'depthOverRadius')
self.depthTopOverRadius = evaluate.getEvaluatedFloat(
self.depthTopOverRadius, elementNode, 'depthTopOverRadius')
self.radius = evaluate.getEvaluatedFloat(1.0, elementNode, 'radius')
self.sides = evaluate.getEvaluatedInt(4, elementNode, 'sides')
self.depthBottom = self.radius * self.depthBottomOverRadius
self.depthBottom = evaluate.getEvaluatedFloat(self.depthBottom, elementNode, 'depthBottom')
self.depthTop = self.radius * self.depthTopOverRadius
self.depthTop = evaluate.getEvaluatedFloat(self.depthTop, elementNode, 'depth')
self.depthTop = evaluate.getEvaluatedFloat(self.depthTop, elementNode, 'depthTop')

View File

@ -0,0 +1,178 @@
"""
Solid has functions for 3D shapes.
Solid has some of the same functions as lineation, however you can not define geometry by dictionary string in the target because there is no getGeometryOutputByArguments function. You would have to define a shape by making the shape element. Also, you can not define geometry by 'get<Creation Name>, because the target only gets element. Instead you would have the shape element, and set the target in solid to that element.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.geometry_tools import path
from fabmetheus_utilities.geometry.geometry_utilities import boolean_geometry
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities.geometry.solids import triangle_mesh
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getGeometryOutputByFunction(elementNode, geometryFunction):
'Get geometry output by manipulationFunction.'
if elementNode.xmlObject == None:
print('Warning, there is no object in getGeometryOutputByFunction in solid for:')
print(elementNode)
return None
geometryOutput = elementNode.xmlObject.getGeometryOutput()
if geometryOutput == None:
print('Warning, there is no geometryOutput in getGeometryOutputByFunction in solid for:')
print(elementNode)
return None
return geometryFunction(elementNode, geometryOutput, '')
def getGeometryOutputByManipulation(elementNode, geometryOutput):
'Get geometryOutput manipulated by the plugins in the manipulation shapes & solids folders.'
xmlProcessor = elementNode.getXMLProcessor()
matchingPlugins = getSolidMatchingPlugins(elementNode)
matchingPlugins.sort(evaluate.compareExecutionOrderAscending)
for matchingPlugin in matchingPlugins:
prefix = matchingPlugin.__name__.replace('_', '') + '.'
geometryOutput = matchingPlugin.getManipulatedGeometryOutput(elementNode, geometryOutput, prefix)
return geometryOutput
def getLoopLayersSetCopy(elementNode, geometryOutput, importRadius, radius):
'Get the loop layers and set the copyShallow.'
halfLayerHeight = 0.5 * radius
copyShallow = elementNode.getCopyShallow()
processElementNodeByGeometry(copyShallow, geometryOutput)
targetMatrix = matrix.getBranchMatrixSetElementNode(elementNode)
matrix.setElementNodeDictionaryMatrix(copyShallow, targetMatrix)
transformedVertexes = copyShallow.xmlObject.getTransformedVertexes()
minimumZ = boolean_geometry.getMinimumZ(copyShallow.xmlObject)
if minimumZ == None:
copyShallow.parentNode.xmlObject.archivableObjects.remove(copyShallow.xmlObject)
return []
maximumZ = euclidean.getTopPath(transformedVertexes)
copyShallow.attributes['visible'] = True
copyShallowObjects = [copyShallow.xmlObject]
bottomLoopLayer = euclidean.LoopLayer(minimumZ)
z = minimumZ + 0.1 * radius
zoneArrangement = triangle_mesh.ZoneArrangement(radius, transformedVertexes)
bottomLoopLayer.loops = boolean_geometry.getEmptyZLoops(copyShallowObjects, importRadius, False, z, zoneArrangement)
loopLayers = [bottomLoopLayer]
z = minimumZ + halfLayerHeight
loopLayers += boolean_geometry.getLoopLayers(copyShallowObjects, importRadius, halfLayerHeight, maximumZ, False, z, zoneArrangement)
copyShallow.parentNode.xmlObject.archivableObjects.remove(copyShallow.xmlObject)
return loopLayers
def getLoopOrEmpty(loopIndex, loopLayers):
'Get the loop, or if the loopIndex is out of range, get an empty list.'
if loopIndex < 0 or loopIndex >= len(loopLayers):
return []
return loopLayers[loopIndex].loops[0]
def getNewDerivation(elementNode):
'Get new derivation.'
return SolidDerivation(elementNode)
def getSolidMatchingPlugins(elementNode):
'Get solid plugins in the manipulation matrix, shapes & solids folders.'
xmlProcessor = elementNode.getXMLProcessor()
matchingPlugins = evaluate.getMatchingPlugins(elementNode, xmlProcessor.manipulationMatrixDictionary)
return matchingPlugins + evaluate.getMatchingPlugins(elementNode, xmlProcessor.manipulationShapeDictionary)
def processArchiveRemoveSolid(elementNode, geometryOutput):
'Process the target by the manipulationFunction.'
solidMatchingPlugins = getSolidMatchingPlugins(elementNode)
if len(solidMatchingPlugins) == 0:
elementNode.parentNode.xmlObject.archivableObjects.append(elementNode.xmlObject)
matrix.getBranchMatrixSetElementNode(elementNode)
return
processElementNodeByGeometry(elementNode, getGeometryOutputByManipulation(elementNode, geometryOutput))
def processElementNode(elementNode):
'Process the xml element.'
processElementNodeByDerivation(None, elementNode)
def processElementNodeByDerivation(derivation, elementNode):
'Process the xml element by derivation.'
if derivation == None:
derivation = SolidDerivation(elementNode)
elementAttributesCopy = elementNode.attributes.copy()
for target in derivation.targets:
targetAttributesCopy = target.attributes.copy()
target.attributes = elementAttributesCopy
processTarget(target)
target.attributes = targetAttributesCopy
def processElementNodeByFunction(elementNode, manipulationFunction):
'Process the xml element.'
if 'target' not in elementNode.attributes:
print('Warning, there was no target in processElementNodeByFunction in solid for:')
print(elementNode)
return
target = evaluate.getEvaluatedLinkValue(elementNode, str(elementNode.attributes['target']).strip())
if target.__class__.__name__ == 'ElementNode':
manipulationFunction(elementNode, target)
return
path.convertElementNode(elementNode, target)
manipulationFunction(elementNode, elementNode)
def processElementNodeByFunctionPair(elementNode, geometryFunction, pathFunction):
'Process the xml element by the appropriate manipulationFunction.'
elementAttributesCopy = elementNode.attributes.copy()
targets = evaluate.getElementNodesByKey(elementNode, 'target')
for target in targets:
targetAttributesCopy = target.attributes.copy()
target.attributes = elementAttributesCopy
processTargetByFunctionPair(geometryFunction, pathFunction, target)
target.attributes = targetAttributesCopy
def processElementNodeByGeometry(elementNode, geometryOutput):
'Process the xml element by geometryOutput.'
if geometryOutput != None:
elementNode.getXMLProcessor().convertElementNode(elementNode, geometryOutput)
def processTarget(target):
'Process the target.'
if target.xmlObject == None:
print('Warning, there is no object in processElementNode in solid for:')
print(target)
return
geometryOutput = target.xmlObject.getGeometryOutput()
if geometryOutput == None:
print('Warning, there is no geometryOutput in processElementNode in solid for:')
print(target.xmlObject)
return
geometryOutput = getGeometryOutputByManipulation(target, geometryOutput)
lineation.removeChildNodesFromElementObject(target)
target.getXMLProcessor().convertElementNode(target, geometryOutput)
def processTargetByFunctionPair(geometryFunction, pathFunction, target):
'Process the target by the manipulationFunction.'
if target.xmlObject == None:
print('Warning, there is no object in processTargetByFunctions in solid for:')
print(target)
return
if len(target.xmlObject.getPaths()) > 0:
lineation.processTargetByFunction(pathFunction, target)
return
geometryOutput = getGeometryOutputByFunction(target, geometryFunction)
lineation.removeChildNodesFromElementObject(target)
target.getXMLProcessor().convertElementNode(target, geometryOutput)
class SolidDerivation:
'Class to hold solid variables.'
def __init__(self, elementNode):
'Set defaults.'
self.targets = evaluate.getElementNodesByKey(elementNode, 'target')

View File

@ -0,0 +1,157 @@
"""
Sponge slice.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.geometry_tools import path
from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
import math
import random
import time
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getGeometryOutput(derivation, elementNode):
"Get vector3 vertexes from attribute dictionary."
if derivation == None:
derivation = SpongeSliceDerivation(elementNode)
awayPoints = []
vector3Path = euclidean.getVector3Path(euclidean.getSquareLoopWiddershins(-derivation.inradius, derivation.inradius))
geometryOutput = lineation.SideLoop(vector3Path).getManipulationPluginLoops(elementNode)
minimumDistanceFromOther = derivation.wallThickness + derivation.minimumRadius + derivation.minimumRadius
if derivation.inradiusMinusRadiusThickness.real <= 0.0 or derivation.inradiusMinusRadiusThickness.imag <= 0.0:
return geometryOutput
for point in derivation.path:
if abs(point.x) <= derivation.inradiusMinusRadiusThickness.real and abs(point.y) <= derivation.inradiusMinusRadiusThickness.imag:
awayPoints.append(point)
awayCircles = []
for point in awayPoints:
if getIsPointAway(minimumDistanceFromOther, point, awayCircles):
awayCircles.append(SpongeCircle(point, derivation.minimumRadius))
averagePotentialBubbleArea = derivation.potentialBubbleArea / float(len(awayCircles))
averageBubbleRadius = math.sqrt(averagePotentialBubbleArea / math.pi) - 0.5 * derivation.wallThickness
sides = -4 * (max(evaluate.getSidesBasedOnPrecision(elementNode, averageBubbleRadius), 4) / 4)
sideAngle = math.pi / sides
cosSide = math.cos(sideAngle)
overlapArealRatio = (1 - cosSide) / cosSide
for circleIndex, circle in enumerate(awayCircles):
otherCircles = awayCircles[: circleIndex] + awayCircles[circleIndex + 1 :]
circle.radius = circle.getRadius(circle.center, derivation, otherCircles, overlapArealRatio)
if derivation.searchAttempts > 0:
for circleIndex, circle in enumerate(awayCircles):
otherCircles = awayCircles[: circleIndex] + awayCircles[circleIndex + 1 :]
circle.moveCircle(derivation, otherCircles, overlapArealRatio)
for circle in awayCircles:
vector3Path = euclidean.getVector3Path(euclidean.getComplexPolygon(circle.center.dropAxis(), circle.radius, sides, sideAngle))
geometryOutput += lineation.SideLoop(vector3Path).getManipulationPluginLoops(elementNode)
return geometryOutput
def getGeometryOutputByArguments(arguments, elementNode):
"Get vector3 vertexes from attribute dictionary by arguments."
return getGeometryOutput(None, elementNode)
def getIsPointAway(minimumDistance, point, spongeCircles):
'Determine if the point is at least the minimumDistance away from other points.'
for otherSpongeCircle in spongeCircles:
if abs(otherSpongeCircle.center - point) < minimumDistance:
return False
return True
def getNewDerivation(elementNode):
'Get new derivation.'
return SpongeSliceDerivation(elementNode)
def processElementNode(elementNode):
"Process the xml element."
path.convertElementNode(elementNode, getGeometryOutput(None, elementNode))
class SpongeCircle:
"Class to hold sponge circle."
def __init__(self, center, radius=0.0):
'Initialize.'
self.center = center
self.radius = radius
def getRadius(self, center, derivation, otherCircles, overlapArealRatio):
'Get sponge bubble radius.'
radius = 987654321.0
for otherSpongeCircle in otherCircles:
distance = abs(otherSpongeCircle.center.dropAxis() - center.dropAxis())
radius = min(distance - derivation.wallThickness - otherSpongeCircle.radius, radius)
overlapAreal = overlapArealRatio * radius
radius = min(derivation.inradiusMinusThickness.real + overlapAreal - abs(center.x), radius)
return min(derivation.inradiusMinusThickness.imag + overlapAreal - abs(center.y), radius)
def moveCircle(self, derivation, otherCircles, overlapArealRatio):
'Move circle into an open spot.'
angle = (abs(self.center) + self.radius) % euclidean.globalTau
movedCenter = self.center
searchRadius = derivation.searchRadiusOverRadius * self.radius
distanceIncrement = searchRadius / float(derivation.searchAttempts)
distance = 0.0
greatestRadius = self.radius
searchCircles = []
searchCircleDistance = searchRadius + searchRadius + self.radius + derivation.wallThickness
for otherCircle in otherCircles:
if abs(self.center - otherCircle.center) <= searchCircleDistance + otherCircle.radius:
searchCircles.append(otherCircle)
for attemptIndex in xrange(derivation.searchAttempts):
angle += euclidean.globalGoldenAngle
distance += distanceIncrement
offset = distance * euclidean.getWiddershinsUnitPolar(angle)
attemptCenter = self.center + Vector3(offset.real, offset.imag)
radius = self.getRadius(attemptCenter, derivation, searchCircles, overlapArealRatio)
if radius > greatestRadius:
greatestRadius = radius
movedCenter = attemptCenter
self.center = movedCenter
self.radius = greatestRadius
class SpongeSliceDerivation:
"Class to hold sponge slice variables."
def __init__(self, elementNode):
'Initialize.'
elementNode.attributes['closed'] = 'true'
self.density = evaluate.getEvaluatedFloat(1.0, elementNode, 'density')
self.minimumRadiusOverThickness = evaluate.getEvaluatedFloat(1.0, elementNode, 'minimumRadiusOverThickness')
self.mobile = evaluate.getEvaluatedBoolean(False, elementNode, 'mobile')
self.inradius = lineation.getInradius(complex(10.0, 10.0), elementNode)
self.path = None
if 'path' in elementNode.attributes:
self.path = evaluate.getPathByKey([], elementNode, 'path')
self.searchAttempts = evaluate.getEvaluatedInt(0, elementNode, 'searchAttempts')
self.searchRadiusOverRadius = evaluate.getEvaluatedFloat(1.0, elementNode, 'searchRadiusOverRadius')
self.seed = evaluate.getEvaluatedInt(None, elementNode, 'seed')
self.wallThickness = evaluate.getEvaluatedFloat(2.0 * setting.getEdgeWidth(elementNode), elementNode, 'wallThickness')
# Set derived variables.
self.halfWallThickness = 0.5 * self.wallThickness
self.inradiusMinusThickness = self.inradius - complex(self.wallThickness, self.wallThickness)
self.minimumRadius = evaluate.getEvaluatedFloat(self.minimumRadiusOverThickness * self.wallThickness, elementNode, 'minimumRadius')
self.inradiusMinusRadiusThickness = self.inradiusMinusThickness - complex(self.minimumRadius, self.minimumRadius)
self.potentialBubbleArea = 4.0 * self.inradiusMinusThickness.real * self.inradiusMinusThickness.imag
if self.path == None:
radiusPlusHalfThickness = self.minimumRadius + self.halfWallThickness
numberOfPoints = int(math.ceil(self.density * self.potentialBubbleArea / math.pi / radiusPlusHalfThickness / radiusPlusHalfThickness))
self.path = []
if self.seed == None:
self.seed = time.time()
print('Sponge slice seed used was: %s' % self.seed)
random.seed(self.seed)
for pointIndex in xrange(numberOfPoints):
point = euclidean.getRandomComplex(-self.inradiusMinusRadiusThickness, self.inradiusMinusRadiusThickness)
self.path.append(Vector3(point.real, point.imag))

View File

@ -0,0 +1,80 @@
"""
Square path.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.geometry_tools import path
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getGeometryOutput(derivation, elementNode):
"Get vector3 vertexes from attribute dictionary."
if derivation == None:
derivation = SquareDerivation(elementNode)
topRight = complex(derivation.topDemiwidth, derivation.demiheight)
topLeft = complex(-derivation.topDemiwidth, derivation.demiheight)
bottomLeft = complex(-derivation.bottomDemiwidth, -derivation.demiheight)
bottomRight = complex(derivation.bottomDemiwidth, -derivation.demiheight)
if derivation.interiorAngle != 90.0:
interiorPlaneAngle = euclidean.getWiddershinsUnitPolar(math.radians(derivation.interiorAngle - 90.0))
topRight = (topRight - bottomRight) * interiorPlaneAngle + bottomRight
topLeft = (topLeft - bottomLeft) * interiorPlaneAngle + bottomLeft
lineation.setClosedAttribute(elementNode, derivation.revolutions)
complexLoop = [topRight, topLeft, bottomLeft, bottomRight]
originalLoop = complexLoop[:]
for revolution in xrange(1, derivation.revolutions):
complexLoop += originalLoop
spiral = lineation.Spiral(derivation.spiral, 0.25)
loop = []
loopCentroid = euclidean.getLoopCentroid(originalLoop)
for point in complexLoop:
unitPolar = euclidean.getNormalized(point - loopCentroid)
loop.append(spiral.getSpiralPoint(unitPolar, Vector3(point.real, point.imag)))
return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop(loop, 0.5 * math.pi))
def getGeometryOutputByArguments(arguments, elementNode):
"Get vector3 vertexes from attribute dictionary by arguments."
if len(arguments) < 1:
return getGeometryOutput(None, elementNode)
inradius = 0.5 * euclidean.getFloatFromValue(arguments[0])
elementNode.attributes['inradius.x'] = str(inradius)
if len(arguments) > 1:
inradius = 0.5 * euclidean.getFloatFromValue(arguments[1])
elementNode.attributes['inradius.y'] = str(inradius)
return getGeometryOutput(None, elementNode)
def getNewDerivation(elementNode):
'Get new derivation.'
return SquareDerivation(elementNode)
def processElementNode(elementNode):
"Process the xml element."
path.convertElementNode(elementNode, getGeometryOutput(None, elementNode))
class SquareDerivation:
"Class to hold square variables."
def __init__(self, elementNode):
'Set defaults.'
self.inradius = lineation.getInradius(complex(1.0, 1.0), elementNode)
self.demiwidth = lineation.getFloatByPrefixBeginEnd(elementNode, 'demiwidth', 'width', self.inradius.real)
self.demiheight = lineation.getFloatByPrefixBeginEnd(elementNode, 'demiheight', 'height', self.inradius.imag)
self.bottomDemiwidth = lineation.getFloatByPrefixBeginEnd(elementNode, 'bottomdemiwidth', 'bottomwidth', self.demiwidth)
self.topDemiwidth = lineation.getFloatByPrefixBeginEnd(elementNode, 'topdemiwidth', 'topwidth', self.demiwidth)
self.interiorAngle = evaluate.getEvaluatedFloat(90.0, elementNode, 'interiorangle')
self.revolutions = evaluate.getEvaluatedInt(1, elementNode, 'revolutions')
self.spiral = evaluate.getVector3ByPrefix(None, elementNode, 'spiral')

View File

@ -0,0 +1,116 @@
"""
Teardrop path.
"""
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 fabmetheus_utilities.geometry.creation import extrude
from fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.geometry_tools import path
from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def addNegativesByRadius(elementNode, end, negatives, radius, start):
"Add teardrop drill hole to negatives."
if radius <= 0.0:
return
copyShallow = elementNode.getCopyShallow()
extrude.setElementNodeToEndStart(copyShallow, end, start)
extrudeDerivation = extrude.ExtrudeDerivation(copyShallow)
extrude.addNegatives(extrudeDerivation, negatives, [getTeardropPathByEndStart(elementNode, end, radius, start)])
def getGeometryOutput(derivation, elementNode):
"Get vector3 vertexes from attribute dictionary."
if derivation == None:
derivation = TeardropDerivation(elementNode)
teardropPath = getTeardropPath(
derivation.inclination, derivation.overhangRadians, derivation.overhangSpan, derivation.radiusArealized, derivation.sides)
return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop(teardropPath))
def getGeometryOutputByArguments(arguments, elementNode):
"Get vector3 vertexes from attribute dictionary by arguments."
evaluate.setAttributesByArguments(['radius', 'inclination'], arguments, elementNode)
return getGeometryOutput(None, elementNode)
def getInclination(end, start):
"Get inclination."
if end == None or start == None:
return 0.0
endMinusStart = end - start
return math.atan2(endMinusStart.z, abs(endMinusStart.dropAxis()))
def getNewDerivation(elementNode):
'Get new derivation.'
return TeardropDerivation(elementNode)
def getTeardropPath(inclination, overhangRadians, overhangSpan, radiusArealized, sides):
"Get vector3 teardrop path."
sideAngle = 2.0 * math.pi / float(sides)
overhangPlaneAngle = euclidean.getWiddershinsUnitPolar(overhangRadians)
overhangRadians = math.atan2(overhangPlaneAngle.imag, overhangPlaneAngle.real * math.cos(inclination))
tanOverhangAngle = math.tan(overhangRadians)
beginAngle = overhangRadians
beginMinusEndAngle = math.pi + overhangRadians + overhangRadians
withinSides = int(math.ceil(beginMinusEndAngle / sideAngle))
withinSideAngle = -beginMinusEndAngle / float(withinSides)
teardropPath = []
for side in xrange(withinSides + 1):
unitPolar = euclidean.getWiddershinsUnitPolar(beginAngle)
teardropPath.append(unitPolar * radiusArealized)
beginAngle += withinSideAngle
firstPoint = teardropPath[0]
if overhangSpan <= 0.0:
teardropPath.append(complex(0.0, firstPoint.imag + firstPoint.real / tanOverhangAngle))
else:
deltaX = (radiusArealized - firstPoint.imag) * tanOverhangAngle
overhangPoint = complex(firstPoint.real - deltaX, radiusArealized)
remainingDeltaX = max(0.0, overhangPoint.real - 0.5 * overhangSpan )
overhangPoint += complex(-remainingDeltaX, remainingDeltaX / tanOverhangAngle)
teardropPath.append(complex(-overhangPoint.real, overhangPoint.imag))
teardropPath.append(overhangPoint)
return euclidean.getVector3Path(teardropPath)
def getTeardropPathByEndStart(elementNode, end, radius, start):
"Get vector3 teardrop path by end and start."
inclination = getInclination(end, start)
sides = evaluate.getSidesMinimumThreeBasedOnPrecisionSides(elementNode, radius)
radiusArealized = evaluate.getRadiusArealizedBasedOnAreaRadius(elementNoderadius, sides)
return getTeardropPath(inclination, setting.getOverhangRadians(elementNode), setting.getOverhangSpan(elementNode), radiusArealized, sides)
def processElementNode(elementNode):
"Process the xml element."
path.convertElementNode(elementNode, getGeometryOutput(None, elementNode))
class TeardropDerivation:
"Class to hold teardrop variables."
def __init__(self, elementNode):
'Set defaults.'
end = evaluate.getVector3ByPrefix(None, elementNode, 'end')
start = evaluate.getVector3ByPrefix(Vector3(), elementNode, 'start')
inclinationDegree = math.degrees(getInclination(end, start))
self.elementNode = elementNode
self.inclination = math.radians(evaluate.getEvaluatedFloat(inclinationDegree, elementNode, 'inclination'))
self.overhangRadians = setting.getOverhangRadians(elementNode)
self.overhangSpan = setting.getOverhangSpan(elementNode)
self.radius = lineation.getFloatByPrefixBeginEnd(elementNode, 'radius', 'diameter', 1.0)
size = evaluate.getEvaluatedFloat(None, elementNode, 'size')
if size != None:
self.radius = 0.5 * size
self.sides = evaluate.getEvaluatedFloat(None, elementNode, 'sides')
if self.sides == None:
self.sides = evaluate.getSidesMinimumThreeBasedOnPrecisionSides(elementNode, self.radius)
self.radiusArealized = evaluate.getRadiusArealizedBasedOnAreaRadius(elementNode, self.radius, self.sides)

View File

@ -0,0 +1,63 @@
"""
Text vertexes.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.geometry_tools import path
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities import euclidean
from fabmetheus_utilities import svg_reader
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getGeometryOutput(derivation, elementNode):
"Get vector3 vertexes from attributes."
if derivation == None:
derivation = TextDerivation(elementNode)
if derivation.textString == '':
print('Warning, textString is empty in getGeometryOutput in text for:')
print(elementNode)
return []
geometryOutput = []
for textComplexLoop in svg_reader.getTextComplexLoops(derivation.fontFamily, derivation.fontSize, derivation.textString):
textComplexLoop.reverse()
vector3Path = euclidean.getVector3Path(textComplexLoop)
sideLoop = lineation.SideLoop(vector3Path)
sideLoop.rotate(elementNode)
geometryOutput += lineation.getGeometryOutputByManipulation(elementNode, sideLoop)
return geometryOutput
def getGeometryOutputByArguments(arguments, elementNode):
"Get vector3 vertexes from attribute dictionary by arguments."
evaluate.setAttributesByArguments(['text', 'fontSize', 'fontFamily'], arguments, elementNode)
return getGeometryOutput(None, elementNode)
def getNewDerivation(elementNode):
'Get new derivation.'
return TextDerivation(elementNode)
def processElementNode(elementNode):
"Process the xml element."
path.convertElementNode(elementNode, getGeometryOutput(None, elementNode))
class TextDerivation:
"Class to hold text variables."
def __init__(self, elementNode):
'Set defaults.'
self.fontFamily = evaluate.getEvaluatedString('Gentium Basic Regular', elementNode, 'font-family')
self.fontFamily = evaluate.getEvaluatedString(self.fontFamily, elementNode, 'fontFamily')
self.fontSize = evaluate.getEvaluatedFloat(12.0, elementNode, 'font-size')
self.fontSize = evaluate.getEvaluatedFloat(self.fontSize, elementNode, 'fontSize')
self.textString = elementNode.getTextContent()
self.textString = evaluate.getEvaluatedString(self.textString, elementNode, 'text')

View File

@ -0,0 +1,9 @@
#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
import os
import sys
numberOfLevelsDeepInPackageHierarchy = 3
packageFilePath = os.path.abspath(__file__)
for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ):
packageFilePath = os.path.dirname( packageFilePath )
if packageFilePath not in sys.path:
sys.path.insert( 0, packageFilePath )

View File

@ -0,0 +1,178 @@
"""
Boolean geometry dictionary object.
"""
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 fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities import euclidean
from fabmetheus_utilities import xml_simple_writer
import cStringIO
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Nophead <http://hydraraptor.blogspot.com/>\nArt of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/21/04 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getAllPaths(paths, xmlObject):
'Get all paths.'
for archivableObject in xmlObject.archivableObjects:
paths += archivableObject.getPaths()
return paths
def getAllTransformedPaths(transformedPaths, xmlObject):
'Get all transformed paths.'
for archivableObject in xmlObject.archivableObjects:
transformedPaths += archivableObject.getTransformedPaths()
return transformedPaths
def getAllTransformedVertexes(transformedVertexes, xmlObject):
'Get all transformed vertexes.'
for archivableObject in xmlObject.archivableObjects:
transformedVertexes += archivableObject.getTransformedVertexes()
return transformedVertexes
def getAllVertexes(vertexes, xmlObject):
'Get all vertexes.'
for archivableObject in xmlObject.archivableObjects:
vertexes += archivableObject.getVertexes()
return vertexes
def processElementNode(elementNode):
'Process the xml element.'
evaluate.processArchivable( Dictionary, elementNode)
class Dictionary:
'A dictionary object.'
def __init__(self):
'Add empty lists.'
self.archivableObjects = []
self.elementNode = None
def __repr__(self):
'Get the string representation of this object info.'
output = xml_simple_writer.getBeginGeometryXMLOutput(self.elementNode)
self.addXML( 1, output )
return xml_simple_writer.getEndGeometryXMLString(output)
def addXML(self, depth, output):
'Add xml for this object.'
attributeCopy = {}
if self.elementNode != None:
attributeCopy = evaluate.getEvaluatedDictionaryByCopyKeys(['paths', 'target', 'vertexes'], self.elementNode)
euclidean.removeElementsFromDictionary(attributeCopy, matrix.getKeysM())
euclidean.removeTrueFromDictionary(attributeCopy, 'visible')
innerOutput = cStringIO.StringIO()
self.addXMLInnerSection(depth + 1, innerOutput)
self.addXMLArchivableObjects(depth + 1, innerOutput)
xml_simple_writer.addBeginEndInnerXMLTag(attributeCopy, depth, innerOutput.getvalue(), self.getXMLLocalName(), output)
def addXMLArchivableObjects(self, depth, output):
'Add xml for this object.'
xml_simple_writer.addXMLFromObjects( depth, self.archivableObjects, output )
def addXMLInnerSection(self, depth, output):
'Add xml section for this object.'
pass
def createShape(self):
'Create the shape.'
pass
def getAttributes(self):
'Get attribute table.'
if self.elementNode == None:
return {}
return self.elementNode.attributes
def getComplexTransformedPathLists(self):
'Get complex transformed path lists.'
complexTransformedPathLists = []
for archivableObject in self.archivableObjects:
complexTransformedPathLists.append(euclidean.getComplexPaths(archivableObject.getTransformedPaths()))
return complexTransformedPathLists
def getFabricationExtension(self):
'Get fabrication extension.'
return 'xml'
def getFabricationText(self, addLayerTemplate):
'Get fabrication text.'
return self.__repr__()
def getGeometryOutput(self):
'Get geometry output dictionary.'
shapeOutput = []
for visibleObject in evaluate.getVisibleObjects(self.archivableObjects):
geometryOutput = visibleObject.getGeometryOutput()
if geometryOutput != None:
visibleObject.transformGeometryOutput(geometryOutput)
shapeOutput.append(geometryOutput)
if len(shapeOutput) < 1:
return None
return {self.getXMLLocalName() : {'shapes' : shapeOutput}}
def getMatrix4X4(self):
"Get the matrix4X4."
return None
def getMatrixChainTetragrid(self):
'Get the matrix chain tetragrid.'
return self.elementNode.parentNode.xmlObject.getMatrixChainTetragrid()
def getMinimumZ(self):
'Get the minimum z.'
return None
def getPaths(self):
'Get all paths.'
return getAllPaths([], self)
def getTransformedPaths(self):
'Get all transformed paths.'
return getAllTransformedPaths([], self)
def getTransformedVertexes(self):
'Get all transformed vertexes.'
return getAllTransformedVertexes([], self)
def getTriangleMeshes(self):
'Get all triangleMeshes.'
triangleMeshes = []
for archivableObject in self.archivableObjects:
triangleMeshes += archivableObject.getTriangleMeshes()
return triangleMeshes
def getType(self):
'Get type.'
return self.__class__.__name__
def getVertexes(self):
'Get all vertexes.'
return getAllVertexes([], self)
def getVisible(self):
'Get visible.'
return False
def getXMLLocalName(self):
'Get xml local name.'
return self.__class__.__name__.lower()
def setToElementNode(self, elementNode):
'Set the shape of this carvable object info.'
self.elementNode = elementNode
elementNode.parentNode.xmlObject.archivableObjects.append(self)
def transformGeometryOutput(self, geometryOutput):
'Transform the geometry output by the local matrix4x4.'
if self.getMatrix4X4() != None:
matrix.transformVector3sByMatrix(self.getMatrix4X4().tetragrid, matrix.getVertexes(geometryOutput))

View File

@ -0,0 +1,171 @@
"""
Face of a triangle mesh.
"""
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 fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities.vector3index import Vector3Index
from fabmetheus_utilities import euclidean
from fabmetheus_utilities import gcodec
from fabmetheus_utilities import intercircle
from fabmetheus_utilities import xml_simple_reader
from fabmetheus_utilities import xml_simple_writer
import cStringIO
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def addFaces(geometryOutput, faces):
'Add the faces.'
if geometryOutput.__class__ == list:
for element in geometryOutput:
addFaces(element, faces)
return
if geometryOutput.__class__ != dict:
return
for geometryOutputKey in geometryOutput.keys():
geometryOutputValue = geometryOutput[geometryOutputKey]
if geometryOutputKey == 'face':
for face in geometryOutputValue:
faces.append(face)
else:
addFaces(geometryOutputValue, faces)
def addGeometryList(elementNode, faces):
"Add vertex elements to an xml element."
for face in faces:
faceElement = xml_simple_reader.ElementNode()
face.addToAttributes( faceElement.attributes )
faceElement.localName = 'face'
faceElement.parentNode = elementNode
elementNode.childNodes.append( faceElement )
def getCommonVertexIndex( edgeFirst, edgeSecond ):
"Get the vertex index that both edges have in common."
for edgeFirstVertexIndex in edgeFirst.vertexIndexes:
if edgeFirstVertexIndex == edgeSecond.vertexIndexes[0] or edgeFirstVertexIndex == edgeSecond.vertexIndexes[1]:
return edgeFirstVertexIndex
print("Inconsistent GNU Triangulated Surface")
print(edgeFirst)
print(edgeSecond)
return 0
def getFaces(geometryOutput):
'Get the faces.'
faces = []
addFaces(geometryOutput, faces)
return faces
def processElementNode(elementNode):
"Process the xml element."
face = Face()
face.index = len(elementNode.parentNode.xmlObject.faces)
for vertexIndexIndex in xrange(3):
face.vertexIndexes.append(evaluate.getEvaluatedInt(None, elementNode, 'vertex' + str(vertexIndexIndex)))
elementNode.parentNode.xmlObject.faces.append(face)
class Edge:
"An edge of a triangle mesh."
def __init__(self):
"Set the face indexes to None."
self.faceIndexes = []
self.vertexIndexes = []
self.zMaximum = None
self.zMinimum = None
def __repr__(self):
"Get the string representation of this Edge."
return str( self.index ) + ' ' + str( self.faceIndexes ) + ' ' + str(self.vertexIndexes)
def addFaceIndex( self, faceIndex ):
"Add first None face index to input face index."
self.faceIndexes.append( faceIndex )
def getFromVertexIndexes( self, edgeIndex, vertexIndexes ):
"Initialize from two vertex indices."
self.index = edgeIndex
self.vertexIndexes = vertexIndexes[:]
self.vertexIndexes.sort()
return self
class Face:
"A face of a triangle mesh."
def __init__(self):
"Initialize."
self.edgeIndexes = []
self.index = None
self.vertexIndexes = []
def __repr__(self):
"Get the string representation of this object info."
output = cStringIO.StringIO()
self.addXML( 2, output )
return output.getvalue()
def addToAttributes(self, attributes):
"Add to the attribute dictionary."
for vertexIndexIndex in xrange(len(self.vertexIndexes)):
vertexIndex = self.vertexIndexes[vertexIndexIndex]
attributes['vertex' + str(vertexIndexIndex)] = str(vertexIndex)
def addXML(self, depth, output):
"Add the xml for this object."
attributes = {}
self.addToAttributes(attributes)
xml_simple_writer.addClosedXMLTag( attributes, depth, 'face', output )
def copy(self):
'Get the copy of this face.'
faceCopy = Face()
faceCopy.edgeIndexes = self.edgeIndexes[:]
faceCopy.index = self.index
faceCopy.vertexIndexes = self.vertexIndexes[:]
return faceCopy
def getFromEdgeIndexes( self, edgeIndexes, edges, faceIndex ):
"Initialize from edge indices."
if len(self.vertexIndexes) > 0:
return
self.index = faceIndex
self.edgeIndexes = edgeIndexes
for edgeIndex in edgeIndexes:
edges[ edgeIndex ].addFaceIndex( faceIndex )
for triangleIndex in xrange(3):
indexFirst = ( 3 - triangleIndex ) % 3
indexSecond = ( 4 - triangleIndex ) % 3
self.vertexIndexes.append( getCommonVertexIndex( edges[ edgeIndexes[ indexFirst ] ], edges[ edgeIndexes[ indexSecond ] ] ) )
return self
def setEdgeIndexesToVertexIndexes( self, edges, edgeTable ):
"Set the edge indexes to the vertex indexes."
if len(self.edgeIndexes) > 0:
return
for triangleIndex in xrange(3):
indexFirst = ( 3 - triangleIndex ) % 3
indexSecond = ( 4 - triangleIndex ) % 3
vertexIndexFirst = self.vertexIndexes[ indexFirst ]
vertexIndexSecond = self.vertexIndexes[ indexSecond ]
vertexIndexPair = [ vertexIndexFirst, vertexIndexSecond ]
vertexIndexPair.sort()
edgeIndex = len( edges )
if str( vertexIndexPair ) in edgeTable:
edgeIndex = edgeTable[ str( vertexIndexPair ) ]
else:
edgeTable[ str( vertexIndexPair ) ] = edgeIndex
edge = Edge().getFromVertexIndexes( edgeIndex, vertexIndexPair )
edges.append( edge )
edges[ edgeIndex ].addFaceIndex( self.index )
self.edgeIndexes.append( edgeIndex )

View File

@ -0,0 +1,204 @@
"""
Path.
"""
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 fabmetheus_utilities.geometry.geometry_tools import dictionary
from fabmetheus_utilities.geometry.geometry_tools import vertex
from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
from fabmetheus_utilities import svg_writer
from fabmetheus_utilities import xml_simple_reader
from fabmetheus_utilities import xml_simple_writer
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def convertElementNode(elementNode, geometryOutput):
'Convert the xml element by geometryOutput.'
if geometryOutput == None:
return
if len(geometryOutput) < 1:
return
if len(geometryOutput) == 1:
firstLoop = geometryOutput[0]
if firstLoop.__class__ == list:
geometryOutput = firstLoop
firstElement = geometryOutput[0]
if firstElement.__class__ == list:
if len(firstElement) > 1:
convertElementNodeRenameByPaths(elementNode, geometryOutput)
else:
convertElementNodeByPath(elementNode, firstElement)
else:
convertElementNodeByPath(elementNode, geometryOutput)
def convertElementNodeByPath(elementNode, geometryOutput):
'Convert the xml element to a path xml element.'
createLinkPath(elementNode)
elementNode.xmlObject.vertexes = geometryOutput
vertex.addGeometryList(elementNode, geometryOutput)
def convertElementNodeRenameByPaths(elementNode, geometryOutput):
'Convert the xml element to a path xml element and add paths.'
createLinkPath(elementNode)
for geometryOutputChild in geometryOutput:
pathElement = xml_simple_reader.ElementNode()
pathElement.setParentAddToChildNodes(elementNode)
convertElementNodeByPath(pathElement, geometryOutputChild)
def createLinkPath(elementNode):
'Create and link a path object.'
elementNode.localName = 'path'
elementNode.linkObject(Path())
def processElementNode(elementNode):
'Process the xml element.'
evaluate.processArchivable(Path, elementNode)
class Path(dictionary.Dictionary):
'A path.'
def __init__(self):
'Add empty lists.'
dictionary.Dictionary.__init__(self)
self.matrix4X4 = matrix.Matrix()
self.oldChainTetragrid = None
self.transformedPath = None
self.vertexes = []
def addXMLInnerSection(self, depth, output):
'Add the xml section for this object.'
if self.matrix4X4 != None:
self.matrix4X4.addXML(depth, output)
xml_simple_writer.addXMLFromVertexes(depth, output, self.vertexes)
def getFabricationExtension(self):
'Get fabrication extension.'
return 'svg'
def getFabricationText(self, addLayerTemplate):
'Get fabrication text.'
carving = SVGFabricationCarving(addLayerTemplate, self.elementNode)
carving.setCarveLayerHeight(setting.getSheetThickness(self.elementNode))
carving.processSVGElement(self.elementNode.getOwnerDocument().fileName)
return str(carving)
def getMatrix4X4(self):
"Get the matrix4X4."
return self.matrix4X4
def getMatrixChainTetragrid(self):
'Get the matrix chain tetragrid.'
return matrix.getTetragridTimesOther(self.elementNode.parentNode.xmlObject.getMatrixChainTetragrid(), self.matrix4X4.tetragrid)
def getPaths(self):
'Get all paths.'
self.transformedPath = None
if len(self.vertexes) > 0:
return dictionary.getAllPaths([self.vertexes], self)
return dictionary.getAllPaths([], self)
def getTransformedPaths(self):
'Get all transformed paths.'
if self.elementNode == None:
return dictionary.getAllPaths([self.vertexes], self)
chainTetragrid = self.getMatrixChainTetragrid()
if self.oldChainTetragrid != chainTetragrid:
self.oldChainTetragrid = chainTetragrid
self.transformedPath = None
if self.transformedPath == None:
self.transformedPath = matrix.getTransformedVector3s(chainTetragrid, self.vertexes)
if len(self.transformedPath) > 0:
return dictionary.getAllTransformedPaths([self.transformedPath], self)
return dictionary.getAllTransformedPaths([], self)
class SVGFabricationCarving:
'An svg carving.'
def __init__(self, addLayerTemplate, elementNode):
'Add empty lists.'
self.addLayerTemplate = addLayerTemplate
self.elementNode = elementNode
self.layerHeight = 1.0
self.loopLayers = []
def __repr__(self):
'Get the string representation of this carving.'
return self.getCarvedSVG()
def addXML(self, depth, output):
'Add xml for this object.'
xml_simple_writer.addXMLFromObjects(depth, self.loopLayers, output)
def getCarveBoundaryLayers(self):
'Get the boundary layers.'
return self.loopLayers
def getCarveCornerMaximum(self):
'Get the corner maximum of the vertexes.'
return self.cornerMaximum
def getCarveCornerMinimum(self):
'Get the corner minimum of the vertexes.'
return self.cornerMinimum
def getCarvedSVG(self):
'Get the carved svg text.'
return svg_writer.getSVGByLoopLayers(self.addLayerTemplate, self, self.loopLayers)
def getCarveLayerHeight(self):
'Get the layer height.'
return self.layerHeight
def getFabmetheusXML(self):
'Return the fabmetheus XML.'
return self.elementNode.getOwnerDocument().getOriginalRoot()
def getInterpretationSuffix(self):
'Return the suffix for a carving.'
return 'svg'
def processSVGElement(self, fileName):
'Parse SVG element and store the layers.'
self.fileName = fileName
paths = self.elementNode.xmlObject.getPaths()
oldZ = None
self.loopLayers = []
loopLayer = None
for path in paths:
if len(path) > 0:
z = path[0].z
if z != oldZ:
loopLayer = euclidean.LoopLayer(z)
self.loopLayers.append(loopLayer)
oldZ = z
loopLayer.loops.append(euclidean.getComplexPath(path))
if len(self.loopLayers) < 1:
return
self.cornerMaximum = Vector3(-987654321.0, -987654321.0, -987654321.0)
self.cornerMinimum = Vector3(987654321.0, 987654321.0, 987654321.0)
svg_writer.setSVGCarvingCorners(self.cornerMaximum, self.cornerMinimum, self.layerHeight, self.loopLayers)
def setCarveImportRadius( self, importRadius ):
'Set the import radius.'
pass
def setCarveIsCorrectMesh( self, isCorrectMesh ):
'Set the is correct mesh flag.'
pass
def setCarveLayerHeight( self, layerHeight ):
'Set the layer height.'
self.layerHeight = layerHeight

View File

@ -0,0 +1,9 @@
#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
import os
import sys
numberOfLevelsDeepInPackageHierarchy = 4
packageFilePath = os.path.abspath(__file__)
for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ):
packageFilePath = os.path.dirname( packageFilePath )
if packageFilePath not in sys.path:
sys.path.insert( 0, packageFilePath )

View File

@ -0,0 +1,51 @@
"""
Arc vertexes.
From:
http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
from fabmetheus_utilities import svg_reader
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getArcPath(elementNode):
"Get the arc path.rx ry x-axis-rotation large-arc-flag sweep-flag"
begin = elementNode.getPreviousVertex(Vector3())
end = evaluate.getVector3FromElementNode(elementNode)
largeArcFlag = evaluate.getEvaluatedBoolean(True, elementNode, 'largeArcFlag')
radius = lineation.getComplexByPrefix(elementNode, 'radius', complex(1.0, 1.0))
sweepFlag = evaluate.getEvaluatedBoolean(True, elementNode, 'sweepFlag')
xAxisRotation = math.radians(evaluate.getEvaluatedFloat(0.0, elementNode, 'xAxisRotation'))
arcComplexes = svg_reader.getArcComplexes(begin.dropAxis(), end.dropAxis(), largeArcFlag, radius, sweepFlag, xAxisRotation)
path = []
if len(arcComplexes) < 1:
return []
incrementZ = (end.z - begin.z) / float(len(arcComplexes))
z = begin.z
for pointIndex in xrange(len(arcComplexes)):
pointComplex = arcComplexes[pointIndex]
z += incrementZ
path.append(Vector3(pointComplex.real, pointComplex.imag, z))
if len(path) > 0:
path[-1] = end
return path
def processElementNode(elementNode):
"Process the xml element."
elementNode.parentNode.xmlObject.vertexes += getArcPath(elementNode)

View File

@ -0,0 +1,58 @@
"""
Cubic vertexes.
From:
http://www.w3.org/TR/SVG/paths.html#PathDataCubicBezierCommands
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import svg_reader
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getCubicPath(elementNode):
"Get the cubic path."
end = evaluate.getVector3FromElementNode(elementNode)
previousElementNode = elementNode.getPreviousElementNode()
if previousElementNode == None:
print('Warning, can not get previousElementNode in getCubicPath in cubic for:')
print(elementNode)
return [end]
begin = elementNode.getPreviousVertex(Vector3())
evaluatedControlPoints = evaluate.getTransformedPathByKey([], elementNode, 'controlPoints')
if len(evaluatedControlPoints) > 1:
return getCubicPathByBeginEnd(begin, evaluatedControlPoints, elementNode, end)
controlPoint0 = evaluate.getVector3ByPrefix(None, elementNode, 'controlPoint0')
controlPoint1 = evaluate.getVector3ByPrefix(None, elementNode, 'controlPoint1')
if len(evaluatedControlPoints) == 1:
controlPoint1 = evaluatedControlPoints[0]
if controlPoint0 == None:
oldControlPoint = evaluate.getVector3ByPrefixes(previousElementNode, ['controlPoint','controlPoint1'], None)
if oldControlPoint == None:
oldControlPoints = evaluate.getTransformedPathByKey([], previousElementNode, 'controlPoints')
if len(oldControlPoints) > 0:
oldControlPoint = oldControlPoints[-1]
if oldControlPoint == None:
oldControlPoint = end
controlPoint0 = begin + begin - oldControlPoint
return getCubicPathByBeginEnd(begin, [controlPoint0, controlPoint1], elementNode, end)
def getCubicPathByBeginEnd(begin, controlPoints, elementNode, end):
"Get the cubic path by begin and end."
return svg_reader.getCubicPoints(begin, controlPoints, end, lineation.getNumberOfBezierPoints(begin, elementNode, end))
def processElementNode(elementNode):
"Process the xml element."
elementNode.parentNode.xmlObject.vertexes += getCubicPath(elementNode)

View File

@ -0,0 +1,45 @@
"""
Quadratic vertexes.
From:
http://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import svg_reader
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getQuadraticPath(elementNode):
"Get the quadratic path."
end = evaluate.getVector3FromElementNode(elementNode)
previousElementNode = elementNode.getPreviousElementNode()
if previousElementNode == None:
print('Warning, can not get previousElementNode in getQuadraticPath in quadratic for:')
print(elementNode)
return [end]
begin = elementNode.getPreviousVertex(Vector3())
controlPoint = evaluate.getVector3ByPrefix(None, elementNode, 'controlPoint')
if controlPoint == None:
oldControlPoint = evaluate.getVector3ByPrefixes(previousElementNode, ['controlPoint','controlPoint1'], None)
if oldControlPoint == None:
oldControlPoint = end
controlPoint = begin + begin - oldControlPoint
evaluate.addVector3ToElementNode(elementNode, 'controlPoint', controlPoint)
return svg_reader.getQuadraticPoints(begin, controlPoint, end, lineation.getNumberOfBezierPoints(begin, elementNode, end))
def processElementNode(elementNode):
"Process the xml element."
elementNode.parentNode.xmlObject.vertexes += getQuadraticPath(elementNode)

View File

@ -0,0 +1,45 @@
"""
Vertex of a triangle mesh.
"""
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 fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities import xml_simple_reader
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def addGeometryList(elementNode, vertexes):
"Add vertex elements to an xml element."
for vertex in vertexes:
vertexElement = getUnboundVertexElement(vertex)
vertexElement.parentNode = elementNode
elementNode.childNodes.append( vertexElement )
def addVertexToAttributes(attributes, vertex):
"Add to the attribute dictionary."
if vertex.x != 0.0:
attributes['x'] = str(vertex.x)
if vertex.y != 0.0:
attributes['y'] = str(vertex.y)
if vertex.z != 0.0:
attributes['z'] = str(vertex.z)
def getUnboundVertexElement(vertex):
"Add vertex element to an xml element."
vertexElement = xml_simple_reader.ElementNode()
addVertexToAttributes(vertexElement.attributes, vertex)
vertexElement.localName = 'vertex'
return vertexElement
def processElementNode(elementNode):
"Process the xml element."
elementNode.parentNode.xmlObject.vertexes.append(evaluate.getVector3FromElementNode(elementNode))

View File

@ -0,0 +1,12 @@
"""
This page is in the table of contents.
This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
"""
import os
import sys
numberOfLevelsDeepInPackageHierarchy = 3
packageFilePath = os.path.abspath(__file__)
for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ):
packageFilePath = os.path.dirname( packageFilePath )
if packageFilePath not in sys.path:
sys.path.insert( 0, packageFilePath )

View File

@ -0,0 +1,196 @@
"""
This page is in the table of contents.
The xml.py script is an import translator plugin to get a carving from an Art of Illusion xml file.
An import plugin is a script in the interpret_plugins folder which has the function getCarving. It is meant to be run from the interpret tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name.
The getCarving function takes the file name of an xml file and returns the carving.
An xml file can be exported from Art of Illusion by going to the "File" menu, then going into the "Export" menu item, then picking the XML choice. This will bring up the XML file chooser window, choose a place to save the file then click "OK". Leave the "compressFile" checkbox unchecked. All the objects from the scene will be exported, this plugin will ignore the light and camera. If you want to fabricate more than one object at a time, you can have multiple objects in the Art of Illusion scene and they will all be carved, then fabricated together.
"""
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 fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting
from fabmetheus_utilities.geometry.geometry_utilities import boolean_solid
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.solids import triangle_mesh
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
from fabmetheus_utilities import settings
from fabmetheus_utilities import xml_simple_writer
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Nophead <http://hydraraptor.blogspot.com/>\nArt of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/21/04 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getEmptyZLoops(archivableObjects, importRadius, shouldPrintWarning, z, zoneArrangement):
'Get loops at empty z level.'
emptyZ = zoneArrangement.getEmptyZ(z)
visibleObjects = evaluate.getVisibleObjects(archivableObjects)
visibleObjectLoopsList = boolean_solid.getVisibleObjectLoopsList(importRadius, visibleObjects, emptyZ)
loops = euclidean.getConcatenatedList(visibleObjectLoopsList)
if euclidean.isLoopListIntersecting(loops):
loops = boolean_solid.getLoopsUnion(importRadius, visibleObjectLoopsList)
if shouldPrintWarning:
print('Warning, the triangle mesh slice intersects itself in getExtruderPaths in boolean_geometry.')
print('Something will still be printed, but there is no guarantee that it will be the correct shape.')
print('Once the gcode is saved, you should check over the layer with a z of:')
print(z)
return loops
def getLoopLayers(archivableObjects, importRadius, layerHeight, maximumZ, shouldPrintWarning, z, zoneArrangement):
'Get loop layers.'
loopLayers = []
while z <= maximumZ:
triangle_mesh.getLoopLayerAppend(loopLayers, z).loops = getEmptyZLoops(archivableObjects, importRadius, True, z, zoneArrangement)
z += layerHeight
return loopLayers
def getMinimumZ(geometryObject):
'Get the minimum of the minimum z of the archivableObjects and the object.'
booleanGeometry = BooleanGeometry()
booleanGeometry.archivableObjects = geometryObject.archivableObjects
booleanGeometry.importRadius = setting.getImportRadius(geometryObject.elementNode)
booleanGeometry.layerHeight = setting.getLayerHeight(geometryObject.elementNode)
archivableMinimumZ = booleanGeometry.getMinimumZ()
geometryMinimumZ = geometryObject.getMinimumZ()
if archivableMinimumZ == None:
return geometryMinimumZ
if geometryMinimumZ == None:
return archivableMinimumZ
return min(archivableMinimumZ, geometryMinimumZ)
class BooleanGeometry:
'A boolean geometry scene.'
def __init__(self):
'Add empty lists.'
self.archivableObjects = []
self.belowLoops = []
self.importRadius = 0.6
self.layerHeight = 0.4
self.loopLayers = []
def __repr__(self):
'Get the string representation of this carving.'
elementNode = None
if len(self.archivableObjects) > 0:
elementNode = self.archivableObjects[0].elementNode
output = xml_simple_writer.getBeginGeometryXMLOutput(elementNode)
self.addXML( 1, output )
return xml_simple_writer.getEndGeometryXMLString(output)
def addXML(self, depth, output):
'Add xml for this object.'
xml_simple_writer.addXMLFromObjects( depth, self.archivableObjects, output )
def getCarveBoundaryLayers(self):
'Get the boundary layers.'
if self.getMinimumZ() == None:
return []
z = self.minimumZ + 0.5 * self.layerHeight
self.loopLayers = getLoopLayers(self.archivableObjects, self.importRadius, self.layerHeight, self.maximumZ, True, z, self.zoneArrangement)
self.cornerMaximum = Vector3(-912345678.0, -912345678.0, -912345678.0)
self.cornerMinimum = Vector3(912345678.0, 912345678.0, 912345678.0)
for loopLayer in self.loopLayers:
for loop in loopLayer.loops:
for point in loop:
pointVector3 = Vector3(point.real, point.imag, loopLayer.z)
self.cornerMaximum.maximize(pointVector3)
self.cornerMinimum.minimize(pointVector3)
self.cornerMaximum.z += self.halfHeight
self.cornerMinimum.z -= self.halfHeight
for loopLayerIndex in xrange(len(self.loopLayers) -1, -1, -1):
loopLayer = self.loopLayers[loopLayerIndex]
if len(loopLayer.loops) > 0:
return self.loopLayers[: loopLayerIndex + 1]
return []
def getCarveCornerMaximum(self):
'Get the corner maximum of the vertexes.'
return self.cornerMaximum
def getCarveCornerMinimum(self):
'Get the corner minimum of the vertexes.'
return self.cornerMinimum
def getCarveLayerHeight(self):
'Get the layer height.'
return self.layerHeight
def getFabmetheusXML(self):
'Return the fabmetheus XML.'
if len(self.archivableObjects) > 0:
return self.archivableObjects[0].elementNode.getOwnerDocument().getOriginalRoot()
return None
def getInterpretationSuffix(self):
'Return the suffix for a boolean carving.'
return 'xml'
def getMatrix4X4(self):
'Get the matrix4X4.'
return None
def getMatrixChainTetragrid(self):
'Get the matrix chain tetragrid.'
return None
def getMinimumZ(self):
'Get the minimum z.'
vertexes = []
for visibleObject in evaluate.getVisibleObjects(self.archivableObjects):
vertexes += visibleObject.getTransformedVertexes()
if len(vertexes) < 1:
return None
self.maximumZ = -912345678.0
self.minimumZ = 912345678.0
for vertex in vertexes:
self.maximumZ = max(self.maximumZ, vertex.z)
self.minimumZ = min(self.minimumZ, vertex.z)
self.zoneArrangement = triangle_mesh.ZoneArrangement(self.layerHeight, vertexes)
self.halfHeight = 0.5 * self.layerHeight
self.setActualMinimumZ()
return self.minimumZ
def getNumberOfEmptyZLoops(self, z):
'Get number of empty z loops.'
return len(getEmptyZLoops(self.archivableObjects, self.importRadius, False, z, self.zoneArrangement))
def setActualMinimumZ(self):
'Get the actual minimum z at the lowest rotated boundary layer.'
halfHeightOverMyriad = 0.0001 * self.halfHeight
while self.minimumZ < self.maximumZ:
if self.getNumberOfEmptyZLoops(self.minimumZ + halfHeightOverMyriad) > 0:
if self.getNumberOfEmptyZLoops(self.minimumZ - halfHeightOverMyriad) < 1:
return
increment = -self.halfHeight
while abs(increment) > halfHeightOverMyriad:
self.minimumZ += increment
increment = 0.5 * abs(increment)
if self.getNumberOfEmptyZLoops(self.minimumZ) > 0:
increment = -increment
self.minimumZ = round(self.minimumZ, -int(round(math.log10(halfHeightOverMyriad) + 1.5)))
return
self.minimumZ += self.layerHeight
def setCarveImportRadius( self, importRadius ):
'Set the import radius.'
self.importRadius = importRadius
def setCarveIsCorrectMesh( self, isCorrectMesh ):
'Set the is correct mesh flag.'
self.isCorrectMesh = isCorrectMesh
def setCarveLayerHeight( self, layerHeight ):
'Set the layer height.'
self.layerHeight = layerHeight

View File

@ -0,0 +1,235 @@
"""
This page is in the table of contents.
The xml.py script is an import translator plugin to get a carving from an Art of Illusion xml file.
An import plugin is a script in the interpret_plugins folder which has the function getCarving. It is meant to be run from the interpret tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name.
The getCarving function takes the file name of an xml file and returns the carving.
An xml file can be exported from Art of Illusion by going to the "File" menu, then going into the "Export" menu item, then picking the XML choice. This will bring up the XML file chooser window, choose a place to save the file then click "OK". Leave the "compressFile" checkbox unchecked. All the objects from the scene will be exported, this plugin will ignore the light and camera. If you want to fabricate more than one object at a time, you can have multiple objects in the Art of Illusion scene and they will all be carved, then fabricated together.
"""
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 fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.solids import group
from fabmetheus_utilities.geometry.solids import triangle_mesh
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
from fabmetheus_utilities import gcodec
from fabmetheus_utilities import intercircle
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Nophead <http://hydraraptor.blogspot.com/>\nArt of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/21/04 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def addLineLoopsIntersections( loopLoopsIntersections, loops, pointBegin, pointEnd ):
'Add intersections of the line with the loops.'
normalizedSegment = pointEnd - pointBegin
normalizedSegmentLength = abs( normalizedSegment )
if normalizedSegmentLength <= 0.0:
return
lineLoopsIntersections = []
normalizedSegment /= normalizedSegmentLength
segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag)
pointBeginRotated = segmentYMirror * pointBegin
pointEndRotated = segmentYMirror * pointEnd
addLoopsXSegmentIntersections( lineLoopsIntersections, loops, pointBeginRotated.real, pointEndRotated.real, segmentYMirror, pointBeginRotated.imag )
for lineLoopsIntersection in lineLoopsIntersections:
point = complex( lineLoopsIntersection, pointBeginRotated.imag ) * normalizedSegment
loopLoopsIntersections.append(point)
def addLineXSegmentIntersection( lineLoopsIntersections, segmentFirstX, segmentSecondX, vector3First, vector3Second, y ):
'Add intersections of the line with the x segment.'
xIntersection = euclidean.getXIntersectionIfExists( vector3First, vector3Second, y )
if xIntersection == None:
return
if xIntersection < min( segmentFirstX, segmentSecondX ):
return
if xIntersection <= max( segmentFirstX, segmentSecondX ):
lineLoopsIntersections.append( xIntersection )
def addLoopLoopsIntersections( loop, loopsLoopsIntersections, otherLoops ):
'Add intersections of the loop with the other loops.'
for pointIndex in xrange(len(loop)):
pointBegin = loop[pointIndex]
pointEnd = loop[(pointIndex + 1) % len(loop)]
addLineLoopsIntersections( loopsLoopsIntersections, otherLoops, pointBegin, pointEnd )
def addLoopsXSegmentIntersections( lineLoopsIntersections, loops, segmentFirstX, segmentSecondX, segmentYMirror, y ):
'Add intersections of the loops with the x segment.'
for loop in loops:
addLoopXSegmentIntersections( lineLoopsIntersections, loop, segmentFirstX, segmentSecondX, segmentYMirror, y )
def addLoopXSegmentIntersections( lineLoopsIntersections, loop, segmentFirstX, segmentSecondX, segmentYMirror, y ):
'Add intersections of the loop with the x segment.'
rotatedLoop = euclidean.getRotatedComplexes( segmentYMirror, loop )
for pointIndex in xrange( len( rotatedLoop ) ):
pointFirst = rotatedLoop[pointIndex]
pointSecond = rotatedLoop[ (pointIndex + 1) % len( rotatedLoop ) ]
addLineXSegmentIntersection( lineLoopsIntersections, segmentFirstX, segmentSecondX, pointFirst, pointSecond, y )
def getInBetweenLoopsFromLoops(loops, radius):
'Get the in between loops from loops.'
inBetweenLoops = []
for loop in loops:
inBetweenLoop = []
for pointIndex in xrange(len(loop)):
pointBegin = loop[pointIndex]
pointEnd = loop[(pointIndex + 1) % len(loop)]
intercircle.addPointsFromSegment(pointBegin, pointEnd, inBetweenLoop, radius)
inBetweenLoops.append(inBetweenLoop)
return inBetweenLoops
def getInsetPointsByInsetLoop( insetLoop, inside, loops, radius ):
'Get the inset points of the inset loop inside the loops.'
insetPointsByInsetLoop = []
for pointIndex in xrange( len( insetLoop ) ):
pointBegin = insetLoop[ ( pointIndex + len( insetLoop ) - 1 ) % len( insetLoop ) ]
pointCenter = insetLoop[pointIndex]
pointEnd = insetLoop[ (pointIndex + 1) % len( insetLoop ) ]
if getIsInsetPointInsideLoops( inside, loops, pointBegin, pointCenter, pointEnd, radius ):
insetPointsByInsetLoop.append( pointCenter )
return insetPointsByInsetLoop
def getInsetPointsByInsetLoops( insetLoops, inside, loops, radius ):
'Get the inset points of the inset loops inside the loops.'
insetPointsByInsetLoops = []
for insetLoop in insetLoops:
insetPointsByInsetLoops += getInsetPointsByInsetLoop( insetLoop, inside, loops, radius )
return insetPointsByInsetLoops
def getIsInsetPointInsideLoops( inside, loops, pointBegin, pointCenter, pointEnd, radius ):
'Determine if the inset point is inside the loops.'
centerMinusBegin = euclidean.getNormalized( pointCenter - pointBegin )
centerMinusBeginWiddershins = complex( - centerMinusBegin.imag, centerMinusBegin.real )
endMinusCenter = euclidean.getNormalized( pointEnd - pointCenter )
endMinusCenterWiddershins = complex( - endMinusCenter.imag, endMinusCenter.real )
widdershinsNormalized = euclidean.getNormalized( centerMinusBeginWiddershins + endMinusCenterWiddershins ) * radius
return euclidean.getIsInFilledRegion( loops, pointCenter + widdershinsNormalized ) == inside
def getLoopsDifference(importRadius, loopLists):
'Get difference loops.'
halfImportRadius = 0.5 * importRadius # so that there are no misses on shallow angles
radiusSide = 0.01 * importRadius
negativeLoops = getLoopsUnion(importRadius, loopLists[1 :])
intercircle.directLoops(False, negativeLoops)
positiveLoops = loopLists[0]
intercircle.directLoops(True, positiveLoops)
corners = getInsetPointsByInsetLoops(negativeLoops, True, positiveLoops, radiusSide)
corners += getInsetPointsByInsetLoops(positiveLoops, False, negativeLoops, radiusSide)
allPoints = corners[:]
allPoints += getInsetPointsByInsetLoops(getInBetweenLoopsFromLoops(negativeLoops, halfImportRadius), True, positiveLoops, radiusSide)
allPoints += getInsetPointsByInsetLoops(getInBetweenLoopsFromLoops(positiveLoops, halfImportRadius), False, negativeLoops, radiusSide)
return triangle_mesh.getDescendingAreaOrientedLoops(allPoints, corners, importRadius)
def getLoopsIntersection(importRadius, loopLists):
'Get intersection loops.'
intercircle.directLoopLists(True, loopLists)
if len(loopLists) < 1:
return []
if len(loopLists) < 2:
return loopLists[0]
intercircle.directLoopLists(True, loopLists)
loopsIntersection = loopLists[0]
for loopList in loopLists[1 :]:
loopsIntersection = getLoopsIntersectionByPair(importRadius, loopsIntersection, loopList)
return loopsIntersection
def getLoopsIntersectionByPair(importRadius, loopsFirst, loopsLast):
'Get intersection loops for a pair of loop lists.'
halfImportRadius = 0.5 * importRadius # so that there are no misses on shallow angles
radiusSide = 0.01 * importRadius
corners = []
corners += getInsetPointsByInsetLoops(loopsFirst, True, loopsLast, radiusSide)
corners += getInsetPointsByInsetLoops(loopsLast, True, loopsFirst, radiusSide)
allPoints = corners[:]
allPoints += getInsetPointsByInsetLoops(getInBetweenLoopsFromLoops(loopsFirst, halfImportRadius), True, loopsLast, radiusSide)
allPoints += getInsetPointsByInsetLoops(getInBetweenLoopsFromLoops(loopsLast, halfImportRadius), True, loopsFirst, radiusSide)
return triangle_mesh.getDescendingAreaOrientedLoops(allPoints, corners, importRadius)
def getLoopsListsIntersections( loopsList ):
'Get intersections betweens the loops lists.'
loopsListsIntersections = []
for loopsIndex in xrange( len( loopsList ) ):
loops = loopsList[ loopsIndex ]
for otherLoops in loopsList[ : loopsIndex ]:
loopsListsIntersections += getLoopsLoopsIntersections( loops, otherLoops )
return loopsListsIntersections
def getLoopsLoopsIntersections( loops, otherLoops ):
'Get all the intersections of the loops with the other loops.'
loopsLoopsIntersections = []
for loop in loops:
addLoopLoopsIntersections( loop, loopsLoopsIntersections, otherLoops )
return loopsLoopsIntersections
def getLoopsUnion(importRadius, loopLists):
'Get joined loops sliced through shape.'
allPoints = []
corners = getLoopsListsIntersections(loopLists)
radiusSideNegative = -0.01 * importRadius
intercircle.directLoopLists(True, loopLists)
for loopListIndex in xrange(len(loopLists)):
insetLoops = loopLists[ loopListIndex ]
inBetweenInsetLoops = getInBetweenLoopsFromLoops(insetLoops, importRadius)
otherLoops = euclidean.getConcatenatedList(loopLists[: loopListIndex] + loopLists[loopListIndex + 1 :])
corners += getInsetPointsByInsetLoops(insetLoops, False, otherLoops, radiusSideNegative)
allPoints += getInsetPointsByInsetLoops(inBetweenInsetLoops, False, otherLoops, radiusSideNegative)
allPoints += corners[:]
return triangle_mesh.getDescendingAreaOrientedLoops(allPoints, corners, importRadius)
def getVisibleObjectLoopsList( importRadius, visibleObjects, z ):
'Get visible object loops list.'
visibleObjectLoopsList = []
for visibleObject in visibleObjects:
visibleObjectLoops = visibleObject.getLoops(importRadius, z)
visibleObjectLoopsList.append( visibleObjectLoops )
return visibleObjectLoopsList
class BooleanSolid( group.Group ):
'A boolean solid object.'
def getDifference(self, importRadius, visibleObjectLoopsList):
'Get subtracted loops sliced through shape.'
return getLoopsDifference(importRadius, visibleObjectLoopsList)
def getIntersection(self, importRadius, visibleObjectLoopsList):
'Get intersected loops sliced through shape.'
return getLoopsIntersection(importRadius, visibleObjectLoopsList)
def getLoops(self, importRadius, z):
'Get loops sliced through shape.'
visibleObjects = evaluate.getVisibleObjects(self.archivableObjects)
if len( visibleObjects ) < 1:
return []
visibleObjectLoopsList = getVisibleObjectLoopsList( importRadius, visibleObjects, z )
loops = self.getLoopsFromObjectLoopsList(importRadius, visibleObjectLoopsList)
return euclidean.getSimplifiedLoops( loops, importRadius )
def getLoopsFromObjectLoopsList(self, importRadius, visibleObjectLoopsList):
'Get loops from visible object loops list.'
return self.operationFunction(importRadius, visibleObjectLoopsList)
def getTransformedPaths(self):
'Get all transformed paths.'
importRadius = setting.getImportRadius(self.elementNode)
loopsFromObjectLoopsList = self.getLoopsFromObjectLoopsList(importRadius, self.getComplexTransformedPathLists())
return euclidean.getVector3Paths(loopsFromObjectLoopsList)
def getUnion(self, importRadius, visibleObjectLoopsList):
'Get joined loops sliced through shape.'
return getLoopsUnion(importRadius, visibleObjectLoopsList)
def getXMLLocalName(self):
'Get xml class name.'
return self.operationFunction.__name__.lower()[ len('get') : ]

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,9 @@
#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
import os
import sys
numberOfLevelsDeepInPackageHierarchy = 4
packageFilePath = os.path.abspath(__file__)
for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ):
packageFilePath = os.path.dirname( packageFilePath )
if packageFilePath not in sys.path:
sys.path.insert( 0, packageFilePath )

View File

@ -0,0 +1,60 @@
"""
Boolean geometry utilities.
"""
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 fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities import archive
from fabmetheus_utilities import gcodec
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def _getAccessibleAttribute(attributeName, elementNode):
'Get the accessible attribute.'
functionName = attributeName[len('get') :].lower()
if functionName not in evaluate.globalCreationDictionary:
print('Warning, functionName not in globalCreationDictionary in _getAccessibleAttribute in creation for:')
print(functionName)
print(elementNode)
return None
pluginModule = archive.getModuleWithPath(evaluate.globalCreationDictionary[functionName])
if pluginModule == None:
print('Warning, _getAccessibleAttribute in creation can not get a pluginModule for:')
print(functionName)
print(elementNode)
return None
return Creation(elementNode, pluginModule).getCreation
class Creation:
'Class to handle a creation.'
def __init__(self, elementNode, pluginModule):
'Initialize.'
self.elementNode = elementNode
self.pluginModule = pluginModule
def __repr__(self):
"Get the string representation of this creation."
return self.elementNode
def getCreation(self, *arguments):
"Get creation."
dictionary = {'_fromCreationEvaluator': 'true'}
firstArgument = None
if len(arguments) > 0:
firstArgument = arguments[0]
if firstArgument.__class__ == dict:
dictionary.update(firstArgument)
return self.pluginModule.getGeometryOutput(None, self.elementNode.getCopyShallow(dictionary))
copyShallow = self.elementNode.getCopyShallow(dictionary)
return self.pluginModule.getGeometryOutputByArguments(arguments, copyShallow)

View File

@ -0,0 +1,97 @@
"""
Boolean geometry utilities.
"""
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__
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def _getAccessibleAttribute(attributeName, elementNode):
'Get the accessible attribute.'
if attributeName in globalGetAccessibleAttributeSet:
return getattr(Document(elementNode), attributeName, None)
return None
class Document:
'Class to handle elementNodes in a document.'
def __init__(self, elementNode):
'Initialize.'
self.elementNode = elementNode
def __repr__(self):
'Get the string representation of this Document.'
return self.elementNode
def getCascadeBoolean(self, defaultBoolean, key):
'Get cascade boolean.'
return self.elementNode.getCascadeBoolean(defaultBoolean, key)
def getCascadeFloat(self, defaultFloat, key):
'Get cascade float.'
return self.elementNode.getCascadeFloat(defaultFloat, key)
def getDocumentElement(self):
'Get document element element.'
return self.elementNode.getDocumentElement()
def getElementByID(self, idKey):
'Get element by id.'
elementByID = self.elementNode.getElementNodeByID(idKey)
if elementByID == None:
print('Warning, could not get elementByID in getElementByID in document for:')
print(idKey)
print(self.elementNode)
return elementByID
def getElementsByName(self, nameKey):
'Get element by name.'
elementsByName = self.elementNode.getElementNodesByName(nameKey)
if elementsByName == None:
print('Warning, could not get elementsByName in getElementsByName in document for:')
print(nameKey)
print(self.elementNode)
return elementsByName
def getElementsByTag(self, tagKey):
'Get element by tag.'
elementsByTag = self.elementNode.getElementNodesByTag(tagKey)
if elementsByTag == None:
print('Warning, could not get elementsByTag in getElementsByTag in document for:')
print(tagKey)
print(self.elementNode)
return elementsByTag
def getParentNode(self):
'Get parentNode element.'
return self.elementNode.parentNode
def getPrevious(self):
'Get previous element.'
return self.getPreviousElement()
def getPreviousElement(self):
'Get previous element.'
return self.elementNode.getPreviousElementNode()
def getPreviousVertex(self):
'Get previous element.'
return self.elementNode.getPreviousVertex()
def getSelfElement(self):
'Get self element.'
return self.elementNode
globalAccessibleAttributeDictionary = 'getCascadeBoolean getCascadeFloat getDocumentElement getElementByID getElementsByName'.split()
globalAccessibleAttributeDictionary += 'getElementsByTag getParentNode getPrevious getPreviousElement getPreviousVertex'.split()
globalAccessibleAttributeDictionary += 'getSelfElement'.split()
globalGetAccessibleAttributeSet = set(globalAccessibleAttributeDictionary)

View File

@ -0,0 +1,180 @@
"""
Boolean geometry utilities.
"""
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_application.skeinforge_utilities import skeinforge_craft
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def _getAccessibleAttribute(attributeName, elementNode):
'Get the accessible attribute.'
if attributeName in globalGetAccessibleAttributeSet:
return getattr(Setting(elementNode), attributeName, None)
return None
def getCascadeFloatWithoutSelf(defaultFloat, elementNode, key):
'Get the cascade float.'
if key in elementNode.attributes:
value = elementNode.attributes[key]
functionName = 'get' + key[0].upper() + key[1 :]
if functionName in value:
if elementNode.parentNode == None:
return defaultFloat
else:
elementNode = elementNode.parentNode
return elementNode.getCascadeFloat(defaultFloat, key)
def getEdgeWidth(elementNode):
'Get the edge width.'
if elementNode == None:
return 0.72
preferences = skeinforge_craft.getCraftPreferences('carve')
layerHeight = skeinforge_craft.getCraftValue('Layer Height', preferences)
layerHeight = getCascadeFloatWithoutSelf(layerHeight, elementNode, 'layerHeight')
edgeWidthOverHeight = skeinforge_craft.getCraftValue('Edge Width over Height', preferences)
edgeWidthOverHeight = getCascadeFloatWithoutSelf(edgeWidthOverHeight, elementNode, 'edgeWidthOverHeight')
return getCascadeFloatWithoutSelf(edgeWidthOverHeight * layerHeight, elementNode, 'edgeWidth')
def getImportCoarseness(elementNode, preferences=None):
'Get the importCoarseness.'
if elementNode == None:
return 1.0
if preferences == None:
preferences = skeinforge_craft.getCraftPreferences('carve')
importCoarseness = skeinforge_craft.getCraftValue('Import Coarseness', preferences)
return getCascadeFloatWithoutSelf(importCoarseness, elementNode, 'importCoarseness')
def getImportRadius(elementNode):
'Get the importRadius.'
if elementNode == None:
return 0.36
preferences = skeinforge_craft.getCraftPreferences('carve')
importCoarseness = getImportCoarseness(elementNode, preferences)
layerHeight = skeinforge_craft.getCraftValue('Layer Height', preferences)
layerHeight = getCascadeFloatWithoutSelf(layerHeight, elementNode, 'layerHeight')
edgeWidthOverHeight = skeinforge_craft.getCraftValue('Edge Width over Height', preferences)
edgeWidthOverHeight = getCascadeFloatWithoutSelf(edgeWidthOverHeight, elementNode, 'edgeWidthOverHeight')
return getCascadeFloatWithoutSelf(0.5 * importCoarseness * layerHeight * edgeWidthOverHeight, elementNode, 'importRadius')
def getInteriorOverhangAngle(elementNode):
'Get the interior overhang support angle in degrees.'
return getCascadeFloatWithoutSelf(30.0, elementNode, 'interiorOverhangAngle')
def getInteriorOverhangRadians(elementNode):
'Get the interior overhang support angle in radians.'
return math.radians(getInteriorOverhangAngle(elementNode))
def getLayerHeight(elementNode):
'Get the layer height.'
if elementNode == None:
return 0.4
preferences = skeinforge_craft.getCraftPreferences('carve')
return getCascadeFloatWithoutSelf(skeinforge_craft.getCraftValue('Layer Height', preferences), elementNode, 'layerHeight')
def getOverhangAngle(elementNode):
'Get the overhang support angle in degrees.'
return getCascadeFloatWithoutSelf(45.0, elementNode, 'overhangAngle')
def getOverhangRadians(elementNode):
'Get the overhang support angle in radians.'
return math.radians(getOverhangAngle(elementNode))
def getOverhangSpan(elementNode):
'Get the overhang span.'
return getCascadeFloatWithoutSelf(2.0 * getLayerHeight(elementNode), elementNode, 'overhangSpan')
def getPrecision(elementNode):
'Get the cascade precision.'
return getCascadeFloatWithoutSelf(0.2 * getLayerHeight(elementNode), elementNode, 'precision')
def getSheetThickness(elementNode):
'Get the sheet thickness.'
return getCascadeFloatWithoutSelf(3.0, elementNode, 'sheetThickness')
def getTwistPrecision(elementNode):
'Get the twist precision in degrees.'
return getCascadeFloatWithoutSelf(5.0, elementNode, 'twistPrecision')
def getTwistPrecisionRadians(elementNode):
'Get the twist precision in radians.'
return math.radians(getTwistPrecision(elementNode))
class Setting:
'Class to get handle elementNodes in a setting.'
def __init__(self, elementNode):
'Initialize.'
self.elementNode = elementNode
def __repr__(self):
'Get the string representation of this Setting.'
return self.elementNode
def getEdgeWidth(self):
'Get the edge width.'
return getEdgeWidth(self.elementNode)
def getImportCoarseness(self):
'Get the importCoarseness.'
return getImportCoarseness(self.elementNode)
def getImportRadius(self):
'Get the importRadius.'
return getImportRadius(self.elementNode)
def getInteriorOverhangAngle(self):
'Get the interior overhang support angle in degrees.'
return getInteriorOverhangAngle(self.elementNode)
def getInteriorOverhangRadians(self):
'Get the interior overhang support angle in radians.'
return getInteriorOverhangRadians(self.elementNode)
def getLayerHeight(self):
'Get the layer height.'
return getLayerHeight(self.elementNode)
def getOverhangAngle(self):
'Get the overhang support angle in degrees.'
return getOverhangAngle(self.elementNode)
def getOverhangRadians(self):
'Get the overhang support angle in radians.'
return getOverhangRadians(self.elementNode)
def getOverhangSpan(self):
'Get the overhang span.'
return getOverhangSpan(self.elementNode)
def getPrecision(self):
'Get the cascade precision.'
return getPrecision(self.elementNode)
def getSheetThickness(self):
'Get the sheet thickness.'
return getSheetThickness(self.elementNode)
def getTwistPrecision(self):
'Get the twist precision in degrees.'
return getTwistPrecision(self.elementNode)
def getTwistPrecisionRadians(self):
'Get the twist precision in radians.'
return getTwistPrecisionRadians(self.elementNode)
globalAccessibleAttributeDictionary = 'getEdgeWidth getImportCoarseness getImportRadius getInteriorOverhangAngle getInteriorOverhangRadians'.split()
globalAccessibleAttributeDictionary += 'getLayerHeight getOverhangSpan getOverhangAngle getOverhangRadians'.split()
globalAccessibleAttributeDictionary += 'getPrecision getSheetThickness getTwistPrecision getTwistPrecisionRadians'.split()
globalGetAccessibleAttributeSet = set(globalAccessibleAttributeDictionary)

View File

@ -0,0 +1,9 @@
#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
import os
import sys
numberOfLevelsDeepInPackageHierarchy = 4
packageFilePath = os.path.abspath(__file__)
for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ):
packageFilePath = os.path.dirname( packageFilePath )
if packageFilePath not in sys.path:
sys.path.insert( 0, packageFilePath )

View File

@ -0,0 +1,102 @@
"""
Dictionary object attributes.
"""
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 fabmetheus_utilities import euclidean
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def _getAccessibleAttribute(attributeName, dictionaryObject):
'Get the accessible attribute.'
if attributeName in globalNativeFunctionSet:
return getattr(dictionaryObject, attributeName, None)
if attributeName in globalGetAccessibleAttributeSet:
stringAttribute = DictionaryAttribute(dictionaryObject)
return getattr(stringAttribute, attributeName, None)
return None
class DictionaryAttribute:
'Class to handle a dictionary.'
def __init__(self, dictionaryObject):
'Initialize.'
self.dictionaryObject = dictionaryObject
def __repr__(self):
"Get the dictionary representation of this DictionaryAttribute."
return str(self.dictionaryObject)
def count(self, value):
'Get the count.'
countTotal = 0
for key, iteratorValue in self.dictionaryObject.iteritems():
if iteratorValue == value:
countTotal += 1
return countTotal
def delete(self, arguments):
'Get the delete dictionary.'
if arguments.__class__ != list:
del self.dictionaryObject[arguments]
return self.dictionaryObject
if len(arguments) == 0:
self.dictionaryObject.clear()
return self.dictionaryObject
if len(arguments) == 1:
del self.dictionaryObject[arguments[0]]
return self.dictionaryObject
for enumeratorKey in euclidean.getEnumeratorKeysAlwaysList(self.dictionaryObject, arguments):
del self.dictionaryObject[enumeratorKey]
return self.dictionaryObject
def getIsIn(self, value):
'Determine if the value is in.'
return value in self.dictionaryObject
def getIsNotIn(self, value):
'Determine if the value is in.'
return not(value in self.dictionaryObject)
def getLength(self):
'Get the length.'
return len(self.dictionaryObject)
def getMax(self):
'Get the max.'
return max(self.dictionaryObject)
def getMin(self):
'Get the min.'
return min(self.dictionaryObject)
def index(self, value):
'Get the index element.'
for key, iteratorValue in self.dictionaryObject.iteritems():
if iteratorValue == value:
return key
raise ValueError('Value (%s) not found in index in DictionaryAttribute for (%s).' % (value, self.dictionaryObject))
def length(self):
'Get the length.'
return len(self.dictionaryObject)
def set(self, itemIndex, value):
'Set value.'
self.dictionaryObject[itemIndex] = value
return self.dictionaryObject
globalAccessibleAttributeDictionary = 'count delete getIsIn getIsNotIn getLength getMax getMin index length set'.split()
globalGetAccessibleAttributeSet = set(globalAccessibleAttributeDictionary)
globalNativeFunctions = 'clear copy fromkeys get items keys pop popitem remove setdefault update values'.split()
globalNativeFunctionSet = set(globalNativeFunctions)

View File

@ -0,0 +1,123 @@
"""
List object attributes.
"""
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 fabmetheus_utilities import euclidean
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def _getAccessibleAttribute(attributeName, listObject):
'Get the accessible attribute.'
if attributeName in globalNativeFunctionSet:
return getattr(listObject, attributeName, None)
if attributeName in globalGetAccessibleAttributeSet:
stringAttribute = ListAttribute(listObject)
return getattr(stringAttribute, attributeName, None)
return None
class ListAttribute:
'Class to handle a list.'
def __init__(self, listObject):
'Initialize.'
self.listObject = listObject
def __repr__(self):
"Get the list representation of this ListAttribute."
return str(self.listObject)
def add(self, value):
'Get the concatenation, same as append.'
return self.listObject + [value]
def copy(self):
'Get the copy.'
return self.listObject[:]
def delete(self, arguments):
'Get the delete list.'
deleteList = []
enumeratorSet = set(euclidean.getEnumeratorKeysAlwaysList(self.listObject, arguments))
for elementIndex, element in enumerate(self.listObject):
if elementIndex not in enumeratorSet:
deleteList.append(element)
return deleteList
def get(self, itemIndex):
'Get value by index'
return self.listObject[itemIndex]
def getExpansion(self, items):
'Get the concatenated copies.'
expansion = []
for itemIndex in xrange(items):
expansion += self.listObject[:]
return expansion
def getIsIn(self, value):
'Determine if the value is in.'
return value in self.listObject
def getIsNotIn(self, value):
'Determine if the value is in.'
return not(value in self.listObject)
def getLength(self):
'Get the length.'
return len(self.listObject)
def getMax(self):
'Get the max.'
return max(self.listObject)
def getMin(self):
'Get the min.'
return min(self.listObject)
def insert(self, insertIndex, value):
'Get the insert list.'
if insertIndex < 0:
insertIndex += len(self.listObject)
insertIndex = max(0, insertIndex)
return self.listObject[: insertIndex] + [value] + self.listObject[insertIndex :]
def keys(self):
'Get the keys.'
return range(len(self.listObject))
def length(self):
'Get the length.'
return len(self.listObject)
def rindex(self, value):
'Get the rindex element.'
for elementIndex, element in enumerate(self.listObject):
if element == value:
return elementIndex
raise ValueError('Value (%s) not found in rindex in ListAttribute for (%s).' % (value, self.listObject))
def set(self, itemIndex, value):
'Set value.'
self.listObject[itemIndex] = value
return self.listObject
def values(self, arguments=None):
'Get the values.'
return self.listObject
globalAccessibleAttributeDictionary = 'add copy count delete get getExpansion getIsIn getIsNotIn getLength getMax getMin'.split()
globalAccessibleAttributeDictionary += 'insert keys length rindex set values'.split()
globalGetAccessibleAttributeSet = set(globalAccessibleAttributeDictionary)
globalNativeFunctions = 'append extend index pop remove reverse sort'.split()
globalNativeFunctionSet = set(globalNativeFunctions)

View File

@ -0,0 +1,136 @@
"""
String object attributes.
"""
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 fabmetheus_utilities import euclidean
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def _getAccessibleAttribute(attributeName, stringObject):
'Get the accessible attribute.'
if attributeName in globalNativeFunctionSet:
return getattr(stringObject, attributeName, None)
if attributeName in globalGetAccessibleAttributeSet:
stringAttribute = StringAttribute(stringObject)
return getattr(stringAttribute, attributeName, None)
return None
class StringAttribute:
'Class to handle a string.'
def __init__(self, stringObject):
'Initialize.'
self.stringObject = stringObject
def __repr__(self):
"Get the string representation of this StringAttribute."
return self.stringObject
def add(self, nextString):
'Get the add string, same as append.'
return self.stringObject + nextString
def append(self, nextString):
'Get the append string.'
return self.stringObject + nextString
def copy(self):
'Get the copy.'
return self.stringObject[:]
def delete(self, arguments):
'Get the delete string.'
deleteString = ''
enumeratorSet = set(euclidean.getEnumeratorKeysAlwaysList(self.stringObject, arguments))
for characterIndex, character in enumerate(self.stringObject):
if characterIndex not in enumeratorSet:
deleteString += character
return deleteString
def get(self, itemIndex):
'Get value by characterIndex'
return self.stringObject[itemIndex]
def getExpansion(self, items):
'Get the concatenated copies.'
expansion = ''
for itemIndex in xrange(items):
expansion += self.stringObject
return expansion
def getIsIn(self, value):
'Determine if the value is in.'
return value in self.stringObject
def getIsNotIn(self, value):
'Determine if the value is in.'
return not(value in self.stringObject)
def getLength(self):
'Get the length.'
return len(self.stringObject)
def getMax(self):
'Get the max.'
return max(self.stringObject)
def getMin(self):
'Get the min.'
return min(self.stringObject)
def insert(self, insertIndex, value):
'Get the insert string.'
if insertIndex < 0:
insertIndex += len(self.stringObject)
insertIndex = max(0, insertIndex)
return self.stringObject[: insertIndex] + value + self.stringObject[insertIndex :]
def keys(self):
'Get the keys.'
return range(len(self.stringObject))
def length(self):
'Get the length.'
return len(self.stringObject)
def remove(self, value):
'Get the remove string.'
removeIndex = self.stringObject.find(value)
if removeIndex > -1:
return self.stringObject[: removeIndex] + self.stringObject[removeIndex + len(value) :]
return self.stringObject
def reverse(self):
'Get the reverse string.'
return self.stringObject[: : -1]
def set(self, itemIndex, value):
'Set value.'
self.stringObject[itemIndex] = value
return self.stringObject
def values(self):
'Get the values.'
values = []
for character in self.stringObject:
values.append(character)
return values
globalAccessibleAttributeDictionary = 'add append copy delete get getExpansion getIsIn getIsNotIn getLength getMax getMin'.split()
globalAccessibleAttributeDictionary += 'insert keys length remove reverse set values'.split()
globalGetAccessibleAttributeSet = set(globalAccessibleAttributeDictionary)
globalNativeFunctions = 'capitalize center count decode encode endswith expandtabs find format index isalnum join'.split()
globalNativeFunctions += 'isalpha isdigit islower isspace istitle isupper ljust lower lstrip partition replace rfind rindex'.split()
globalNativeFunctions += 'rjust rpartition rsplit rstrip split splitlines startswith strip swapcase title translate upper zfill'.split()
globalNativeFunctionSet = set(globalNativeFunctions)

View File

@ -0,0 +1,9 @@
#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
import os
import sys
numberOfLevelsDeepInPackageHierarchy = 4
packageFilePath = os.path.abspath(__file__)
for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ):
packageFilePath = os.path.dirname( packageFilePath )
if packageFilePath not in sys.path:
sys.path.insert( 0, packageFilePath )

View File

@ -0,0 +1,101 @@
"""
Boolean geometry utilities.
"""
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 fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
globalNativeFunctions = 'acos asin atan atan2 ceil cos cosh degrees e exp fabs floor fmod frexp hypot'.split()
globalNativeFunctions += 'ldexp log log10 modf pi pow radians sin sinh sqrt tan tanh trunc'.split()
globalNativeFunctionSet = set(globalNativeFunctions)
#Constants from: http://www.physlink.com/reference/MathConstants.cfm
#Tau is from: http://tauday.com/
#If anyone wants to add stuff, more constants are at: http://en.wikipedia.org/wiki/Mathematical_constant
globalMathConstantDictionary = {
'euler' : 0.5772156649015328606065120,
'golden' : euclidean.globalGoldenRatio,
'goldenAngle' : euclidean.globalGoldenAngle,
'goldenRatio' : euclidean.globalGoldenRatio,
'tau' : euclidean.globalTau}
def _getAccessibleAttribute(attributeName):
'Get the accessible attribute.'
if attributeName in globalMathConstantDictionary:
return globalMathConstantDictionary[attributeName]
if attributeName in globalNativeFunctionSet:
return math.__dict__[attributeName]
if attributeName in globalAccessibleAttributeDictionary:
return globalAccessibleAttributeDictionary[attributeName]
return None
def getAbs(value):
'Get the abs.'
return abs(value)
def getBoolean(value):
'Get the boolean.'
return bool(value)
def getDivmod(x, y):
'Get the divmod.'
return divmod(x, y)
def getFloat(value):
'Get the float.'
return float(value)
def getHex(value):
'Get the hex.'
return hex(value)
def getInt(value):
'Get the int.'
return int(value)
def getLong(value):
'Get the long.'
return long(value)
def getMax(first, second):
'Get the max.'
return max(first, second)
def getMin(first, second):
'Get the min.'
return min(first, second)
def getRound(value):
'Get the round.'
return round(value)
def getString(value):
'Get the string.'
return str(value)
globalAccessibleAttributeDictionary = {
'abs' : getAbs,
'boolean' : getBoolean,
'divmod' : getDivmod,
'float' : getFloat,
'hex' : getHex,
'int' : getInt,
'long' : getLong,
'max' : getMax,
'min' : getMin,
'round' : getRound,
'string' : getString}

View File

@ -0,0 +1,95 @@
"""
Boolean geometry utilities.
"""
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 fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities.vector3index import Vector3Index
from fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def _getAccessibleAttribute(attributeName):
'Get the accessible attribute.'
if attributeName in globalAccessibleAttributeDictionary:
return globalAccessibleAttributeDictionary[attributeName]
return None
def getComplex(x=0.0, y=0.0):
'Get the complex.'
return complex(x, y)
def getCylindrical(azimuthDegrees, radius=1.0, z=0.0):
'Get the cylindrical vector3 by degrees.'
return getCylindricalByRadians(math.radians(azimuthDegrees), radius, z)
def getCylindricalByRadians(azimuthRadians, radius=1.0, z=0.0):
'Get the cylindrical vector3 by radians.'
polar = radius * euclidean.getWiddershinsUnitPolar(azimuthRadians)
return Vector3(polar.real, polar.imag, z)
def getNestedVectorTestExample(x=0.0, y=0.0, z=0.0):
'Get the NestedVectorTestExample.'
return NestedVectorTestExample(Vector3(x, y, z))
def getPolar(angleDegrees, radius=1.0):
'Get the complex polar by degrees.'
return radius * euclidean.getWiddershinsUnitPolar(math.radians(angleDegrees))
def getPolarByRadians(angleRadians, radius=1.0):
'Get the complex polar by radians.'
return radius * euclidean.getWiddershinsUnitPolar(angleRadians)
def getSpherical(azimuthDegrees, elevationDegrees, radius=1.0):
'Get the spherical vector3 unit by degrees.'
return getSphericalByRadians(math.radians(azimuthDegrees), math.radians(elevationDegrees), radius)
def getSphericalByRadians(azimuthRadians, elevationRadians, radius=1.0):
'Get the spherical vector3 unit by radians.'
elevationComplex = euclidean.getWiddershinsUnitPolar(elevationRadians)
azimuthComplex = euclidean.getWiddershinsUnitPolar(azimuthRadians) * elevationComplex.real
return Vector3(azimuthComplex.real, azimuthComplex.imag, elevationComplex.imag) * radius
def getVector3(x=0.0, y=0.0, z=0.0):
'Get the vector3.'
return Vector3(x, y, z)
def getVector3Index(index=0, x=0.0, y=0.0, z=0.0):
'Get the vector3.'
return Vector3Index(index, x, y, z)
class NestedVectorTestExample:
'Class to test local attribute.'
def __init__(self, vector3):
'Get the accessible attribute.'
self.vector3 = vector3
def _getAccessibleAttribute(self, attributeName):
"Get the accessible attribute."
if attributeName == 'vector3':
return getattr(self, attributeName, None)
return None
globalAccessibleAttributeDictionary = {
'complex' : getComplex,
'getCylindrical' : getCylindrical,
'getCylindricalByRadians' : getCylindricalByRadians,
'getPolar' : getPolar,
'getPolarByRadians' : getPolarByRadians,
'getSpherical' : getSpherical,
'getSphericalByRadians' : getSphericalByRadians,
'NestedVectorTestExample' : getNestedVectorTestExample,
'Vector3' : getVector3,
'Vector3Index' : getVector3Index}

View File

@ -0,0 +1,63 @@
"""
Boolean geometry utilities.
"""
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 fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def _getAccessibleAttribute(attributeName):
'Get the accessible attribute.'
if attributeName in globalAccessibleAttributeDictionary:
return globalAccessibleAttributeDictionary[attributeName]
return None
def getBoundingBoxByPaths(elementNode):
'Get bounding box of the transformed paths of the xmlObject of the elementNode.'
transformedPaths = elementNode.xmlObject.getTransformedPaths()
maximum = euclidean.getMaximumByVector3Paths(transformedPaths)
minimum = euclidean.getMinimumByVector3Paths(transformedPaths)
return [minimum, maximum]
def getCenterByPaths(elementNode):
'Get center of the transformed paths of the xmlObject of the elementNode.'
transformedPaths = elementNode.xmlObject.getTransformedPaths()
return 0.5 * (euclidean.getMaximumByVector3Paths(transformedPaths) + euclidean.getMinimumByVector3Paths(transformedPaths))
def getExtentByPaths(elementNode):
'Get extent of the transformed paths of the xmlObject of the elementNode.'
transformedPaths = elementNode.xmlObject.getTransformedPaths()
return euclidean.getMaximumByVector3Paths(transformedPaths) - euclidean.getMinimumByVector3Paths(transformedPaths)
def getInradiusByPaths(elementNode):
'Get inradius of the transformed paths of the xmlObject of the elementNode.'
return 0.5 * getExtentByPaths(elementNode)
def getMinimumByPaths(elementNode):
'Get minimum of the transformed paths of the xmlObject of the elementNode.'
return euclidean.getMinimumByVector3Paths(elementNode.xmlObject.getTransformedPaths())
def getMaximumByPaths(elementNode):
'Get maximum of the transformed paths of the xmlObject of the elementNode.'
return euclidean.getMaximumByVector3Paths(elementNode.xmlObject.getTransformedPaths())
globalAccessibleAttributeDictionary = {
'getBoundingBoxByPaths' : getBoundingBoxByPaths,
'getCenterByPaths' : getCenterByPaths,
'getExtentByPaths' : getExtentByPaths,
'getInradiusByPaths' : getInradiusByPaths,
'getMaximumByPaths' : getMaximumByPaths,
'getMinimumByPaths' : getMinimumByPaths}

View File

@ -0,0 +1,36 @@
"""
Boolean geometry utilities.
"""
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__
import sys
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def _getAccessibleAttribute(attributeName):
'Get the accessible attribute.'
if attributeName in globalAccessibleAttributeDictionary:
return globalAccessibleAttributeDictionary[attributeName]
return None
def continuous(valueString):
'Print continuous.'
sys.stdout.write(str(valueString))
return valueString
def line(valueString):
'Print line.'
print(valueString)
return valueString
globalAccessibleAttributeDictionary = {'continuous' : continuous, 'line' : line}

View File

@ -0,0 +1,22 @@
"A"
0 0
2 8
4 0
1 4
3 4
"B"
0 4
2 4
4 5
4 7
3 8
0 8
0 0
3 0
4 1
4 3
2 4
"C"
1 A
2 0 0
3 2 8
4 4 0
5
6 1 4
7 3 4
8
9 B
10 0 4
11 2 4
12 4 5
13 4 7
14 3 8
15 0 8
16 0 0
17 3 0
18 4 1
19 4 3
20 2 4
21
22 C

View File

@ -0,0 +1,495 @@
"""
Boolean geometry four by four matrix.
"""
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 fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import archive
from fabmetheus_utilities import euclidean
from fabmetheus_utilities import xml_simple_writer
import cStringIO
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
globalExecutionOrder = 300
def addVertexes(geometryOutput, vertexes):
'Add the vertexes.'
if geometryOutput.__class__ == list:
for element in geometryOutput:
addVertexes(element, vertexes)
return
if geometryOutput.__class__ == dict:
for geometryOutputKey in geometryOutput.keys():
if geometryOutputKey == 'vertex':
vertexes += geometryOutput[geometryOutputKey]
else:
addVertexes(geometryOutput[geometryOutputKey], vertexes)
def getBranchMatrix(elementNode):
'Get matrix starting from the object if it exists, otherwise get a matrix starting from stratch.'
branchMatrix = Matrix()
matrixChildElement = elementNode.getFirstChildByLocalName('matrix')
if matrixChildElement != None:
branchMatrix = branchMatrix.getFromElementNode(matrixChildElement, '')
branchMatrix = branchMatrix.getFromElementNode(elementNode, 'matrix.')
if elementNode.xmlObject == None:
return branchMatrix
elementNodeMatrix = elementNode.xmlObject.getMatrix4X4()
if elementNodeMatrix == None:
return branchMatrix
return elementNodeMatrix.getOtherTimesSelf(branchMatrix.tetragrid)
def getBranchMatrixSetElementNode(elementNode):
'Get matrix starting from the object if it exists, otherwise get a matrix starting from stratch.'
branchMatrix = getBranchMatrix(elementNode)
setElementNodeDictionaryMatrix(elementNode, branchMatrix)
return branchMatrix
def getCumulativeVector3Remove(defaultVector3, elementNode, prefix):
'Get cumulative vector3 and delete the prefixed attributes.'
if prefix == '':
defaultVector3.x = evaluate.getEvaluatedFloat(defaultVector3.x, elementNode, 'x')
defaultVector3.y = evaluate.getEvaluatedFloat(defaultVector3.y, elementNode, 'y')
defaultVector3.z = evaluate.getEvaluatedFloat(defaultVector3.z, elementNode, 'z')
euclidean.removeElementsFromDictionary(elementNode.attributes, ['x', 'y', 'z'])
prefix = 'cartesian'
defaultVector3 = evaluate.getVector3ByPrefix(defaultVector3, elementNode, prefix)
euclidean.removePrefixFromDictionary(elementNode.attributes, prefix)
return defaultVector3
def getDiagonalSwitchedTetragrid(angleDegrees, diagonals):
'Get the diagonals and switched matrix by degrees.'
return getDiagonalSwitchedTetragridByRadians(math.radians(angleDegrees), diagonals)
def getDiagonalSwitchedTetragridByPolar(diagonals, unitPolar):
'Get the diagonals and switched matrix by unitPolar.'
diagonalSwitchedTetragrid = getIdentityTetragrid()
for diagonal in diagonals:
diagonalSwitchedTetragrid[diagonal][diagonal] = unitPolar.real
diagonalSwitchedTetragrid[diagonals[0]][diagonals[1]] = -unitPolar.imag
diagonalSwitchedTetragrid[diagonals[1]][diagonals[0]] = unitPolar.imag
return diagonalSwitchedTetragrid
def getDiagonalSwitchedTetragridByRadians(angleRadians, diagonals):
'Get the diagonals and switched matrix by radians.'
return getDiagonalSwitchedTetragridByPolar(diagonals, euclidean.getWiddershinsUnitPolar(angleRadians))
def getIdentityTetragrid(tetragrid=None):
'Get four by four matrix with diagonal elements set to one.'
if tetragrid == None:
return [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]]
return tetragrid
def getIsIdentityTetragrid(tetragrid):
'Determine if the tetragrid is the identity tetragrid.'
for column in xrange(4):
for row in xrange(4):
if column == row:
if tetragrid[column][row] != 1.0:
return False
elif tetragrid[column][row] != 0.0:
return False
return True
def getIsIdentityTetragridOrNone(tetragrid):
'Determine if the tetragrid is None or if it is the identity tetragrid.'
if tetragrid == None:
return True
return getIsIdentityTetragrid(tetragrid)
def getKeyA(row, column, prefix=''):
'Get the a format key string from row & column, counting from zero.'
return '%sa%s%s' % (prefix, row, column)
def getKeyM(row, column, prefix=''):
'Get the m format key string from row & column, counting from one.'
return '%sm%s%s' % (prefix, row + 1, column + 1)
def getKeysA(prefix=''):
'Get the matrix keys, counting from zero.'
keysA = []
for row in xrange(4):
for column in xrange(4):
key = getKeyA(row, column, prefix)
keysA.append(key)
return keysA
def getKeysM(prefix=''):
'Get the matrix keys, counting from one.'
keysM = []
for row in xrange(4):
for column in xrange(4):
key = getKeyM(row, column, prefix)
keysM.append(key)
return keysM
def getRemovedFloat(defaultFloat, elementNode, key, prefix):
'Get the float by the key and the prefix.'
prefixKey = prefix + key
if prefixKey in elementNode.attributes:
floatValue = evaluate.getEvaluatedFloat(None, elementNode, prefixKey)
if floatValue == None:
print('Warning, evaluated value in getRemovedFloatByKeys in matrix is None for key:')
print(prefixKey)
print('for elementNode dictionary value:')
print(elementNode.attributes[prefixKey])
print('for elementNode dictionary:')
print(elementNode.attributes)
else:
defaultFloat = floatValue
del elementNode.attributes[prefixKey]
return defaultFloat
def getRemovedFloatByKeys(defaultFloat, elementNode, keys, prefix):
'Get the float by the keys and the prefix.'
for key in keys:
defaultFloat = getRemovedFloat(defaultFloat, elementNode, key, prefix)
return defaultFloat
def getRotateAroundAxisTetragrid(elementNode, prefix):
'Get rotate around axis tetragrid and delete the axis and angle attributes.'
angle = getRemovedFloatByKeys(0.0, elementNode, ['angle', 'counterclockwise'], prefix)
angle -= getRemovedFloat(0.0, elementNode, 'clockwise', prefix)
if angle == 0.0:
return None
angleRadians = math.radians(angle)
axis = getCumulativeVector3Remove(Vector3(), elementNode, prefix + 'axis')
axisLength = abs(axis)
if axisLength <= 0.0:
print('Warning, axisLength was zero in getRotateAroundAxisTetragrid in matrix so nothing will be done for:')
print(elementNode)
return None
axis /= axisLength
tetragrid = getIdentityTetragrid()
cosAngle = math.cos(angleRadians)
sinAngle = math.sin(angleRadians)
oneMinusCos = 1.0 - math.cos(angleRadians)
xx = axis.x * axis.x
xy = axis.x * axis.y
xz = axis.x * axis.z
yy = axis.y * axis.y
yz = axis.y * axis.z
zz = axis.z * axis.z
tetragrid[0] = [cosAngle + xx * oneMinusCos, xy * oneMinusCos - axis.z * sinAngle, xz * oneMinusCos + axis.y * sinAngle, 0.0]
tetragrid[1] = [xy * oneMinusCos + axis.z * sinAngle, cosAngle + yy * oneMinusCos, yz * oneMinusCos - axis.x * sinAngle, 0.0]
tetragrid[2] = [xz * oneMinusCos - axis.y * sinAngle, yz * oneMinusCos + axis.x * sinAngle, cosAngle + zz * oneMinusCos, 0.0]
return tetragrid
def getRotateTetragrid(elementNode, prefix):
'Get rotate tetragrid and delete the rotate attributes.'
# http://en.wikipedia.org/wiki/Rotation_matrix
rotateMatrix = Matrix()
rotateMatrix.tetragrid = getRotateAroundAxisTetragrid(elementNode, prefix)
zAngle = getRemovedFloatByKeys(0.0, elementNode, ['axisclockwisez', 'observerclockwisez', 'z'], prefix)
zAngle -= getRemovedFloatByKeys(0.0, elementNode, ['axiscounterclockwisez', 'observercounterclockwisez'], prefix)
if zAngle != 0.0:
rotateMatrix.tetragrid = getTetragridTimesOther(getDiagonalSwitchedTetragrid(-zAngle, [0, 1]), rotateMatrix.tetragrid)
xAngle = getRemovedFloatByKeys(0.0, elementNode, ['axisclockwisex', 'observerclockwisex', 'x'], prefix)
xAngle -= getRemovedFloatByKeys(0.0, elementNode, ['axiscounterclockwisex', 'observercounterclockwisex'], prefix)
if xAngle != 0.0:
rotateMatrix.tetragrid = getTetragridTimesOther(getDiagonalSwitchedTetragrid(-xAngle, [1, 2]), rotateMatrix.tetragrid)
yAngle = getRemovedFloatByKeys(0.0, elementNode, ['axiscounterclockwisey', 'observerclockwisey', 'y'], prefix)
yAngle -= getRemovedFloatByKeys(0.0, elementNode, ['axisclockwisey', 'observercounterclockwisey'], prefix)
if yAngle != 0.0:
rotateMatrix.tetragrid = getTetragridTimesOther(getDiagonalSwitchedTetragrid(yAngle, [0, 2]), rotateMatrix.tetragrid)
return rotateMatrix.tetragrid
def getScaleTetragrid(elementNode, prefix):
'Get scale matrix and delete the scale attributes.'
scaleDefaultVector3 = Vector3(1.0, 1.0, 1.0)
scale = getCumulativeVector3Remove(scaleDefaultVector3.copy(), elementNode, prefix)
if scale == scaleDefaultVector3:
return None
return [[scale.x, 0.0, 0.0, 0.0], [0.0, scale.y, 0.0, 0.0], [0.0, 0.0, scale.z, 0.0], [0.0, 0.0, 0.0, 1.0]]
def getTetragridA(elementNode, prefix, tetragrid):
'Get the tetragrid from the elementNode letter a values.'
keysA = getKeysA(prefix)
evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, keysA)
if len(evaluatedDictionary.keys()) < 1:
return tetragrid
for row in xrange(4):
for column in xrange(4):
key = getKeyA(row, column, prefix)
if key in evaluatedDictionary:
value = evaluatedDictionary[key]
if value == None or value == 'None':
print('Warning, value in getTetragridA in matrix is None for key for dictionary:')
print(key)
print(evaluatedDictionary)
else:
tetragrid = getIdentityTetragrid(tetragrid)
tetragrid[row][column] = float(value)
euclidean.removeElementsFromDictionary(elementNode.attributes, keysA)
return tetragrid
def getTetragridC(elementNode, prefix, tetragrid):
'Get the matrix Tetragrid from the elementNode letter c values.'
columnKeys = 'Pc1 Pc2 Pc3 Pc4'.replace('P', prefix).split()
evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, columnKeys)
if len(evaluatedDictionary.keys()) < 1:
return tetragrid
for columnKeyIndex, columnKey in enumerate(columnKeys):
if columnKey in evaluatedDictionary:
value = evaluatedDictionary[columnKey]
if value == None or value == 'None':
print('Warning, value in getTetragridC in matrix is None for columnKey for dictionary:')
print(columnKey)
print(evaluatedDictionary)
else:
tetragrid = getIdentityTetragrid(tetragrid)
for elementIndex, element in enumerate(value):
tetragrid[elementIndex][columnKeyIndex] = element
euclidean.removeElementsFromDictionary(elementNode.attributes, columnKeys)
return tetragrid
def getTetragridCopy(tetragrid):
'Get tetragrid copy.'
if tetragrid == None:
return None
tetragridCopy = []
for tetragridRow in tetragrid:
tetragridCopy.append(tetragridRow[:])
return tetragridCopy
def getTetragridM(elementNode, prefix, tetragrid):
'Get the tetragrid from the elementNode letter m values.'
keysM = getKeysM(prefix)
evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, keysM)
if len(evaluatedDictionary.keys()) < 1:
return tetragrid
for row in xrange(4):
for column in xrange(4):
key = getKeyM(row, column, prefix)
if key in evaluatedDictionary:
value = evaluatedDictionary[key]
if value == None or value == 'None':
print('Warning, value in getTetragridM in matrix is None for key for dictionary:')
print(key)
print(evaluatedDictionary)
else:
tetragrid = getIdentityTetragrid(tetragrid)
tetragrid[row][column] = float(value)
euclidean.removeElementsFromDictionary(elementNode.attributes, keysM)
return tetragrid
def getTetragridMatrix(elementNode, prefix, tetragrid):
'Get the tetragrid from the elementNode matrix value.'
matrixKey = prefix + 'matrix'
evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, [matrixKey])
if len(evaluatedDictionary.keys()) < 1:
return tetragrid
value = evaluatedDictionary[matrixKey]
if value == None or value == 'None':
print('Warning, value in getTetragridMatrix in matrix is None for matrixKey for dictionary:')
print(matrixKey)
print(evaluatedDictionary)
else:
tetragrid = getIdentityTetragrid(tetragrid)
for rowIndex, row in enumerate(value):
for elementIndex, element in enumerate(row):
tetragrid[rowIndex][elementIndex] = element
euclidean.removeElementsFromDictionary(elementNode.attributes, [matrixKey])
return tetragrid
def getTetragridR(elementNode, prefix, tetragrid):
'Get the tetragrid from the elementNode letter r values.'
rowKeys = 'Pr1 Pr2 Pr3 Pr4'.replace('P', prefix).split()
evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, rowKeys)
if len(evaluatedDictionary.keys()) < 1:
return tetragrid
for rowKeyIndex, rowKey in enumerate(rowKeys):
if rowKey in evaluatedDictionary:
value = evaluatedDictionary[rowKey]
if value == None or value == 'None':
print('Warning, value in getTetragridR in matrix is None for rowKey for dictionary:')
print(rowKey)
print(evaluatedDictionary)
else:
tetragrid = getIdentityTetragrid(tetragrid)
for elementIndex, element in enumerate(value):
tetragrid[rowKeyIndex][elementIndex] = element
euclidean.removeElementsFromDictionary(elementNode.attributes, rowKeys)
return tetragrid
def getTetragridTimesOther(firstTetragrid, otherTetragrid ):
'Get this matrix multiplied by the other matrix.'
#A down, B right from http://en.wikipedia.org/wiki/Matrix_multiplication
if firstTetragrid == None:
return otherTetragrid
if otherTetragrid == None:
return firstTetragrid
tetragridTimesOther = []
for row in xrange(4):
matrixRow = firstTetragrid[row]
tetragridTimesOtherRow = []
tetragridTimesOther.append(tetragridTimesOtherRow)
for column in xrange(4):
dotProduct = 0
for elementIndex in xrange(4):
dotProduct += matrixRow[elementIndex] * otherTetragrid[elementIndex][column]
tetragridTimesOtherRow.append(dotProduct)
return tetragridTimesOther
def getTransformedByList(floatList, point):
'Get the point transformed by the array.'
return floatList[0] * point.x + floatList[1] * point.y + floatList[2] * point.z + floatList[3]
def getTransformedVector3(tetragrid, vector3):
'Get the vector3 multiplied by a matrix.'
if getIsIdentityTetragridOrNone(tetragrid):
return vector3.copy()
return getTransformedVector3Blindly(tetragrid, vector3)
def getTransformedVector3Blindly(tetragrid, vector3):
'Get the vector3 multiplied by a tetragrid without checking if the tetragrid exists.'
return Vector3(
getTransformedByList(tetragrid[0], vector3),
getTransformedByList(tetragrid[1], vector3),
getTransformedByList(tetragrid[2], vector3))
def getTransformedVector3s(tetragrid, vector3s):
'Get the vector3s multiplied by a matrix.'
if getIsIdentityTetragridOrNone(tetragrid):
return euclidean.getPathCopy(vector3s)
transformedVector3s = []
for vector3 in vector3s:
transformedVector3s.append(getTransformedVector3Blindly(tetragrid, vector3))
return transformedVector3s
def getTransformTetragrid(elementNode, prefix):
'Get the tetragrid from the elementNode.'
tetragrid = getTetragridA(elementNode, prefix, None)
tetragrid = getTetragridC(elementNode, prefix, tetragrid)
tetragrid = getTetragridM(elementNode, prefix, tetragrid)
tetragrid = getTetragridMatrix(elementNode, prefix, tetragrid)
tetragrid = getTetragridR(elementNode, prefix, tetragrid)
return tetragrid
def getTranslateTetragrid(elementNode, prefix):
'Get translate matrix and delete the translate attributes.'
translation = getCumulativeVector3Remove(Vector3(), elementNode, prefix)
if translation.getIsDefault():
return None
return getTranslateTetragridByTranslation(translation)
def getTranslateTetragridByTranslation(translation):
'Get translate tetragrid by translation.'
return [[1.0, 0.0, 0.0, translation.x], [0.0, 1.0, 0.0, translation.y], [0.0, 0.0, 1.0, translation.z], [0.0, 0.0, 0.0, 1.0]]
def getVertexes(geometryOutput):
'Get the vertexes.'
vertexes = []
addVertexes(geometryOutput, vertexes)
return vertexes
def setAttributesToMultipliedTetragrid(elementNode, tetragrid):
'Set the element attribute dictionary and element matrix to the matrix times the tetragrid.'
setElementNodeDictionaryMatrix(elementNode, getBranchMatrix(elementNode).getOtherTimesSelf(tetragrid))
def setElementNodeDictionaryMatrix(elementNode, matrix4X4):
'Set the element attribute dictionary or element matrix to the matrix.'
if elementNode.xmlObject == None:
elementNode.attributes.update(matrix4X4.getAttributes('matrix.'))
else:
elementNode.xmlObject.matrix4X4 = matrix4X4
def transformVector3Blindly(tetragrid, vector3):
'Transform the vector3 by a tetragrid without checking to see if it exists.'
x = getTransformedByList(tetragrid[0], vector3)
y = getTransformedByList(tetragrid[1], vector3)
z = getTransformedByList(tetragrid[2], vector3)
vector3.x = x
vector3.y = y
vector3.z = z
def transformVector3ByMatrix(tetragrid, vector3):
'Transform the vector3 by a matrix.'
if getIsIdentityTetragridOrNone(tetragrid):
return
transformVector3Blindly(tetragrid, vector3)
def transformVector3sByMatrix(tetragrid, vector3s):
'Transform the vector3s by a matrix.'
if getIsIdentityTetragridOrNone(tetragrid):
return
for vector3 in vector3s:
transformVector3Blindly(tetragrid, vector3)
class Matrix:
'A four by four matrix.'
def __init__(self, tetragrid=None):
'Add empty lists.'
self.tetragrid = getTetragridCopy(tetragrid)
def __eq__(self, other):
'Determine whether this matrix is identical to other one.'
if other == None:
return False
if other.__class__ != self.__class__:
return False
return other.tetragrid == self.tetragrid
def __ne__(self, other):
'Determine whether this vector is not identical to other one.'
return not self.__eq__(other)
def __repr__(self):
'Get the string representation of this four by four matrix.'
output = cStringIO.StringIO()
self.addXML(0, output)
return output.getvalue()
def addXML(self, depth, output):
'Add xml for this object.'
attributes = self.getAttributes()
if len(attributes) > 0:
xml_simple_writer.addClosedXMLTag(attributes, depth, self.__class__.__name__.lower(), output)
def getAttributes(self, prefix=''):
'Get the attributes from row column attribute strings, counting from one.'
attributes = {}
if self.tetragrid == None:
return attributes
for row in xrange(4):
for column in xrange(4):
default = float(column == row)
value = self.tetragrid[row][column]
if abs( value - default ) > 0.00000000000001:
if abs(value) < 0.00000000000001:
value = 0.0
attributes[prefix + getKeyM(row, column)] = value
return attributes
def getFromElementNode(self, elementNode, prefix):
'Get the values from row column attribute strings, counting from one.'
attributes = elementNode.attributes
if attributes == None:
return self
self.tetragrid = getTetragridTimesOther(getTransformTetragrid(elementNode, prefix), self.tetragrid)
self.tetragrid = getTetragridTimesOther(getScaleTetragrid(elementNode, 'scale.'), self.tetragrid)
self.tetragrid = getTetragridTimesOther(getRotateTetragrid(elementNode, 'rotate.'), self.tetragrid)
self.tetragrid = getTetragridTimesOther(getTranslateTetragrid(elementNode, 'translate.'), self.tetragrid)
return self
def getOtherTimesSelf(self, otherTetragrid):
'Get this matrix reverse multiplied by the other matrix.'
return Matrix(getTetragridTimesOther(otherTetragrid, self.tetragrid))
def getSelfTimesOther(self, otherTetragrid):
'Get this matrix multiplied by the other matrix.'
return Matrix(getTetragridTimesOther(self.tetragrid, otherTetragrid))

View File

@ -0,0 +1,9 @@
#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
import os
import sys
numberOfLevelsDeepInPackageHierarchy = 3
packageFilePath = os.path.abspath(__file__)
for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ):
packageFilePath = os.path.dirname( packageFilePath )
if packageFilePath not in sys.path:
sys.path.insert( 0, packageFilePath )

View File

@ -0,0 +1,68 @@
"""
Boolean geometry scale.
"""
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 fabmetheus_utilities.geometry.creation import solid
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
globalExecutionOrder = 340
def getManipulatedGeometryOutput(elementNode, geometryOutput, prefix):
"Get equated geometryOutput."
scalePoints( elementNode, matrix.getVertexes(geometryOutput), prefix )
return geometryOutput
def getManipulatedPaths(close, elementNode, loop, prefix, sideLength):
"Get equated paths."
scalePoints( elementNode, loop, prefix )
return [loop]
def getNewDerivation(elementNode, prefix, sideLength):
'Get new derivation.'
return ScaleDerivation(elementNode)
def manipulateElementNode(elementNode, target):
"Manipulate the xml element."
derivation = ScaleDerivation(elementNode)
if derivation.scaleTetragrid == None:
print('Warning, scaleTetragrid was None in scale so nothing will be done for:')
print(elementNode)
return
matrix.setAttributesToMultipliedTetragrid(target, derivation.scaleTetragrid)
def processElementNode(elementNode):
"Process the xml element."
solid.processElementNodeByFunction(elementNode, manipulateElementNode)
def scalePoints(elementNode, points, prefix):
"Scale the points."
scaleVector3Default = Vector3(1.0, 1.0, 1.0)
scaleVector3 = matrix.getCumulativeVector3Remove(scaleVector3Default.copy(), elementNode, prefix)
if scaleVector3 == scaleVector3Default:
return
for point in points:
point.x *= scaleVector3.x
point.y *= scaleVector3.y
point.z *= scaleVector3.z
class ScaleDerivation:
"Class to hold scale variables."
def __init__(self, elementNode):
'Set defaults.'
self.scaleTetragrid = matrix.getScaleTetragrid(elementNode, '')

View File

@ -0,0 +1,67 @@
"""
Boolean geometry rotate.
"""
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 fabmetheus_utilities.geometry.creation import solid
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
globalExecutionOrder = 360
def getManipulatedGeometryOutput(elementNode, geometryOutput, prefix):
'Get equated geometryOutput.'
rotatePoints(elementNode, matrix.getVertexes(geometryOutput), prefix)
return geometryOutput
def getManipulatedPaths(close, elementNode, loop, prefix, sideLength):
'Get equated paths.'
rotatePoints(elementNode, loop, prefix)
return [loop]
def getNewDerivation(elementNode, prefix, sideLength):
'Get new derivation.'
return RotateDerivation(elementNode, prefix)
def manipulateElementNode(elementNode, target):
'Manipulate the xml element.'
derivation = RotateDerivation(elementNode, '')
if derivation.rotateTetragrid == None:
print('Warning, rotateTetragrid was None in rotate so nothing will be done for:')
print(elementNode)
return
matrix.setAttributesToMultipliedTetragrid(target, derivation.rotateTetragrid)
def processElementNode(elementNode):
'Process the xml element.'
solid.processElementNodeByFunction(elementNode, manipulateElementNode)
def rotatePoints(elementNode, points, prefix):
'Rotate the points.'
derivation = RotateDerivation(elementNode, prefix)
if derivation.rotateTetragrid == None:
print('Warning, rotateTetragrid was None in rotate so nothing will be done for:')
print(elementNode)
return
matrix.transformVector3sByMatrix(derivation.rotateTetragrid, points)
class RotateDerivation:
"Class to hold rotate variables."
def __init__(self, elementNode, prefix):
'Set defaults.'
self.rotateTetragrid = matrix.getRotateTetragrid(elementNode, prefix)

View File

@ -0,0 +1,67 @@
"""
Boolean geometry transform.
"""
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 fabmetheus_utilities.geometry.creation import solid
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
globalExecutionOrder = 320
def getManipulatedGeometryOutput(elementNode, geometryOutput, prefix):
'Get equated geometryOutput.'
transformPoints(elementNode, matrix.getVertexes(geometryOutput), prefix)
return geometryOutput
def getManipulatedPaths(close, elementNode, loop, prefix, sideLength):
'Get equated paths.'
transformPoints(elementNode, loop, prefix)
return [loop]
def getNewDerivation(elementNode, prefix, sideLength):
'Get new derivation.'
return TransformDerivation(elementNode, prefix)
def manipulateElementNode(elementNode, target):
'Manipulate the xml element.'
derivation = TransformDerivation(elementNode, '')
if derivation.transformTetragrid == None:
print('Warning, transformTetragrid was None in transform so nothing will be done for:')
print(elementNode)
return
matrix.setAttributesToMultipliedTetragrid(target, derivation.transformTetragrid)
def processElementNode(elementNode):
'Process the xml element.'
solid.processElementNodeByFunction(elementNode, manipulateElementNode)
def transformPoints(elementNode, points, prefix):
'Transform the points.'
derivation = TransformDerivation(elementNode, prefix)
if derivation.transformTetragrid == None:
print('Warning, transformTetragrid was None in transform so nothing will be done for:')
print(elementNode)
return
matrix.transformVector3sByMatrix(derivation.transformTetragrid, points)
class TransformDerivation:
"Class to hold transform variables."
def __init__(self, elementNode, prefix):
'Set defaults.'
self.transformTetragrid = matrix.getTransformTetragrid(elementNode, prefix)

View File

@ -0,0 +1,68 @@
"""
Boolean geometry translation.
"""
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 fabmetheus_utilities.geometry.creation import solid
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
globalExecutionOrder = 380
def getManipulatedGeometryOutput(elementNode, geometryOutput, prefix):
"Get equated geometryOutput."
translatePoints(elementNode, matrix.getVertexes(geometryOutput), prefix)
return geometryOutput
def getManipulatedPaths(close, elementNode, loop, prefix, sideLength):
"Get equated paths."
translatePoints(elementNode, loop, prefix)
return [loop]
def getNewDerivation(elementNode, prefix, sideLength):
'Get new derivation.'
return TranslateDerivation(elementNode)
def manipulateElementNode(elementNode, target):
"Manipulate the xml element."
derivation = TranslateDerivation(elementNode)
if derivation.translateTetragrid == None:
print('Warning, translateTetragrid was None in translate so nothing will be done for:')
print(elementNode)
return
matrix.setAttributesToMultipliedTetragrid(target, derivation.translateTetragrid)
def processElementNode(elementNode):
"Process the xml element."
solid.processElementNodeByFunction(elementNode, manipulateElementNode)
def translateNegativesPositives(negatives, positives, translation):
'Translate the negatives and postives.'
euclidean.translateVector3Path(matrix.getVertexes(negatives), translation)
euclidean.translateVector3Path(matrix.getVertexes(positives), translation)
def translatePoints(elementNode, points, prefix):
"Translate the points."
translateVector3 = matrix.getCumulativeVector3Remove(Vector3(), elementNode, prefix)
if abs(translateVector3) > 0.0:
euclidean.translateVector3Path(points, translateVector3)
class TranslateDerivation:
"Class to hold translate variables."
def __init__(self, elementNode):
'Set defaults.'
self.translateTetragrid = matrix.getTranslateTetragrid(elementNode, '')

View File

@ -0,0 +1,9 @@
#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
import os
import sys
numberOfLevelsDeepInPackageHierarchy = 3
packageFilePath = os.path.abspath(__file__)
for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ):
packageFilePath = os.path.dirname( packageFilePath )
if packageFilePath not in sys.path:
sys.path.insert( 0, packageFilePath )

View File

@ -0,0 +1,129 @@
"""
Boolean geometry array.
"""
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 fabmetheus_utilities.geometry.geometry_tools import vertex
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def addPathToGroup(derivation, groupDictionaryCopy, path, targetMatrix, totalIndex):
'Add path to the array group.'
for pointIndex, point in enumerate(path):
arrayElement = derivation.target.getCopy(derivation.elementNode.getIDSuffix(totalIndex), derivation.elementNode)
arrayDictionary = arrayElement.attributes
arrayDictionary['visible'] = str(derivation.visible).lower()
arrayDictionary.update(groupDictionaryCopy)
euclidean.removeTrueFromDictionary(arrayDictionary, 'visible')
vertexMatrix = matrix.Matrix(matrix.getTranslateTetragridByTranslation(point))
zAngle = totalIndex * 50.0
rotationMatrix = getRotationMatrix(arrayDictionary, derivation, path, point, pointIndex)
arrayElementMatrix = vertexMatrix.getSelfTimesOther(rotationMatrix.getSelfTimesOther(targetMatrix.tetragrid).tetragrid)
arrayDictionary.update(arrayElementMatrix.getAttributes('matrix.'))
arrayDictionary['_arrayIndex'] = totalIndex
arrayDictionary['_arrayPoint'] = point
totalIndex += 1
def getNewDerivation(elementNode):
'Get new derivation.'
return ArrayDerivation(elementNode)
def getRotationMatrix(arrayDictionary, derivation, path, point, pointIndex):
'Get rotationMatrix.'
if len(path) < 2 or not derivation.track:
return matrix.Matrix()
point = point.dropAxis()
begin = path[(pointIndex + len(path) - 1) % len(path)].dropAxis()
end = path[(pointIndex + 1) % len(path)].dropAxis()
pointMinusBegin = point - begin
pointMinusBeginLength = abs(pointMinusBegin)
endMinusPoint = end - point
endMinusPointLength = abs(endMinusPoint)
if not derivation.closed:
if pointIndex == 0 and endMinusPointLength > 0.0:
return getRotationMatrixByPolar(arrayDictionary, endMinusPoint, endMinusPointLength)
elif pointIndex == len(path) - 1 and pointMinusBeginLength > 0.0:
return getRotationMatrixByPolar(arrayDictionary, pointMinusBegin, pointMinusBeginLength)
if pointMinusBeginLength <= 0.0:
print('Warning, point equals previous point in getRotationMatrix in array for:')
print(path)
print(pointIndex)
print(derivation.elementNode)
return matrix.Matrix()
pointMinusBegin /= pointMinusBeginLength
if endMinusPointLength <= 0.0:
print('Warning, point equals next point in getRotationMatrix in array for:')
print(path)
print(pointIndex)
print(derivation.elementNode)
return matrix.Matrix()
endMinusPoint /= endMinusPointLength
averagePolar = pointMinusBegin + endMinusPoint
averagePolarLength = abs(averagePolar)
if averagePolarLength <= 0.0:
print('Warning, averagePolarLength is zero in getRotationMatrix in array for:')
print(path)
print(pointIndex)
print(derivation.elementNode)
return matrix.Matrix()
return getRotationMatrixByPolar(arrayDictionary, averagePolar, averagePolarLength)
def getRotationMatrixByPolar(arrayDictionary, polar, polarLength):
'Get rotationMatrix by polar and polarLength.'
polar /= polarLength
arrayDictionary['_arrayRotation'] = math.degrees(math.atan2(polar.imag, polar.real))
return matrix.Matrix(matrix.getDiagonalSwitchedTetragridByPolar([0, 1], polar))
def processElementNode(elementNode):
"Process the xml element."
processElementNodeByDerivation(None, elementNode)
def processElementNodeByDerivation(derivation, elementNode):
'Process the xml element by derivation.'
if derivation == None:
derivation = ArrayDerivation(elementNode)
if derivation.target == None:
print('Warning, array could not get target for:')
print(elementNode)
return
if len(derivation.paths) < 1:
print('Warning, array could not get paths for:')
print(elementNode)
return
groupDictionaryCopy = elementNode.attributes.copy()
euclidean.removeElementsFromDictionary(groupDictionaryCopy, ['closed', 'paths', 'target', 'track', 'vertexes'])
evaluate.removeIdentifiersFromDictionary(groupDictionaryCopy)
targetMatrix = matrix.getBranchMatrixSetElementNode(derivation.target)
elementNode.localName = 'group'
totalIndex = 0
for path in derivation.paths:
addPathToGroup(derivation, groupDictionaryCopy, path, targetMatrix, totalIndex)
elementNode.getXMLProcessor().processElementNode(elementNode)
class ArrayDerivation:
"Class to hold array variables."
def __init__(self, elementNode):
'Set defaults.'
self.closed = evaluate.getEvaluatedBoolean(True, elementNode, 'closed')
self.elementNode = elementNode
self.paths = evaluate.getTransformedPathsByKey([], elementNode, 'paths')
vertexTargets = evaluate.getElementNodesByKey(elementNode, 'vertexes')
for vertexTarget in vertexTargets:
self.paths.append(vertexTarget.getVertexes())
self.target = evaluate.getElementNodeByKey(elementNode, 'target')
self.track = evaluate.getEvaluatedBoolean(True, elementNode, 'track')
self.visible = evaluate.getEvaluatedBoolean(True, elementNode, 'visible')

View File

@ -0,0 +1,95 @@
"""
Boolean geometry carve.
"""
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 fabmetheus_utilities.geometry.geometry_tools import path
from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting
from fabmetheus_utilities.geometry.geometry_utilities import boolean_geometry
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities.geometry.solids import triangle_mesh
from fabmetheus_utilities import euclidean
from fabmetheus_utilities import xml_simple_reader
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getLinkedElementNode(idSuffix, parentNode, target):
'Get elementNode with identifiers and parentNode.'
linkedElementNode = xml_simple_reader.ElementNode()
euclidean.overwriteDictionary(target.attributes, ['id', 'name', 'quantity'], linkedElementNode.attributes)
linkedElementNode.addSuffixToID(idSuffix)
tagKeys = target.getTagKeys()
tagKeys.append('carve')
tagKeys.sort()
tags = ', '.join(tagKeys)
linkedElementNode.attributes['tags'] = tags
linkedElementNode.setParentAddToChildNodes(parentNode)
linkedElementNode.addToIdentifierDictionaries()
return linkedElementNode
def getNewDerivation(elementNode):
'Get new derivation.'
return CarveDerivation(elementNode)
def processElementNode(elementNode):
'Process the xml element.'
processElementNodeByDerivation(None, elementNode)
def processElementNodeByDerivation(derivation, elementNode):
'Process the xml element by derivation.'
if derivation == None:
derivation = CarveDerivation(elementNode)
targetElementNode = derivation.targetElementNode
if targetElementNode == None:
print('Warning, carve could not get target for:')
print(elementNode)
return
xmlObject = targetElementNode.xmlObject
if xmlObject == None:
print('Warning, processElementNodeByDerivation in carve could not get xmlObject for:')
print(targetElementNode)
print(derivation.elementNode)
return
matrix.getBranchMatrixSetElementNode(targetElementNode)
transformedVertexes = xmlObject.getTransformedVertexes()
if len(transformedVertexes) < 1:
print('Warning, transformedVertexes is zero in processElementNodeByDerivation in carve for:')
print(xmlObject)
print(targetElementNode)
print(derivation.elementNode)
return
elementNode.localName = 'group'
elementNode.getXMLProcessor().processElementNode(elementNode)
minimumZ = boolean_geometry.getMinimumZ(xmlObject)
maximumZ = euclidean.getTopPath(transformedVertexes)
zoneArrangement = triangle_mesh.ZoneArrangement(derivation.layerHeight, transformedVertexes)
oldVisibleString = targetElementNode.attributes['visible']
targetElementNode.attributes['visible'] = True
z = minimumZ + 0.5 * derivation.layerHeight
loopLayers = boolean_geometry.getLoopLayers([xmlObject], derivation.importRadius, derivation.layerHeight, maximumZ, False, z, zoneArrangement)
targetElementNode.attributes['visible'] = oldVisibleString
for loopLayerIndex, loopLayer in enumerate(loopLayers):
if len(loopLayer.loops) > 0:
pathElement = getLinkedElementNode('_carve_%s' % loopLayerIndex, elementNode, targetElementNode)
vector3Loops = euclidean.getVector3Paths(loopLayer.loops, loopLayer.z)
path.convertElementNode(pathElement, vector3Loops)
class CarveDerivation:
"Class to hold carve variables."
def __init__(self, elementNode):
'Set defaults.'
self.elementNode = elementNode
self.importRadius = setting.getImportRadius(elementNode)
self.layerHeight = setting.getLayerHeight(elementNode)
self.targetElementNode = evaluate.getElementNodeByKey(elementNode, 'target')

View File

@ -0,0 +1,72 @@
"""
Boolean geometry copy.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.creation import solid
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities import euclidean
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getNewDerivation(elementNode):
'Get new derivation.'
return CopyDerivation(elementNode)
def processElementNode(elementNode):
'Process the xml element.'
processElementNodeByDerivation(None, elementNode)
def processElementNodeByDerivation(derivation, elementNode):
'Process the xml element by derivation.'
if derivation == None:
derivation = CopyDerivation(elementNode)
if derivation.target == None:
print('Warning, copy could not get target for:')
print(elementNode)
return
del elementNode.attributes['target']
copyMatrix = matrix.getBranchMatrixSetElementNode(elementNode)
targetMatrix = matrix.getBranchMatrixSetElementNode(derivation.target)
targetDictionaryCopy = evaluate.removeIdentifiersFromDictionary(derivation.target.attributes.copy())
targetDictionaryCopy.update(elementNode.attributes)
elementNode.attributes = targetDictionaryCopy
euclidean.removeTrueFromDictionary(elementNode.attributes, 'visible')
elementNode.localName = derivation.target.localName
derivation.target.copyXMLChildNodes(elementNode.getIDSuffix(), elementNode)
elementNode.getXMLProcessor().processElementNode(elementNode)
if copyMatrix != None and targetMatrix != None:
elementNode.xmlObject.matrix4X4 = copyMatrix.getSelfTimesOther(targetMatrix.tetragrid)
if elementNode.xmlObject == None:
return
if len(elementNode.xmlObject.getPaths()) > 0:
lineation.processElementNode(elementNode)
return
geometryOutput = elementNode.xmlObject.getGeometryOutput()
if geometryOutput == None:
return
solidMatchingPlugins = solid.getSolidMatchingPlugins(elementNode)
if len(solidMatchingPlugins) == 0:
return
geometryOutput = solid.getGeometryOutputByManipulation(elementNode, geometryOutput)
elementNode.xmlObject.transformGeometryOutput(geometryOutput)
lineation.removeChildNodesFromElementObject(elementNode)
elementNode.getXMLProcessor().convertElementNode(elementNode, geometryOutput)
class CopyDerivation:
"Class to hold copy variables."
def __init__(self, elementNode):
'Set defaults.'
self.target = evaluate.getElementNodeByKey(elementNode, 'target')

View File

@ -0,0 +1,114 @@
"""
Boolean geometry disjoin.
"""
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 fabmetheus_utilities.geometry.geometry_tools import path
from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting
from fabmetheus_utilities.geometry.geometry_utilities import boolean_geometry
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities.geometry.solids import difference
from fabmetheus_utilities.geometry.solids import triangle_mesh
from fabmetheus_utilities import euclidean
from fabmetheus_utilities import xml_simple_reader
from fabmetheus_utilities.vector3 import Vector3
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getLinkedElementNode(idSuffix, parentNode, target):
'Get elementNode with identifiers and parentNode.'
linkedElementNode = xml_simple_reader.ElementNode()
euclidean.overwriteDictionary(target.attributes, ['id', 'name', 'quantity'], linkedElementNode.attributes)
linkedElementNode.addSuffixToID(idSuffix)
tagKeys = target.getTagKeys()
tagKeys.append('disjoin')
tagKeys.sort()
tags = ', '.join(tagKeys)
linkedElementNode.attributes['tags'] = tags
linkedElementNode.setParentAddToChildNodes(parentNode)
linkedElementNode.addToIdentifierDictionaries()
return linkedElementNode
def getNewDerivation(elementNode):
'Get new derivation.'
return DisjoinDerivation(elementNode)
def processElementNode(elementNode):
'Process the xml element.'
processElementNodeByDerivation(None, elementNode)
def processElementNodeByDerivation(derivation, elementNode):
'Process the xml element by derivation.'
if derivation == None:
derivation = DisjoinDerivation(elementNode)
targetElementNode = derivation.targetElementNode
if targetElementNode == None:
print('Warning, disjoin could not get target for:')
print(elementNode)
return
xmlObject = targetElementNode.xmlObject
if xmlObject == None:
print('Warning, processElementNodeByDerivation in disjoin could not get xmlObject for:')
print(targetElementNode)
print(derivation.elementNode)
return
matrix.getBranchMatrixSetElementNode(targetElementNode)
transformedVertexes = xmlObject.getTransformedVertexes()
if len(transformedVertexes) < 1:
print('Warning, transformedVertexes is zero in processElementNodeByDerivation in disjoin for:')
print(xmlObject)
print(targetElementNode)
print(derivation.elementNode)
return
elementNode.localName = 'group'
elementNode.getXMLProcessor().processElementNode(elementNode)
targetChainMatrix = matrix.Matrix(xmlObject.getMatrixChainTetragrid())
minimumZ = boolean_geometry.getMinimumZ(xmlObject)
z = minimumZ + 0.5 * derivation.sheetThickness
zoneArrangement = triangle_mesh.ZoneArrangement(derivation.layerHeight, transformedVertexes)
oldVisibleString = targetElementNode.attributes['visible']
targetElementNode.attributes['visible'] = True
loops = boolean_geometry.getEmptyZLoops([xmlObject], derivation.importRadius, False, z, zoneArrangement)
targetElementNode.attributes['visible'] = oldVisibleString
vector3Loops = euclidean.getVector3Paths(loops, z)
pathElement = getLinkedElementNode('_sheet', elementNode, targetElementNode)
path.convertElementNode(pathElement, vector3Loops)
targetOutput = xmlObject.getGeometryOutput()
differenceElement = getLinkedElementNode('_solid', elementNode, targetElementNode)
targetElementCopy = targetElementNode.getCopy('_positive', differenceElement)
targetElementCopy.attributes['visible'] = True
targetElementCopy.attributes.update(targetChainMatrix.getAttributes('matrix.'))
complexMaximum = euclidean.getMaximumByVector3Path(transformedVertexes).dropAxis()
complexMinimum = euclidean.getMinimumByVector3Path(transformedVertexes).dropAxis()
centerComplex = 0.5 * (complexMaximum + complexMinimum)
centerVector3 = Vector3(centerComplex.real, centerComplex.imag, minimumZ)
slightlyMoreThanHalfExtent = 0.501 * (complexMaximum - complexMinimum)
inradius = Vector3(slightlyMoreThanHalfExtent.real, slightlyMoreThanHalfExtent.imag, derivation.sheetThickness)
cubeElement = xml_simple_reader.ElementNode()
cubeElement.attributes['inradius'] = str(inradius)
if not centerVector3.getIsDefault():
cubeElement.attributes['translate.'] = str(centerVector3)
cubeElement.localName = 'cube'
cubeElement.setParentAddToChildNodes(differenceElement)
difference.processElementNode(differenceElement)
class DisjoinDerivation:
"Class to hold disjoin variables."
def __init__(self, elementNode):
'Set defaults.'
self.elementNode = elementNode
self.importRadius = setting.getImportRadius(elementNode)
self.layerHeight = setting.getLayerHeight(elementNode)
self.sheetThickness = setting.getSheetThickness(elementNode)
self.targetElementNode = evaluate.getElementNodeByKey(elementNode, 'target')

View File

@ -0,0 +1,99 @@
"""
Boolean geometry group of solids.
"""
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 fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.solids import group
from fabmetheus_utilities import xml_simple_reader
from fabmetheus_utilities import xml_simple_writer
from fabmetheus_utilities import archive
from fabmetheus_utilities import euclidean
from fabmetheus_utilities import gcodec
from fabmetheus_utilities import settings
import cStringIO
import os
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def appendAttributes(fromElementNode, toElementNode):
'Append the attributes from the child nodes of fromElementNode to the attributes of toElementNode.'
for childNode in fromElementNode.childNodes:
toElementNode.attributes.update(evaluate.removeIdentifiersFromDictionary(childNode.attributes.copy()))
def getNewDerivation(elementNode):
'Get new derivation.'
return ImportDerivation(elementNode)
def getXMLFromCarvingFileName(fileName):
'Get xml text from xml text.'
carving = fabmetheus_interpret.getCarving(fileName)
if carving == None:
return ''
output = xml_simple_writer.getBeginGeometryXMLOutput()
carving.addXML(0, output)
return xml_simple_writer.getEndGeometryXMLString(output)
def processElementNode(elementNode):
"Process the xml element."
processElementNodeByDerivation(None, elementNode)
def processElementNodeByDerivation(derivation, elementNode):
'Process the xml element by derivation.'
if derivation == None:
derivation = ImportDerivation(elementNode)
if derivation.fileName == None:
return
parserFileName = elementNode.getOwnerDocument().fileName
absoluteFileName = archive.getAbsoluteFolderPath(parserFileName, derivation.fileName)
if 'models/' not in absoluteFileName:
print('Warning, models/ was not in the absolute file path, so for security nothing will be done for:')
print(elementNode)
print('For which the absolute file path is:')
print(absoluteFileName)
print('The import tool can only read a file which has models/ in the file path.')
print('To import the file, move the file into a folder called model/ or a subfolder which is inside the model folder tree.')
return
xmlText = ''
if derivation.fileName.endswith('.xml'):
xmlText = archive.getFileText(absoluteFileName)
else:
xmlText = getXMLFromCarvingFileName(absoluteFileName)
print('The import tool is opening the file:')
print(absoluteFileName)
if xmlText == '':
print('The file %s could not be found by processElementNode in import.' % derivation.fileName)
return
if derivation.importName == None:
elementNode.attributes['_importName'] = archive.getUntilDot(derivation.fileName)
if derivation.basename:
elementNode.attributes['_importName'] = os.path.basename(elementNode.attributes['_importName'])
xml_simple_reader.createAppendByText(elementNode, xmlText)
if derivation.appendDocumentElement:
appendAttributes(elementNode, elementNode.getDocumentElement())
if derivation.appendElement:
appendAttributes(elementNode, elementNode)
elementNode.localName = 'group'
evaluate.processArchivable(group.Group, elementNode)
class ImportDerivation:
"Class to hold import variables."
def __init__(self, elementNode):
'Set defaults.'
self.appendDocumentElement = evaluate.getEvaluatedBoolean(False, elementNode, 'appendDocumentElement')
self.appendElement = evaluate.getEvaluatedBoolean(False, elementNode, 'appendElement')
self.basename = evaluate.getEvaluatedBoolean(True, elementNode, 'basename')
self.elementNode = elementNode
self.fileName = evaluate.getEvaluatedString('', elementNode, 'file')
self.importName = evaluate.getEvaluatedString(None, elementNode, '_importName')

View File

@ -0,0 +1,98 @@
"""
Boolean geometry write.
"""
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 fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities import archive
from fabmetheus_utilities import euclidean
from fabmetheus_utilities import gcodec
import os
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def getNewDerivation(elementNode):
'Get new derivation.'
return WriteDerivation(elementNode)
def processElementNode(elementNode):
"Process the xml element."
processElementNodeByDerivation(None, elementNode)
def processElementNodeByDerivation(derivation, elementNode):
'Process the xml element by derivation.'
if derivation == None:
derivation = WriteDerivation(elementNode)
if len(derivation.targets) < 1:
print('Warning, processElementNode in write could not get targets for:')
print(elementNode)
return
fileNames = []
for target in derivation.targets:
writeElementNode(derivation, fileNames, target)
def writeElementNode(derivation, fileNames, target):
"Write a quantity of the target."
xmlObject = target.xmlObject
if xmlObject == None:
print('Warning, writeTarget in write could not get xmlObject for:')
print(target)
print(derivation.elementNode)
return
parserDirectory = os.path.dirname(derivation.elementNode.getOwnerDocument().fileName)
absoluteFolderDirectory = os.path.abspath(os.path.join(parserDirectory, derivation.folderName))
if '/models' not in absoluteFolderDirectory:
print('Warning, models/ was not in the absolute file path, so for security nothing will be done for:')
print(derivation.elementNode)
print('For which the absolute folder path is:')
print(absoluteFolderDirectory)
print('The write tool can only write a file which has models/ in the file path.')
print('To write the file, move the file into a folder called model/ or a subfolder which is inside the model folder tree.')
return
quantity = evaluate.getEvaluatedInt(1, target, 'quantity')
for itemIndex in xrange(quantity):
writeXMLObject(absoluteFolderDirectory, derivation, fileNames, target, xmlObject)
def writeXMLObject(absoluteFolderDirectory, derivation, fileNames, target, xmlObject):
"Write one instance of the xmlObject."
extension = evaluate.getEvaluatedString(xmlObject.getFabricationExtension(), derivation.elementNode, 'extension')
fileNameRoot = derivation.fileName
if fileNameRoot == '':
fileNameRoot = evaluate.getEvaluatedString('', target, 'name')
fileNameRoot = evaluate.getEvaluatedString(fileNameRoot, target, 'id')
fileNameRoot += derivation.suffix
fileName = '%s.%s' % (fileNameRoot, extension)
suffixIndex = 2
while fileName in fileNames:
fileName = '%s_%s.%s' % (fileNameRoot, suffixIndex, extension)
suffixIndex += 1
absoluteFileName = os.path.join(absoluteFolderDirectory, fileName)
fileNames.append(fileName)
archive.makeDirectory(absoluteFolderDirectory)
if not derivation.writeMatrix:
xmlObject.matrix4X4 = matrix.Matrix()
print('The write tool generated the file:')
print(absoluteFileName)
archive.writeFileText(absoluteFileName, xmlObject.getFabricationText(derivation.addLayerTemplate))
class WriteDerivation:
"Class to hold write variables."
def __init__(self, elementNode):
'Set defaults.'
self.addLayerTemplate = evaluate.getEvaluatedBoolean(False, elementNode, 'addLayerTemplate')
self.elementNode = elementNode
self.fileName = evaluate.getEvaluatedString('', elementNode, 'file')
self.folderName = evaluate.getEvaluatedString('', elementNode, 'folder')
self.suffix = evaluate.getEvaluatedString('', elementNode, 'suffix')
self.targets = evaluate.getElementNodesByKey(elementNode, 'target')
self.writeMatrix = evaluate.getEvaluatedBoolean(True, elementNode, 'writeMatrix')

View File

@ -0,0 +1,9 @@
#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
import os
import sys
numberOfLevelsDeepInPackageHierarchy = 3
packageFilePath = os.path.abspath(__file__)
for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ):
packageFilePath = os.path.dirname( packageFilePath )
if packageFilePath not in sys.path:
sys.path.insert( 0, packageFilePath )

View File

@ -0,0 +1,71 @@
"""
Add material to support overhang or remove material at the overhang angle.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
globalExecutionOrder = 20
def getBevelPath( begin, center, close, end, radius ):
"Get bevel path."
beginComplex = begin.dropAxis()
centerComplex = center.dropAxis()
endComplex = end.dropAxis()
beginComplexSegmentLength = abs( centerComplex - beginComplex )
endComplexSegmentLength = abs( centerComplex - endComplex )
minimumRadius = lineation.getMinimumRadius( beginComplexSegmentLength, endComplexSegmentLength, radius )
if minimumRadius <= close:
return [ center ]
beginBevel = center + minimumRadius / beginComplexSegmentLength * ( begin - center )
endBevel = center + minimumRadius / endComplexSegmentLength * ( end - center )
if radius > 0.0:
return [ beginBevel, endBevel ]
midpointComplex = 0.5 * ( beginBevel.dropAxis() + endBevel.dropAxis() )
spikeComplex = centerComplex + centerComplex - midpointComplex
return [ beginBevel, Vector3( spikeComplex.real, spikeComplex.imag, center.z ), endBevel ]
def getManipulatedPaths(close, elementNode, loop, prefix, sideLength):
"Get bevel loop."
if len(loop) < 3:
return [loop]
derivation = BevelDerivation(elementNode, prefix, sideLength)
if derivation.radius == 0.0:
return loop
bevelLoop = []
for pointIndex in xrange(len(loop)):
begin = loop[(pointIndex + len(loop) - 1) % len(loop)]
center = loop[pointIndex]
end = loop[(pointIndex + 1) % len(loop)]
bevelLoop += getBevelPath(begin, center, close, end, derivation.radius)
return [euclidean.getLoopWithoutCloseSequentialPoints(close, bevelLoop)]
def getNewDerivation(elementNode, prefix, sideLength):
'Get new derivation.'
return BevelDerivation(elementNode, prefix, sideLength)
def processElementNode(elementNode):
"Process the xml element."
lineation.processElementNodeByFunction(elementNode, getManipulatedPaths)
class BevelDerivation:
"Class to hold bevel variables."
def __init__(self, elementNode, prefix, sideLength):
'Set defaults.'
self.radius = lineation.getFloatByPrefixSide(0.0, elementNode, prefix + 'radius', sideLength)

View File

@ -0,0 +1,37 @@
"""
Create outline.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities import euclidean
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
globalExecutionOrder = 80
def getManipulatedPaths(close, elementNode, loop, prefix, sideLength):
"Get path with overhangs removed or filled in."
if len(loop) < 4:
return [loop]
loopComplex = euclidean.getComplexPath(loop)
return euclidean.getVector3Paths([euclidean.getLoopConvex(loopComplex)], loop[0].z)
def getNewDerivation(elementNode, prefix, sideLength):
'Get new derivation.'
return evaluate.EmptyObject()
def processElementNode(elementNode):
"Process the xml element."
lineation.processElementNodeByFunction(elementNode, getManipulatedPaths)

View File

@ -0,0 +1,53 @@
"""
Create outline.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
from fabmetheus_utilities import intercircle
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
globalExecutionOrder = 80
def getManipulatedPaths(close, elementNode, loop, prefix, sideLength):
"Get path with outline."
if len(loop) < 2:
return [loop]
derivation = OutlineDerivation(elementNode, prefix, sideLength)
loopComplex = euclidean.getComplexPath(loop)
if derivation.isClosed:
loopComplexes = intercircle.getAroundsFromLoop(loopComplex, derivation.radius)
else:
loopComplexes = intercircle.getAroundsFromPath(loopComplex, derivation.radius)
return euclidean.getVector3Paths(loopComplexes, loop[0].z)
def getNewDerivation(elementNode, prefix, sideLength):
'Get new derivation.'
return OutlineDerivation(elementNode, prefix, sideLength)
def processElementNode(elementNode):
"Process the xml element."
lineation.processElementNodeByFunction(elementNode, getManipulatedPaths)
class OutlineDerivation:
"Class to hold outline variables."
def __init__(self, elementNode, prefix, sideLength):
'Set defaults.'
self.isClosed = evaluate.getEvaluatedBoolean(False, elementNode, prefix + 'closed')
self.radius = evaluate.getEvaluatedFloat(setting.getEdgeWidth(elementNode), elementNode, prefix + 'radius')

View File

@ -0,0 +1,397 @@
"""
Add material to support overhang or remove material at the overhang angle.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
globalExecutionOrder = 100
def addUnsupportedPointIndexes( alongAway ):
"Add the indexes of the unsupported points."
addedUnsupportedPointIndexes = []
for pointIndex in xrange( len( alongAway.loop ) ):
point = alongAway.loop[pointIndex]
if pointIndex not in alongAway.unsupportedPointIndexes:
if not alongAway.getIsClockwisePointSupported(point):
alongAway.unsupportedPointIndexes.append( pointIndex )
addedUnsupportedPointIndexes.append( pointIndex )
for pointIndex in addedUnsupportedPointIndexes:
point = alongAway.loop[pointIndex]
point.y += alongAway.maximumYPlus
def alterClockwiseSupportedPath( alongAway, elementNode ):
"Get clockwise path with overhangs carved out."
alongAway.bottomPoints = []
alongAway.overhangSpan = setting.getOverhangSpan(elementNode)
maximumY = - 987654321.0
minimumYPointIndex = 0
for pointIndex in xrange( len( alongAway.loop ) ):
point = alongAway.loop[pointIndex]
if point.y < alongAway.loop[ minimumYPointIndex ].y:
minimumYPointIndex = pointIndex
maximumY = max( maximumY, point.y )
alongAway.maximumYPlus = 2.0 * ( maximumY - alongAway.loop[ minimumYPointIndex ].y )
alongAway.loop = euclidean.getAroundLoop( minimumYPointIndex, minimumYPointIndex, alongAway.loop )
overhangClockwise = OverhangClockwise( alongAway )
alongAway.unsupportedPointIndexes = []
oldUnsupportedPointIndexesLength = - 987654321.0
while len( alongAway.unsupportedPointIndexes ) > oldUnsupportedPointIndexesLength:
oldUnsupportedPointIndexesLength = len( alongAway.unsupportedPointIndexes )
addUnsupportedPointIndexes( alongAway )
for pointIndex in alongAway.unsupportedPointIndexes:
point = alongAway.loop[pointIndex]
point.y -= alongAway.maximumYPlus
alongAway.unsupportedPointIndexes.sort()
alongAway.unsupportedPointIndexLists = []
oldUnsupportedPointIndex = - 987654321.0
unsupportedPointIndexList = None
for unsupportedPointIndex in alongAway.unsupportedPointIndexes:
if unsupportedPointIndex > oldUnsupportedPointIndex + 1:
unsupportedPointIndexList = []
alongAway.unsupportedPointIndexLists.append( unsupportedPointIndexList )
oldUnsupportedPointIndex = unsupportedPointIndex
unsupportedPointIndexList.append( unsupportedPointIndex )
alongAway.unsupportedPointIndexLists.reverse()
for unsupportedPointIndexList in alongAway.unsupportedPointIndexLists:
overhangClockwise.alterLoop( unsupportedPointIndexList )
def alterWiddershinsSupportedPath( alongAway, close ):
"Get widdershins path with overhangs filled in."
alongAway.bottomPoints = []
alongAway.minimumY = getMinimumYByPath( alongAway.loop )
for point in alongAway.loop:
if point.y - alongAway.minimumY < close:
alongAway.addToBottomPoints(point)
ascendingYPoints = alongAway.loop[:]
ascendingYPoints.sort( compareYAscending )
overhangWiddershinsLeft = OverhangWiddershinsLeft( alongAway )
overhangWiddershinsRight = OverhangWiddershinsRight( alongAway )
for point in ascendingYPoints:
alterWiddershinsSupportedPathByPoint( alongAway, overhangWiddershinsLeft, overhangWiddershinsRight, point )
def alterWiddershinsSupportedPathByPoint( alongAway, overhangWiddershinsLeft, overhangWiddershinsRight, point ):
"Get widdershins path with overhangs filled in for point."
if alongAway.getIsWiddershinsPointSupported(point):
return
overhangWiddershins = overhangWiddershinsLeft
if overhangWiddershinsRight.getDistance() < overhangWiddershinsLeft.getDistance():
overhangWiddershins = overhangWiddershinsRight
overhangWiddershins.alterLoop()
def compareYAscending( point, pointOther ):
"Get comparison in order to sort points in ascending y."
if point.y < pointOther.y:
return - 1
return int( point.y > pointOther.y )
def getManipulatedPaths(close, elementNode, loop, prefix, sideLength):
"Get path with overhangs removed or filled in."
if len(loop) < 3:
print('Warning, loop has less than three sides in getManipulatedPaths in overhang for:')
print(elementNode)
return [loop]
derivation = OverhangDerivation(elementNode, prefix)
overhangPlaneAngle = euclidean.getWiddershinsUnitPolar(0.5 * math.pi - derivation.overhangRadians)
if derivation.overhangInclinationRadians != 0.0:
overhangInclinationCosine = abs(math.cos(derivation.overhangInclinationRadians))
if overhangInclinationCosine == 0.0:
return [loop]
imaginaryTimesCosine = overhangPlaneAngle.imag * overhangInclinationCosine
overhangPlaneAngle = euclidean.getNormalized(complex(overhangPlaneAngle.real, imaginaryTimesCosine))
alongAway = AlongAway(loop, overhangPlaneAngle)
if euclidean.getIsWiddershinsByVector3(loop):
alterWiddershinsSupportedPath(alongAway, close)
else:
alterClockwiseSupportedPath(alongAway, elementNode)
return [euclidean.getLoopWithoutCloseSequentialPoints(close, alongAway.loop)]
def getMinimumYByPath(path):
"Get path with overhangs removed or filled in."
minimumYByPath = path[0].y
for point in path:
minimumYByPath = min( minimumYByPath, point.y )
return minimumYByPath
def getNewDerivation(elementNode, prefix, sideLength):
'Get new derivation.'
return OverhangDerivation(elementNode, prefix)
def processElementNode(elementNode):
"Process the xml element."
lineation.processElementNodeByFunction(elementNode, getManipulatedPaths)
class AlongAway:
"Class to derive the path along the point and away from the point."
def __init__( self, loop, overhangPlaneAngle ):
"Initialize."
self.loop = loop
self.overhangPlaneAngle = overhangPlaneAngle
self.ySupport = - self.overhangPlaneAngle.imag
def __repr__(self):
"Get the string representation of AlongAway."
return '%s' % ( self.overhangPlaneAngle )
def addToBottomPoints(self, point):
"Add point to bottom points and set y to minimumY."
self.bottomPoints.append(point)
point.y = self.minimumY
def getIsClockwisePointSupported(self, point):
"Determine if the point on the clockwise loop is supported."
self.point = point
self.pointIndex = None
self.awayIndexes = []
numberOfIntersectionsBelow = 0
for pointIndex in xrange( len( self.loop ) ):
begin = self.loop[pointIndex]
end = self.loop[ (pointIndex + 1) % len( self.loop ) ]
if begin != point and end != point:
self.awayIndexes.append( pointIndex )
yIntersection = euclidean.getYIntersectionIfExists( begin.dropAxis(), end.dropAxis(), point.x )
if yIntersection != None:
numberOfIntersectionsBelow += ( yIntersection < point.y )
if begin == point:
self.pointIndex = pointIndex
if numberOfIntersectionsBelow % 2 == 0:
return True
if self.pointIndex == None:
return True
if self.getIsPointSupportedBySegment( self.pointIndex - 1 + len( self.loop ) ):
return True
return self.getIsPointSupportedBySegment( self.pointIndex + 1 )
def getIsPointSupportedBySegment( self, endIndex ):
"Determine if the point on the widdershins loop is supported."
endComplex = self.loop[ ( endIndex % len( self.loop ) ) ].dropAxis()
endMinusPointComplex = euclidean.getNormalized( endComplex - self.point.dropAxis() )
return endMinusPointComplex.imag < self.ySupport
def getIsWiddershinsPointSupported(self, point):
"Determine if the point on the widdershins loop is supported."
if point.y <= self.minimumY:
return True
self.point = point
self.pointIndex = None
self.awayIndexes = []
numberOfIntersectionsBelow = 0
for pointIndex in xrange( len( self.loop ) ):
begin = self.loop[pointIndex]
end = self.loop[ (pointIndex + 1) % len( self.loop ) ]
if begin != point and end != point:
self.awayIndexes.append( pointIndex )
yIntersection = euclidean.getYIntersectionIfExists( begin.dropAxis(), end.dropAxis(), point.x )
if yIntersection != None:
numberOfIntersectionsBelow += ( yIntersection < point.y )
if begin == point:
self.pointIndex = pointIndex
if numberOfIntersectionsBelow % 2 == 1:
return True
if self.pointIndex == None:
return True
if self.getIsPointSupportedBySegment( self.pointIndex - 1 + len( self.loop ) ):
return True
return self.getIsPointSupportedBySegment( self.pointIndex + 1 )
class OverhangClockwise:
"Class to get the intersection up from the point."
def __init__( self, alongAway ):
"Initialize."
self.alongAway = alongAway
self.halfRiseOverWidth = 0.5 * alongAway.overhangPlaneAngle.imag / alongAway.overhangPlaneAngle.real
self.widthOverRise = alongAway.overhangPlaneAngle.real / alongAway.overhangPlaneAngle.imag
def __repr__(self):
"Get the string representation of OverhangClockwise."
return '%s' % ( self.intersectionPlaneAngle )
def alterLoop( self, unsupportedPointIndexes ):
"Alter alongAway loop."
unsupportedBeginIndex = unsupportedPointIndexes[0]
unsupportedEndIndex = unsupportedPointIndexes[-1]
beginIndex = unsupportedBeginIndex - 1
endIndex = unsupportedEndIndex + 1
begin = self.alongAway.loop[ beginIndex ]
end = self.alongAway.loop[ endIndex ]
truncatedOverhangSpan = self.alongAway.overhangSpan
width = end.x - begin.x
heightDifference = abs( end.y - begin.y )
remainingWidth = width - self.widthOverRise * heightDifference
if remainingWidth <= 0.0:
del self.alongAway.loop[ unsupportedBeginIndex : endIndex ]
return
highest = begin
supportX = begin.x + remainingWidth
if end.y > begin.y:
highest = end
supportX = end.x - remainingWidth
tipY = highest.y + self.halfRiseOverWidth * remainingWidth
highestBetween = - 987654321.0
for unsupportedPointIndex in unsupportedPointIndexes:
highestBetween = max( highestBetween, self.alongAway.loop[ unsupportedPointIndex ].y )
if highestBetween > highest.y:
truncatedOverhangSpan = 0.0
if highestBetween < tipY:
below = tipY - highestBetween
truncatedOverhangSpan = min( self.alongAway.overhangSpan, below / self.halfRiseOverWidth )
truncatedOverhangSpanRadius = 0.5 * truncatedOverhangSpan
if remainingWidth <= truncatedOverhangSpan:
supportPoint = Vector3( supportX, highest.y, highest.z )
self.alongAway.loop[ unsupportedBeginIndex : endIndex ] = [ supportPoint ]
return
midSupportX = 0.5 * ( supportX + highest.x )
if truncatedOverhangSpan <= 0.0:
supportPoint = Vector3( midSupportX, tipY, highest.z )
self.alongAway.loop[ unsupportedBeginIndex : endIndex ] = [ supportPoint ]
return
supportXLeft = midSupportX - truncatedOverhangSpanRadius
supportXRight = midSupportX + truncatedOverhangSpanRadius
supportY = tipY - self.halfRiseOverWidth * truncatedOverhangSpan
supportPoints = [ Vector3( supportXLeft, supportY, highest.z ), Vector3( supportXRight, supportY, highest.z ) ]
self.alongAway.loop[ unsupportedBeginIndex : endIndex ] = supportPoints
class OverhangDerivation:
"Class to hold overhang variables."
def __init__(self, elementNode, prefix):
'Set defaults.'
self.overhangRadians = setting.getOverhangRadians(elementNode)
self.overhangInclinationRadians = math.radians(evaluate.getEvaluatedFloat(0.0, elementNode, prefix + 'inclination'))
class OverhangWiddershinsLeft:
"Class to get the intersection from the point down to the left."
def __init__( self, alongAway ):
"Initialize."
self.alongAway = alongAway
self.intersectionPlaneAngle = - alongAway.overhangPlaneAngle
self.setRatios()
def __repr__(self):
"Get the string representation of OverhangWiddershins."
return '%s' % ( self.intersectionPlaneAngle )
def alterLoop(self):
"Alter alongAway loop."
insertedPoint = self.alongAway.point.copy()
if self.closestXIntersectionIndex != None:
self.alongAway.loop = self.getIntersectLoop()
intersectionRelativeComplex = self.closestXDistance * self.intersectionPlaneAngle
intersectionPoint = insertedPoint + Vector3( intersectionRelativeComplex.real, intersectionRelativeComplex.imag )
self.alongAway.loop.append( intersectionPoint )
return
if self.closestBottomPoint == None:
return
if self.closestBottomPoint not in self.alongAway.loop:
return
insertedPoint.x = self.bottomX
closestBottomIndex = self.alongAway.loop.index( self.closestBottomPoint )
self.alongAway.addToBottomPoints( insertedPoint )
self.alongAway.loop = self.getBottomLoop( closestBottomIndex, insertedPoint )
self.alongAway.loop.append( insertedPoint )
def getBottomLoop( self, closestBottomIndex, insertedPoint ):
"Get loop around bottom."
endIndex = closestBottomIndex + len( self.alongAway.loop ) + 1
return euclidean.getAroundLoop( self.alongAway.pointIndex, endIndex, self.alongAway.loop )
def getDistance(self):
"Get distance between point and closest intersection or bottom point along line."
self.pointMinusBottomY = self.alongAway.point.y - self.alongAway.minimumY
self.diagonalDistance = self.pointMinusBottomY * self.diagonalRatio
if self.alongAway.pointIndex == None:
return self.getDistanceToBottom()
rotatedLoop = euclidean.getRotatedComplexes( self.intersectionYMirror, euclidean.getComplexPath( self.alongAway.loop ) )
rotatedPointComplex = rotatedLoop[ self.alongAway.pointIndex ]
beginX = rotatedPointComplex.real
endX = beginX + self.diagonalDistance + self.diagonalDistance
xIntersectionIndexList = []
for pointIndex in self.alongAway.awayIndexes:
beginComplex = rotatedLoop[pointIndex]
endComplex = rotatedLoop[ (pointIndex + 1) % len( rotatedLoop ) ]
xIntersection = euclidean.getXIntersectionIfExists( beginComplex, endComplex, rotatedPointComplex.imag )
if xIntersection != None:
if xIntersection >= beginX and xIntersection < endX:
xIntersectionIndexList.append( euclidean.XIntersectionIndex( pointIndex, xIntersection ) )
self.closestXDistance = 987654321.0
self.closestXIntersectionIndex = None
for xIntersectionIndex in xIntersectionIndexList:
xDistance = abs( xIntersectionIndex.x - beginX )
if xDistance < self.closestXDistance:
self.closestXIntersectionIndex = xIntersectionIndex
self.closestXDistance = xDistance
if self.closestXIntersectionIndex != None:
return self.closestXDistance
return self.getDistanceToBottom()
def getDistanceToBottom(self):
"Get distance between point and closest bottom point along line."
self.bottomX = self.alongAway.point.x + self.pointMinusBottomY * self.xRatio
self.closestBottomPoint = None
closestDistanceX = 987654321.0
for point in self.alongAway.bottomPoints:
distanceX = abs( point.x - self.bottomX )
if self.getIsOnside(point.x):
if distanceX < closestDistanceX:
closestDistanceX = distanceX
self.closestBottomPoint = point
return closestDistanceX + self.diagonalDistance
def getIntersectLoop(self):
"Get intersection loop."
endIndex = self.closestXIntersectionIndex.index + len( self.alongAway.loop ) + 1
return euclidean.getAroundLoop( self.alongAway.pointIndex, endIndex, self.alongAway.loop )
def getIsOnside( self, x ):
"Determine if x is on the side along the direction of the intersection line."
return x <= self.alongAway.point.x
def setRatios(self):
"Set ratios."
self.diagonalRatio = 1.0 / abs( self.intersectionPlaneAngle.imag )
self.intersectionYMirror = complex( self.intersectionPlaneAngle.real, - self.intersectionPlaneAngle.imag )
self.xRatio = self.intersectionPlaneAngle.real / abs( self.intersectionPlaneAngle.imag )
class OverhangWiddershinsRight( OverhangWiddershinsLeft ):
"Class to get the intersection from the point down to the right."
def __init__( self, alongAway ):
"Initialize."
self.alongAway = alongAway
self.intersectionPlaneAngle = complex( alongAway.overhangPlaneAngle.real, - alongAway.overhangPlaneAngle.imag )
self.setRatios()
def getBottomLoop( self, closestBottomIndex, insertedPoint ):
"Get loop around bottom."
endIndex = self.alongAway.pointIndex + len( self.alongAway.loop ) + 1
return euclidean.getAroundLoop( closestBottomIndex, endIndex, self.alongAway.loop )
def getIntersectLoop(self):
"Get intersection loop."
beginIndex = self.closestXIntersectionIndex.index + len( self.alongAway.loop ) + 1
endIndex = self.alongAway.pointIndex + len( self.alongAway.loop ) + 1
return euclidean.getAroundLoop( beginIndex, endIndex, self.alongAway.loop )
def getIsOnside( self, x ):
"Determine if x is on the side along the direction of the intersection line."
return x >= self.alongAway.point.x

View File

@ -0,0 +1,94 @@
"""
Add material to support overhang or remove material at the overhang angle.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
globalExecutionOrder = 40
def getManipulatedPaths(close, elementNode, loop, prefix, sideLength):
"Get round loop."
if len(loop) < 3:
return [loop]
derivation = RoundDerivation(elementNode, prefix, sideLength)
if derivation.radius == 0.0:
return loop
roundLoop = []
sidesPerRadian = 0.5 / math.pi * evaluate.getSidesMinimumThreeBasedOnPrecision(elementNode, sideLength)
for pointIndex in xrange(len(loop)):
begin = loop[(pointIndex + len(loop) - 1) % len(loop)]
center = loop[pointIndex]
end = loop[(pointIndex + 1) % len(loop)]
roundLoop += getRoundPath(begin, center, close, end, derivation.radius, sidesPerRadian)
return [euclidean.getLoopWithoutCloseSequentialPoints(close, roundLoop)]
def getNewDerivation(elementNode, prefix, sideLength):
'Get new derivation.'
return RoundDerivation(elementNode, prefix, sideLength)
def getRoundPath( begin, center, close, end, radius, sidesPerRadian ):
"Get round path."
beginComplex = begin.dropAxis()
centerComplex = center.dropAxis()
endComplex = end.dropAxis()
beginComplexSegmentLength = abs( centerComplex - beginComplex )
endComplexSegmentLength = abs( centerComplex - endComplex )
minimumRadius = lineation.getMinimumRadius( beginComplexSegmentLength, endComplexSegmentLength, radius )
if minimumRadius <= close:
return [ center ]
beginBevel = center + minimumRadius / beginComplexSegmentLength * ( begin - center )
endBevel = center + minimumRadius / endComplexSegmentLength * ( end - center )
beginBevelComplex = beginBevel.dropAxis()
endBevelComplex = endBevel.dropAxis()
midpointComplex = 0.5 * ( beginBevelComplex + endBevelComplex )
if radius < 0.0:
centerComplex = midpointComplex + midpointComplex - centerComplex
midpointMinusCenterComplex = midpointComplex - centerComplex
midpointCenterLength = abs( midpointMinusCenterComplex )
midpointEndLength = abs( midpointComplex - endBevelComplex )
midpointCircleCenterLength = midpointEndLength * midpointEndLength / midpointCenterLength
circleRadius = math.sqrt( midpointCircleCenterLength * midpointCircleCenterLength + midpointEndLength * midpointEndLength )
circleCenterComplex = midpointComplex + midpointMinusCenterComplex * midpointCircleCenterLength / midpointCenterLength
circleCenter = Vector3( circleCenterComplex.real, circleCenterComplex.imag, center.z )
endMinusCircleCenterComplex = endBevelComplex - circleCenterComplex
beginMinusCircleCenter = beginBevel - circleCenter
beginMinusCircleCenterComplex = beginMinusCircleCenter.dropAxis()
angleDifference = euclidean.getAngleDifferenceByComplex( endMinusCircleCenterComplex, beginMinusCircleCenterComplex )
steps = int( math.ceil( abs( angleDifference ) * sidesPerRadian ) )
stepPlaneAngle = euclidean.getWiddershinsUnitPolar( angleDifference / float( steps ) )
deltaZStep = ( end.z - begin.z ) / float( steps )
roundPath = [ beginBevel ]
for step in xrange( 1, steps ):
beginMinusCircleCenterComplex = beginMinusCircleCenterComplex * stepPlaneAngle
arcPointComplex = circleCenterComplex + beginMinusCircleCenterComplex
arcPoint = Vector3( arcPointComplex.real, arcPointComplex.imag, begin.z + deltaZStep * step )
roundPath.append( arcPoint )
return roundPath + [ endBevel ]
def processElementNode(elementNode):
"Process the xml element."
lineation.processElementNodeByFunction(elementNode, getManipulatedPaths)
class RoundDerivation:
"Class to hold round variables."
def __init__(self, elementNode, prefix, sideLength):
'Set defaults.'
self.radius = lineation.getFloatByPrefixSide(0.0, elementNode, prefix + 'radius', sideLength)

View File

@ -0,0 +1,166 @@
"""
Add material to support overhang or remove material at the overhang angle.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
globalExecutionOrder = 60
def getManipulatedPaths(close, elementNode, loop, prefix, sideLength):
"Get segment loop."
if len(loop) < 3:
return [loop]
derivation = SegmentDerivation(elementNode, prefix)
if derivation.path == getSegmentPathDefault():
return [loop]
path = getXNormalizedVector3Path(derivation.path)
if euclidean.getIsWiddershinsByVector3(loop):
path = path[: : -1]
for point in path:
point.x = 1.0 - point.x
if derivation.center == None:
point.y = - point.y
segmentLoop = []
startEnd = StartEnd(elementNode, len(loop), prefix)
for pointIndex in xrange(len(loop)):
if pointIndex >= startEnd.start and pointIndex < startEnd.end:
segmentLoop += getSegmentPath(derivation.center, loop, path, pointIndex)
else:
segmentLoop.append(loop[pointIndex])
return [euclidean.getLoopWithoutCloseSequentialPoints( close, segmentLoop)]
def getNewDerivation(elementNode, prefix, sideLength):
'Get new derivation.'
return SegmentDerivation(elementNode, prefix)
def getRadialPath(begin, center, end, path):
"Get radial path."
beginComplex = begin.dropAxis()
endComplex = end.dropAxis()
centerComplex = center.dropAxis()
beginMinusCenterComplex = beginComplex - centerComplex
endMinusCenterComplex = endComplex - centerComplex
beginMinusCenterComplexRadius = abs( beginMinusCenterComplex )
endMinusCenterComplexRadius = abs( endMinusCenterComplex )
if beginMinusCenterComplexRadius == 0.0 or endMinusCenterComplexRadius == 0.0:
return [ begin ]
beginMinusCenterComplex /= beginMinusCenterComplexRadius
endMinusCenterComplex /= endMinusCenterComplexRadius
angleDifference = euclidean.getAngleDifferenceByComplex( endMinusCenterComplex, beginMinusCenterComplex )
radialPath = []
for point in path:
weightEnd = point.x
weightBegin = 1.0 - weightEnd
weightedRadius = beginMinusCenterComplexRadius * weightBegin + endMinusCenterComplexRadius * weightEnd * ( 1.0 + point.y )
radialComplex = weightedRadius * euclidean.getWiddershinsUnitPolar( angleDifference * point.x ) * beginMinusCenterComplex
polygonPoint = center + Vector3( radialComplex.real, radialComplex.imag, point.z )
radialPath.append( polygonPoint )
return radialPath
def getSegmentPath(center, loop, path, pointIndex):
"Get segment path."
centerBegin = loop[pointIndex]
centerEnd = loop[(pointIndex + 1) % len(loop)]
centerEndMinusBegin = centerEnd - centerBegin
if abs( centerEndMinusBegin ) <= 0.0:
return [ centerBegin ]
if center != None:
return getRadialPath(centerBegin, center, centerEnd, path)
begin = loop[(pointIndex + len(loop) - 1) % len(loop)]
end = loop[(pointIndex + 2) % len(loop)]
return getWedgePath(begin, centerBegin, centerEnd, centerEndMinusBegin, end, path)
def getSegmentPathDefault():
"Get segment path default."
return [Vector3(), Vector3(0.0, 1.0)]
def getWedgePath( begin, centerBegin, centerEnd, centerEndMinusBegin, end, path ):
"Get segment path."
beginComplex = begin.dropAxis()
centerBeginComplex = centerBegin.dropAxis()
centerEndComplex = centerEnd.dropAxis()
endComplex = end.dropAxis()
wedgePath = []
centerBeginMinusBeginComplex = euclidean.getNormalized( centerBeginComplex - beginComplex )
centerEndMinusCenterBeginComplexOriginal = centerEndComplex - centerBeginComplex
centerEndMinusCenterBeginComplexLength = abs( centerEndMinusCenterBeginComplexOriginal )
if centerEndMinusCenterBeginComplexLength <= 0.0:
return [ centerBegin ]
centerEndMinusCenterBeginComplex = centerEndMinusCenterBeginComplexOriginal / centerEndMinusCenterBeginComplexLength
endMinusCenterEndComplex = euclidean.getNormalized( endComplex - centerEndComplex )
widdershinsBegin = getWiddershinsAverageByVector3( centerBeginMinusBeginComplex, centerEndMinusCenterBeginComplex )
widdershinsEnd = getWiddershinsAverageByVector3( centerEndMinusCenterBeginComplex, endMinusCenterEndComplex )
for point in path:
weightEnd = point.x
weightBegin = 1.0 - weightEnd
polygonPoint = centerBegin + centerEndMinusBegin * point.x
weightedWiddershins = widdershinsBegin * weightBegin + widdershinsEnd * weightEnd
polygonPoint += weightedWiddershins * point.y * centerEndMinusCenterBeginComplexLength
polygonPoint.z += point.z
wedgePath.append( polygonPoint )
return wedgePath
def getWiddershinsAverageByVector3( centerMinusBeginComplex, endMinusCenterComplex ):
"Get the normalized average of the widdershins vectors."
centerMinusBeginWiddershins = Vector3( - centerMinusBeginComplex.imag, centerMinusBeginComplex.real )
endMinusCenterWiddershins = Vector3( - endMinusCenterComplex.imag, endMinusCenterComplex.real )
return ( centerMinusBeginWiddershins + endMinusCenterWiddershins ).getNormalized()
def getXNormalizedVector3Path(path):
"Get path where the x ranges from 0 to 1."
if len(path) < 1:
return path
minimumX = path[0].x
for point in path[1 :]:
minimumX = min( minimumX, point.x )
for point in path:
point.x -= minimumX
maximumX = path[0].x
for point in path[1 :]:
maximumX = max( maximumX, point.x )
for point in path:
point.x /= maximumX
return path
def processElementNode(elementNode):
"Process the xml element."
lineation.processElementNodeByFunction(elementNode, getManipulatedPaths)
class SegmentDerivation:
"Class to hold segment variables."
def __init__(self, elementNode, prefix):
'Set defaults.'
self.center = evaluate.getVector3ByPrefix(None, elementNode, prefix + 'center')
self.path = evaluate.getPathByPrefix(elementNode, getSegmentPathDefault(), prefix)
class StartEnd:
'Class to get a start through end range.'
def __init__(self, elementNode, modulo, prefix):
"Initialize."
self.start = evaluate.getEvaluatedInt(0, elementNode, prefix + 'start')
self.extent = evaluate.getEvaluatedInt(modulo - self.start, elementNode, prefix + 'extent')
self.end = evaluate.getEvaluatedInt(self.start + self.extent, elementNode, prefix + 'end')
self.revolutions = evaluate.getEvaluatedInt(1, elementNode, prefix + 'revolutions')
if self.revolutions > 1:
self.end += modulo * (self.revolutions - 1)
def __repr__(self):
"Get the string representation of this StartEnd."
return '%s, %s, %s' % (self.start, self.end, self.revolutions)

View File

@ -0,0 +1,43 @@
"""
Add material to support overhang or remove material at the overhang angle.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.vector3 import Vector3
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
globalExecutionOrder = -200
def getManipulatedPaths(close, elementNode, loop, prefix, sideLength):
"Get wedge loop."
derivation = WedgeDerivation(elementNode, prefix)
loop.append(derivation.center)
return [loop]
def getNewDerivation(elementNode, prefix, sideLength):
'Get new derivation.'
return WedgeDerivation(elementNode, prefix)
def processElementNode(elementNode):
"Process the xml element."
lineation.processElementNodeByFunction(elementNode, getManipulatedPaths)
class WedgeDerivation:
"Class to hold wedge variables."
def __init__(self, elementNode, prefix):
'Set defaults.'
self.center = evaluate.getVector3ByPrefix(Vector3(), elementNode, prefix + 'center')

View File

@ -0,0 +1,9 @@
#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
import os
import sys
numberOfLevelsDeepInPackageHierarchy = 3
packageFilePath = os.path.abspath(__file__)
for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ):
packageFilePath = os.path.dirname( packageFilePath )
if packageFilePath not in sys.path:
sys.path.insert( 0, packageFilePath )

View File

@ -0,0 +1,104 @@
"""
Boolean geometry bottom.
"""
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 fabmetheus_utilities.geometry.creation import solid
from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting
from fabmetheus_utilities.geometry.geometry_utilities import boolean_geometry
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities import euclidean
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
globalExecutionOrder = 400
def bottomElementNode(derivation, target):
"Bottom target."
xmlObject = target.xmlObject
if xmlObject == None:
print('Warning, bottomTarget in bottom could not get xmlObject for:')
print(target)
print(derivation.elementNode)
return
targetMatrix = matrix.getBranchMatrixSetElementNode(target)
lift = derivation.altitude
transformedPaths = xmlObject.getTransformedPaths()
if len(transformedPaths) > 0:
lift += derivation.getAdditionalPathLift() - euclidean.getBottomByPaths(transformedPaths)
else:
lift -= boolean_geometry.getMinimumZ(xmlObject)
targetMatrix.tetragrid = matrix.getIdentityTetragrid(targetMatrix.tetragrid)
targetMatrix.tetragrid[2][3] += lift
matrix.setElementNodeDictionaryMatrix(target, targetMatrix)
def getManipulatedGeometryOutput(elementNode, geometryOutput, prefix):
'Get bottomed geometryOutput.'
derivation = BottomDerivation(elementNode, prefix)
copyShallow = elementNode.getCopyShallow()
solid.processElementNodeByGeometry(copyShallow, geometryOutput)
targetMatrix = matrix.getBranchMatrixSetElementNode(elementNode)
matrix.setElementNodeDictionaryMatrix(copyShallow, targetMatrix)
minimumZ = boolean_geometry.getMinimumZ(copyShallow.xmlObject)
copyShallow.parentNode.xmlObject.archivableObjects.remove(copyShallow.xmlObject)
lift = derivation.altitude - minimumZ
vertexes = matrix.getVertexes(geometryOutput)
for vertex in vertexes:
vertex.z += lift
return geometryOutput
def getManipulatedPaths(close, elementNode, loop, prefix, sideLength):
'Get flipped paths.'
if len(loop) < 1:
return [[]]
derivation = BottomDerivation(elementNode, prefix)
targetMatrix = matrix.getBranchMatrixSetElementNode(elementNode)
transformedLoop = matrix.getTransformedVector3s(matrix.getIdentityTetragrid(targetMatrix.tetragrid), loop)
lift = derivation.altitude + derivation.getAdditionalPathLift() - euclidean.getBottomByPath(transformedLoop)
for point in loop:
point.z += lift
return [loop]
def getNewDerivation(elementNode, prefix, sideLength):
'Get new derivation.'
return BottomDerivation(elementNode, '')
def processElementNode(elementNode):
"Process the xml element."
processElementNodeByDerivation(None, elementNode)
def processElementNodeByDerivation(derivation, elementNode):
'Process the xml element by derivation.'
if derivation == None:
derivation = BottomDerivation(elementNode, '')
targets = evaluate.getElementNodesByKey(elementNode, 'target')
if len(targets) < 1:
print('Warning, processElementNode in bottom could not get targets for:')
print(elementNode)
return
for target in targets:
bottomElementNode(derivation, target)
class BottomDerivation:
"Class to hold bottom variables."
def __init__(self, elementNode, prefix):
'Set defaults.'
self.altitude = evaluate.getEvaluatedFloat(0.0, elementNode, prefix + 'altitude')
self.elementNode = elementNode
self.liftPath = evaluate.getEvaluatedBoolean(True, elementNode, prefix + 'liftPath')
def getAdditionalPathLift(self):
"Get path lift."
return 0.5 * setting.getLayerHeight(self.elementNode) * float(self.liftPath)

View File

@ -0,0 +1,85 @@
"""
Create inset.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.creation import solid
from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting
from fabmetheus_utilities.geometry.geometry_utilities import boolean_solid
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities.geometry.solids import triangle_mesh
from fabmetheus_utilities.vector3index import Vector3Index
from fabmetheus_utilities import euclidean
from fabmetheus_utilities import intercircle
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
globalExecutionOrder = 80
def getManipulatedGeometryOutput(elementNode, geometryOutput, prefix):
'Get inset geometryOutput.'
derivation = InsetDerivation(elementNode, prefix)
if derivation.radius == 0.0:
return geometryOutput
halfLayerHeight = 0.5 * derivation.radius
importRadius = 0.5 * derivation.radius * setting.getImportCoarseness(elementNode)
loopLayers = solid.getLoopLayersSetCopy(elementNode, geometryOutput, importRadius, derivation.radius)
triangleAltitude = math.sqrt(0.75) * derivation.radius
loops = []
vertexes = []
for loopLayerIndex in xrange(1, len(loopLayers), 2):
loopLayer = loopLayers[loopLayerIndex]
loopLayer.loops[0] = intercircle.getLargestInsetLoopFromLoop(loopLayer.loops[0], triangleAltitude)
for loopLayerIndex in xrange(0, len(loopLayers), 2):
loopLayer = loopLayers[loopLayerIndex]
loopLists = [[solid.getLoopOrEmpty(loopLayerIndex - 2, loopLayers)]]
loopLists.append([solid.getLoopOrEmpty(loopLayerIndex - 1, loopLayers)])
loopLists.append([intercircle.getLargestInsetLoopFromLoop(loopLayer.loops[0], derivation.radius)])
if evaluate.getEvaluatedBoolean(True, elementNode, prefix + 'insetTop'):
loopLists.append([solid.getLoopOrEmpty(loopLayerIndex + 1, loopLayers)])
loopLists.append([solid.getLoopOrEmpty(loopLayerIndex + 2, loopLayers)])
largestLoop = euclidean.getLargestLoop(boolean_solid.getLoopsIntersection(importRadius, loopLists))
triangle_mesh.addVector3Loop(largestLoop, loops, vertexes, loopLayer.z)
if evaluate.getEvaluatedBoolean(False, elementNode, prefix + 'addExtraTopLayer') and len(loops) > 0:
topLoop = loops[-1]
vector3Loop = []
loops.append(vector3Loop)
z = topLoop[0].z + derivation.radius
for point in topLoop:
vector3Index = Vector3Index(len(vertexes), point.x, point.y, z)
vector3Loop.append(vector3Index)
vertexes.append(vector3Index)
return triangle_mesh.getMeldedPillarOutput(loops)
def getManipulatedPaths(close, elementNode, loop, prefix, sideLength):
"Get inset path."
derivation = InsetDerivation(elementNode, prefix)
return intercircle.getInsetLoopsFromVector3Loop(loop, derivation.radius)
def getNewDerivation(elementNode, prefix, sideLength):
'Get new derivation.'
return OutsetDerivation(elementNode, prefix)
def processElementNode(elementNode):
'Process the xml element.'
solid.processElementNodeByFunctionPair(elementNode, getManipulatedGeometryOutput, getManipulatedPaths)
class InsetDerivation:
"Class to hold inset variables."
def __init__(self, elementNode, prefix):
'Set defaults.'
self.radius = evaluate.getEvaluatedFloat(2.0 * setting.getEdgeWidth(elementNode), elementNode, prefix + 'radius')

View File

@ -0,0 +1,78 @@
"""
Create inset.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.creation import solid
from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting
from fabmetheus_utilities.geometry.geometry_utilities import boolean_solid
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities.geometry.solids import triangle_mesh
from fabmetheus_utilities.vector3index import Vector3Index
from fabmetheus_utilities import euclidean
from fabmetheus_utilities import intercircle
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
globalExecutionOrder = 80
def getManipulatedGeometryOutput(elementNode, geometryOutput, prefix):
'Get outset geometryOutput.'
derivation = OutsetDerivation(elementNode, prefix)
if derivation.radius == 0.0:
return geometryOutput
halfLayerHeight = 0.5 * derivation.radius
importRadius = 0.5 * derivation.radius * setting.getImportCoarseness(elementNode)
loopLayers = solid.getLoopLayersSetCopy(elementNode, geometryOutput, importRadius, derivation.radius)
if len(loopLayers) == 0:
return triangle_mesh.getMeldedPillarOutput([])
triangleAltitude = math.sqrt(0.75) * derivation.radius
loops = []
vertexes = []
for loopLayerIndex in xrange(1, len(loopLayers), 2):
loopLayer = loopLayers[loopLayerIndex]
loopLayer.loops[0] = intercircle.getLargestInsetLoopFromLoop(loopLayer.loops[0], triangleAltitude)
z = loopLayers[0].z - derivation.radius
for loopIndex in xrange(-2, len(loopLayers) + 2, 2):
loopLists = [[solid.getLoopOrEmpty(loopIndex - 2, loopLayers)]]
loopLists.append([solid.getLoopOrEmpty(loopIndex - 1, loopLayers)])
loopLists.append([intercircle.getLargestInsetLoopFromLoop(solid.getLoopOrEmpty(loopIndex, loopLayers), -derivation.radius)])
loopLists.append([solid.getLoopOrEmpty(loopIndex + 1, loopLayers)])
loopLists.append([solid.getLoopOrEmpty(loopIndex + 2, loopLayers)])
largestLoop = euclidean.getLargestLoop(boolean_solid.getLoopsUnion(importRadius, loopLists))
triangle_mesh.addVector3Loop(largestLoop, loops, vertexes, z)
z += derivation.radius
return triangle_mesh.getMeldedPillarOutput(loops)
def getManipulatedPaths(close, elementNode, loop, prefix, sideLength):
"Get outset path."
derivation = OutsetDerivation(elementNode, prefix)
return intercircle.getInsetLoopsFromVector3Loop(loop, -derivation.radius)
def getNewDerivation(elementNode, prefix, sideLength):
'Get new derivation.'
return OutsetDerivation(elementNode, prefix)
def processElementNode(elementNode):
'Process the xml element.'
solid.processElementNodeByFunctionPair(elementNode, getManipulatedGeometryOutput, getManipulatedPaths)
class OutsetDerivation:
"Class to hold outset variables."
def __init__(self, elementNode, prefix):
'Set defaults.'
self.radius = evaluate.getEvaluatedFloat(2.0 * setting.getEdgeWidth(elementNode), elementNode, prefix + 'radius')

View File

@ -0,0 +1,116 @@
"""
Equation for vertexes.
"""
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 fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
globalExecutionOrder = -100
def equate(point, returnValue):
"Get equation for rectangular."
point.setToVector3(evaluate.getVector3ByDictionaryListValue(returnValue, point))
def equatePoints(elementNode, points, prefix, revolutions):
"Equate the points."
derivation = EquationDerivation(elementNode, prefix)
for equationResult in derivation.equationResults:
for point in points:
returnValue = equationResult.getReturnValue(point, revolutions)
if returnValue == None:
print('Warning, returnValue in alterVertexesByEquation in equation is None for:')
print(point)
print(elementNode)
else:
equationResult.equationFunction(point, returnValue)
def equateX(point, returnValue):
"Get equation for rectangular x."
point.x = returnValue
def equateY(point, returnValue):
"Get equation for rectangular y."
point.y = returnValue
def equateZ(point, returnValue):
"Get equation for rectangular z."
point.z = returnValue
def getManipulatedGeometryOutput(elementNode, geometryOutput, prefix):
"Get equated geometryOutput."
equatePoints(elementNode, matrix.getVertexes(geometryOutput), prefix, None)
return geometryOutput
def getManipulatedPaths(close, elementNode, loop, prefix, sideLength):
"Get equated paths."
equatePoints(elementNode, loop, prefix, 0.0)
return [loop]
def getNewDerivation(elementNode, prefix, sideLength):
'Get new derivation.'
return EquationDerivation(elementNode, prefix)
class EquationDerivation:
"Class to hold equation variables."
def __init__(self, elementNode, prefix):
'Set defaults.'
self.equationResults = []
self.addEquationResult(elementNode, equate, prefix)
self.addEquationResult(elementNode, equateX, prefix)
self.addEquationResult(elementNode, equateY, prefix)
self.addEquationResult(elementNode, equateZ, prefix)
def addEquationResult(self, elementNode, equationFunction, prefix):
'Add equation result to equationResults.'
prefixedEquationName = prefix + equationFunction.__name__[ len('equate') : ].replace('Dot', '.').lower()
if prefixedEquationName in elementNode.attributes:
self.equationResults.append(EquationResult(elementNode, equationFunction, prefixedEquationName))
class EquationResult:
"Class to get equation results."
def __init__(self, elementNode, equationFunction, key):
"Initialize."
self.distance = 0.0
elementNode.xmlObject = evaluate.getEvaluatorSplitWords(elementNode.attributes[key])
self.equationFunction = equationFunction
self.function = evaluate.Function(elementNode)
self.points = []
def getReturnValue(self, point, revolutions):
"Get return value."
if self.function == None:
return point
self.function.localDictionary['azimuth'] = math.degrees(math.atan2(point.y, point.x))
if len(self.points) > 0:
self.distance += abs(point - self.points[-1])
self.function.localDictionary['distance'] = self.distance
self.function.localDictionary['radius'] = abs(point.dropAxis())
if revolutions != None:
if len( self.points ) > 0:
revolutions += 0.5 / math.pi * euclidean.getAngleAroundZAxisDifference(point, self.points[-1])
self.function.localDictionary['revolutions'] = revolutions
self.function.localDictionary['vertex'] = point
self.function.localDictionary['vertexes'] = self.points
self.function.localDictionary['vertexindex'] = len(self.points)
self.function.localDictionary['x'] = point.x
self.function.localDictionary['y'] = point.y
self.function.localDictionary['z'] = point.z
self.points.append(point)
return self.function.getReturnValueWithoutDeletion()

View File

@ -0,0 +1,92 @@
"""
Add material to support overhang or remove material at the overhang angle.
"""
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 fabmetheus_utilities.geometry.creation import solid
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities.vector3 import Vector3
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
globalExecutionOrder = 200
# http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=269576
# http://www.opengl.org/resources/code/samples/sig99/advanced99/notes/node159.html
# m.a00 = -2 * norm.x * norm.x + 1;
# m.a10 = -2 * norm.y * norm.x;
# m.a20 = -2 * norm.z * norm.x;
# m.a30 = 0;
# m.a01 = -2 * norm.x * norm.y;
# m.a11 = -2 * norm.y * norm.y + 1;
# m.a21 = -2 * norm.z * norm.y;
# m.a31 = 0;
# m.a02 = -2 * norm.x * norm.z;
# m.a12 = -2 * norm.y * norm.z;
# m.a22 = -2 * norm.z * norm.z + 1;
# m.a32 = 0;
# m.a03 = -2 * norm.x * d;
# m.a13 = -2 * norm.y * d;
# m.a23 = -2 * norm.z * d;
# m.a33 = 1;
# normal = unit_vector(normal[:3])
# M = numpy.identity(4)
# M[:3, :3] -= 2.0 * numpy.outer(normal, normal)
# M[:3, 3] = (2.0 * numpy.dot(point[:3], normal)) * normal
# return M
def flipPoints(elementNode, points, prefix):
'Flip the points.'
derivation = FlipDerivation(elementNode, prefix)
for point in points:
point.setToVector3(point - 2.0 * derivation.axis.dot(point - derivation.origin) * derivation.axis)
def getFlippedLoop(elementNode, loop, prefix):
'Get flipped loop.'
flipPoints(elementNode, loop, prefix)
if getShouldReverse(elementNode, prefix):
loop.reverse()
return loop
def getManipulatedGeometryOutput(elementNode, geometryOutput, prefix):
'Get equated geometryOutput.'
flipPoints(elementNode, matrix.getVertexes(geometryOutput), prefix)
return geometryOutput
def getManipulatedPaths(close, elementNode, loop, prefix, sideLength):
'Get flipped paths.'
return [getFlippedLoop(elementNode, loop, prefix)]
def getNewDerivation(elementNode, prefix, sideLength):
'Get new derivation.'
return FlipDerivation(elementNode, prefix)
def getShouldReverse(elementNode, prefix):
'Determine if the loop should be reversed.'
return evaluate.getEvaluatedBoolean(True, elementNode, prefix + 'reverse')
def processElementNode(elementNode):
'Process the xml element.'
solid.processElementNodeByFunctionPair(elementNode, getManipulatedGeometryOutput, getManipulatedPaths)
class FlipDerivation:
"Class to hold flip variables."
def __init__(self, elementNode, prefix):
'Set defaults.'
self.origin = evaluate.getVector3ByPrefix(Vector3(), elementNode, prefix + 'origin')
self.axis = evaluate.getVector3ByPrefix(Vector3(1.0, 0.0, 0.0), elementNode, prefix + 'axis').getNormalized()

View File

@ -0,0 +1,49 @@
"""
Add material to support overhang or remove material at the overhang angle.
"""
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 fabmetheus_utilities.geometry.creation import solid
from fabmetheus_utilities.geometry.geometry_tools import face
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities.geometry.manipulation_shapes import flip
from fabmetheus_utilities.geometry.solids import triangle_mesh
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Art of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/02/05 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
globalExecutionOrder = 200
def getManipulatedGeometryOutput(elementNode, geometryOutput, prefix):
'Get equated geometryOutput.'
flippedGeometryOutput = triangle_mesh.getGeometryOutputCopy(geometryOutput)
flip.flipPoints(elementNode, matrix.getVertexes(flippedGeometryOutput), prefix)
if flip.getShouldReverse(elementNode, prefix):
flippedFaces = face.getFaces(flippedGeometryOutput)
for flippedFace in flippedFaces:
flippedFace.vertexIndexes.reverse()
return {'union' : {'shapes' : [flippedGeometryOutput, geometryOutput]}}
def getManipulatedPaths(close, elementNode, loop, prefix, sideLength):
'Get flipped paths.'
return [loop + flip.getFlippedLoop(elementNode, euclidean.getPathCopy(loop), prefix)]
def getNewDerivation(elementNode, prefix, sideLength):
'Get new derivation.'
return evaluate.EmptyObject()
def processElementNode(elementNode):
'Process the xml element.'
solid.processElementNodeByFunctionPair(elementNode, getManipulatedGeometryOutput, getManipulatedPaths)

View File

@ -0,0 +1,12 @@
"""
This page is in the table of contents.
This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
"""
import os
import sys
numberOfLevelsDeepInPackageHierarchy = 3
packageFilePath = os.path.abspath(__file__)
for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ):
packageFilePath = os.path.dirname( packageFilePath )
if packageFilePath not in sys.path:
sys.path.insert( 0, packageFilePath )

View File

@ -0,0 +1,78 @@
"""
Boolean geometry cube.
"""
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 fabmetheus_utilities.geometry.creation import solid
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.solids import triangle_mesh
from fabmetheus_utilities.vector3 import Vector3
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Nophead <http://hydraraptor.blogspot.com/>\nArt of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/21/04 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def addCube(elementNode, faces, inradius, vertexes):
'Add cube by inradius.'
square = [
complex(-inradius.x, -inradius.y),
complex(inradius.x, -inradius.y),
complex(inradius.x, inradius.y),
complex(-inradius.x, inradius.y)]
bottomTopSquare = triangle_mesh.getAddIndexedLoops(square, vertexes, [-inradius.z, inradius.z])
triangle_mesh.addPillarByLoops(faces, bottomTopSquare)
def getGeometryOutput(elementNode, inradius):
'Get cube triangle mesh by inradius.'
faces = []
vertexes = []
addCube(elementNode, faces, inradius, vertexes)
return {'trianglemesh' : {'vertex' : vertexes, 'face' : faces}}
def getNewDerivation(elementNode):
'Get new derivation.'
return CubeDerivation(elementNode)
def processElementNode(elementNode):
'Process the xml element.'
evaluate.processArchivable(Cube, elementNode)
class Cube(triangle_mesh.TriangleMesh):
'A cube object.'
def addXMLSection(self, depth, output):
'Add the xml section for this object.'
pass
def createShape(self):
'Create the shape.'
addCube(self.elementNode, self.faces, self.inradius, self.vertexes)
def setToElementNode(self, elementNode):
'Set to elementNode.'
attributes = elementNode.attributes
self.elementNode = elementNode
self.inradius = CubeDerivation(elementNode).inradius
attributes['inradius.x'] = self.inradius.x
attributes['inradius.y'] = self.inradius.y
attributes['inradius.z'] = self.inradius.z
if 'inradius' in attributes:
del attributes['inradius']
self.createShape()
solid.processArchiveRemoveSolid(elementNode, self.getGeometryOutput())
class CubeDerivation:
"Class to hold cube variables."
def __init__(self, elementNode):
'Set defaults.'
self.inradius = evaluate.getVector3ByPrefixes(elementNode, ['demisize', 'inradius'], Vector3(1.0, 1.0, 1.0))
self.inradius = evaluate.getVector3ByMultiplierPrefix(elementNode, 2.0, 'size', self.inradius)

View File

@ -0,0 +1,111 @@
"""
Boolean geometry cylinder.
"""
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 fabmetheus_utilities.geometry.creation import lineation
from fabmetheus_utilities.geometry.creation import solid
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
from fabmetheus_utilities.geometry.geometry_utilities import matrix
from fabmetheus_utilities.geometry.solids import cube
from fabmetheus_utilities.geometry.solids import triangle_mesh
from fabmetheus_utilities.vector3 import Vector3
from fabmetheus_utilities import euclidean
import math
__author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
__credits__ = 'Nophead <http://hydraraptor.blogspot.com/>\nArt of Illusion <http://www.artofillusion.org/>'
__date__ = '$Date: 2008/21/04 $'
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
def addCylinder(faces, inradius, sides, topOverBottom, vertexes):
'Add cylinder by inradius.'
polygonBottom = euclidean.getComplexPolygonByComplexRadius(complex(inradius.x, inradius.y), sides)
polygonTop = polygonBottom
if topOverBottom <= 0.0:
polygonTop = [complex()]
elif topOverBottom != 1.0:
polygonTop = euclidean.getComplexPathByMultiplier(topOverBottom, polygonTop)
bottomTopPolygon = [
triangle_mesh.getAddIndexedLoop(polygonBottom, vertexes, -inradius.z),
triangle_mesh.getAddIndexedLoop(polygonTop, vertexes, inradius.z)]
triangle_mesh.addPillarByLoops(faces, bottomTopPolygon)
def addCylinderOutputByEndStart(endZ, inradiusComplex, outputs, sides, start, topOverBottom=1.0):
'Add cylinder triangle mesh by endZ, inradius and start.'
inradius = Vector3(inradiusComplex.real, inradiusComplex.imag, 0.5 * abs(endZ - start.z))
cylinderOutput = getGeometryOutput(inradius, sides, topOverBottom)
vertexes = matrix.getVertexes(cylinderOutput)
if endZ < start.z:
for vertex in vertexes:
vertex.z = -vertex.z
translation = Vector3(start.x, start.y, inradius.z + min(start.z, endZ))
euclidean.translateVector3Path(vertexes, translation)
outputs.append(cylinderOutput)
def getGeometryOutput(inradius, sides, topOverBottom):
'Get cylinder triangle mesh by inradius.'
faces = []
vertexes = []
addCylinder(faces, inradius, sides, topOverBottom, vertexes)
return {'trianglemesh' : {'vertex' : vertexes, 'face' : faces}}
def getNewDerivation(elementNode):
'Get new derivation.'
return CylinderDerivation(elementNode)
def getTopOverBottom(angle, endZ, inradiusComplex, startZ):
'Get topOverBottom by angle in radians, endZ, inradius and start.'
return max(1.0 - abs(endZ - startZ) * math.tan(angle) / lineation.getRadiusAverage(inradiusComplex), 0.0)
def processElementNode(elementNode):
'Process the xml element.'
evaluate.processArchivable(Cylinder, elementNode)
class Cylinder( cube.Cube ):
'A cylinder object.'
def __init__(self):
'Add empty lists.'
cube.Cube.__init__(self)
def createShape(self):
'Create the shape.'
sides = evaluate.getSidesMinimumThreeBasedOnPrecision(self.elementNode, max(self.inradius.x, self.inradius.y))
if self.elementNode.getCascadeBoolean(False, 'radiusAreal'):
radiusArealizedMultiplier = euclidean.getRadiusArealizedMultiplier(sides)
self.inradius.x *= radiusArealizedMultiplier
self.inradius.y *= radiusArealizedMultiplier
addCylinder(self.faces, self.inradius, sides, self.topOverBottom, self.vertexes)
def setToElementNode(self, elementNode):
'Set to elementNode.'
attributes = elementNode.attributes
self.elementNode = elementNode
derivation = CylinderDerivation(elementNode)
self.inradius = derivation.inradius
self.topOverBottom = derivation.topOverBottom
if 'inradius' in attributes:
del attributes['inradius']
attributes['height'] = self.inradius.z + self.inradius.z
attributes['radius.x'] = self.inradius.x
attributes['radius.y'] = self.inradius.y
attributes['topOverBottom'] = self.topOverBottom
self.createShape()
solid.processArchiveRemoveSolid(elementNode, self.getGeometryOutput())
class CylinderDerivation:
"Class to hold cylinder variables."
def __init__(self, elementNode):
'Set defaults.'
self.inradius = evaluate.getVector3ByPrefixes(elementNode, ['demisize', 'inradius', 'radius'], Vector3(1.0, 1.0, 1.0))
self.inradius = evaluate.getVector3ByMultiplierPrefixes(elementNode, 2.0, ['diameter', 'size'], self.inradius)
self.inradius.z = 0.5 * evaluate.getEvaluatedFloat(self.inradius.z + self.inradius.z, elementNode, 'height')
self.topOverBottom = evaluate.getEvaluatedFloat(1.0, elementNode, 'topOverBottom')

Some files were not shown because too many files have changed in this diff Show More