Major reorganization to only include the communication stack from Cura.
parent
f12cc26fbb
commit
a0cac835b5
89
Cura/cura.py
89
Cura/cura.py
|
@ -1,89 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
"""
|
||||
This page is in the table of contents.
|
||||
==Overview==
|
||||
===Introduction===
|
||||
Cura is a GPL tool chain to forge a gcode skein for a model. Based on Skeinforge.
|
||||
|
||||
The slicing code is the same as Skeinforge. But the UI has been revamped to be... sane.
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
from optparse import OptionParser
|
||||
|
||||
from Cura.util import profile
|
||||
|
||||
__author__ = 'Daid'
|
||||
__credits__ = """
|
||||
David Braam (daid303@gmail.com)
|
||||
Enrique Perez (perez_enrique@yahoo.com)
|
||||
Adrian Bowyer <http://forums.reprap.org/profile.php?12,13>
|
||||
Brendan Erwin <http://forums.reprap.org/profile.php?12,217>
|
||||
Greenarrow <http://forums.reprap.org/profile.php?12,81>
|
||||
Ian England <http://forums.reprap.org/profile.php?12,192>
|
||||
John Gilmore <http://forums.reprap.org/profile.php?12,364>
|
||||
Jonwise <http://forums.reprap.org/profile.php?12,716>
|
||||
Kyle Corbitt <http://forums.reprap.org/profile.php?12,90>
|
||||
Michael Duffin <http://forums.reprap.org/profile.php?12,930>
|
||||
Marius Kintel <http://reprap.soup.io/>
|
||||
Nophead <http://www.blogger.com/profile/12801535866788103677>
|
||||
PJR <http://forums.reprap.org/profile.php?12,757>
|
||||
Reece.Arnott <http://forums.reprap.org/profile.php?12,152>
|
||||
Wade <http://forums.reprap.org/profile.php?12,489>
|
||||
Xsainnz <http://forums.reprap.org/profile.php?12,563>
|
||||
Zach Hoeken <http://blog.zachhoeken.com/>
|
||||
Ilya Kulakov (kulakov.ilya@gmail.com)
|
||||
|
||||
Organizations:
|
||||
Ultimaker <http://www.ultimaker.com>
|
||||
Art of Illusion <http://www.artofillusion.org/>"""
|
||||
|
||||
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
|
||||
|
||||
|
||||
def main():
|
||||
parser = OptionParser(usage="usage: %prog [options] <filename>.stl")
|
||||
parser.add_option("-i", "--ini", action="store", type="string", dest="profileini",
|
||||
help="Load settings from a profile ini file")
|
||||
parser.add_option("-r", "--print", action="store", type="string", dest="printfile",
|
||||
help="Open the printing interface, instead of the normal cura interface.")
|
||||
parser.add_option("-p", "--profile", action="store", type="string", dest="profile",
|
||||
help="Internal option, do not use!")
|
||||
parser.add_option("-s", "--slice", action="store_true", dest="slice",
|
||||
help="Slice the given files instead of opening them in Cura")
|
||||
parser.add_option("-w", "--web", action="store_true", dest="webui",
|
||||
help="Start the webui instead of the normal Cura UI")
|
||||
parser.add_option("-d", "--debug", action="store_true", dest="debug",
|
||||
help="Enable debug mode, currently only used by the webui")
|
||||
parser.add_option("--web-host", action="store", type="string", default="0.0.0.0", dest="webHost",
|
||||
help="Specify the host on which to bind the webui, defaults to 0.0.0.0 (all interfaces) if not set")
|
||||
parser.add_option("--web-port", action="store", type="int", default=5000, dest="webPort",
|
||||
help="Specify the port on which to bind the webui, defaults to 5000 if not set")
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if options.profile is not None:
|
||||
profile.loadGlobalProfileFromString(options.profile)
|
||||
if options.profileini is not None:
|
||||
profile.loadGlobalProfile(options.profileini)
|
||||
|
||||
if options.printfile is not None:
|
||||
from Cura.gui import printWindow
|
||||
printWindow.startPrintInterface(options.printfile)
|
||||
elif options.slice is not None:
|
||||
from Cura.util import sliceRun
|
||||
sliceRun.runSlice(args)
|
||||
elif options.webui:
|
||||
import Cura.webui as webapp
|
||||
webapp.run(host=options.webHost, port=options.webPort, debug=options.debug)
|
||||
else:
|
||||
#Place any unused arguments as last file, so Cura starts with opening those files.
|
||||
if len(args) > 0:
|
||||
profile.putPreference('lastFile', ';'.join(args))
|
||||
|
||||
#Do not import anything from Cura.gui before this spot, as the above code also needs to run in pypy.
|
||||
from Cura.gui import app
|
||||
app.CuraApp().MainLoop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,2 +0,0 @@
|
|||
This SkeinPyPy version is based in Skeinforge: 50
|
||||
|
|
@ -1,405 +0,0 @@
|
|||
"""
|
||||
Boolean geometry utilities.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
import zipfile
|
||||
|
||||
|
||||
__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)
|
||||
filePaths = []
|
||||
if os.path.isdir(directoryName):
|
||||
for fileName in os.listdir(directoryName):
|
||||
filePaths.append(os.path.join(absoluteDirectoryPath, fileName))
|
||||
elif '.zip/' in directoryName:
|
||||
zipfilename = directoryName[:directoryName.rfind('.zip/')+4]
|
||||
subpath = directoryName[directoryName.rfind('.zip/')+5:]
|
||||
|
||||
z = zipfile.ZipFile(zipfilename, 'r')
|
||||
for name in z.namelist():
|
||||
if os.path.dirname(name) == subpath:
|
||||
filePaths.append(os.path.join(zipfilename, name))
|
||||
z.close()
|
||||
print(directoryName, filePaths)
|
||||
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.'
|
||||
if '.zip/' in fileName:
|
||||
zipfilename = fileName[:fileName.rfind('.zip/')+4]
|
||||
subpath = fileName[fileName.rfind('.zip/')+5:]
|
||||
|
||||
try:
|
||||
z = zipfile.ZipFile(zipfilename, 'r')
|
||||
f = z.open(subpath, 'r')
|
||||
fileText = f.read()
|
||||
f.close()
|
||||
z.close()
|
||||
return fileText
|
||||
except KeyError:
|
||||
if printWarning:
|
||||
print('The file ' + fileName + ' does not exist.')
|
||||
return ''
|
||||
try:
|
||||
f = open(fileName, readMode)
|
||||
fileText = f.read()
|
||||
f.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
|
@ -1,129 +0,0 @@
|
|||
"""
|
||||
Fabmetheus interpret is a fabmetheus utility to interpret a file, turning it into fabmetheus constructive solid geometry xml.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from fabmetheus_utilities import archive
|
||||
from fabmetheus_utilities import euclidean
|
||||
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(object):
|
||||
"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)
|
|
@ -1,156 +0,0 @@
|
|||
"""
|
||||
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
|
||||
|
||||
from fabmetheus_utilities import archive
|
||||
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()
|
|
@ -1,134 +0,0 @@
|
|||
from __future__ import absolute_import
|
||||
|
||||
from fabmetheus_utilities.geometry.geometry_tools import face
|
||||
from fabmetheus_utilities.geometry.solids import triangle_mesh
|
||||
from fabmetheus_utilities.vector3 import Vector3
|
||||
|
||||
from xml.parsers.expat import ParserCreate
|
||||
|
||||
def getCarving(fileName=''):
|
||||
"Get the triangle mesh for the dae file."
|
||||
return daeModel().load(fileName)
|
||||
|
||||
class daeModel(triangle_mesh.TriangleMesh):
|
||||
def __init__(self):
|
||||
triangle_mesh.TriangleMesh.__init__(self)
|
||||
|
||||
def load(self, filename):
|
||||
r = ParserCreate()
|
||||
r.StartElementHandler = self._StartElementHandler
|
||||
r.EndElementHandler = self._EndElementHandler
|
||||
r.CharacterDataHandler = self._CharacterDataHandler
|
||||
|
||||
self._base = {}
|
||||
self._cur = self._base
|
||||
self._idMap = {}
|
||||
self._geometryList = []
|
||||
r.ParseFile(open(filename, "r"))
|
||||
|
||||
self._scale = float(self._base['collada'][0]['asset'][0]['unit'][0]['_meter']) * 1000
|
||||
|
||||
for instance_visual_scene in self._base['collada'][0]['scene'][0]['instance_visual_scene']:
|
||||
for node in self._idMap[instance_visual_scene['_url']]['node']:
|
||||
self._ProcessNode2(node)
|
||||
|
||||
self._base = None
|
||||
self._cur = None
|
||||
self._idMap = None
|
||||
|
||||
return self
|
||||
|
||||
def _ProcessNode2(self, node, matrix = None):
|
||||
if 'matrix' in node:
|
||||
oldMatrix = matrix
|
||||
matrix = map(float, node['matrix'][0]['__data'].split())
|
||||
if oldMatrix != None:
|
||||
newMatrix = [0]*16
|
||||
newMatrix[0] = oldMatrix[0] * matrix[0] + oldMatrix[1] * matrix[4] + oldMatrix[2] * matrix[8] + oldMatrix[3] * matrix[12]
|
||||
newMatrix[1] = oldMatrix[0] * matrix[1] + oldMatrix[1] * matrix[5] + oldMatrix[2] * matrix[9] + oldMatrix[3] * matrix[13]
|
||||
newMatrix[2] = oldMatrix[0] * matrix[2] + oldMatrix[1] * matrix[6] + oldMatrix[2] * matrix[10] + oldMatrix[3] * matrix[14]
|
||||
newMatrix[3] = oldMatrix[0] * matrix[3] + oldMatrix[1] * matrix[7] + oldMatrix[2] * matrix[11] + oldMatrix[3] * matrix[15]
|
||||
newMatrix[4] = oldMatrix[4] * matrix[0] + oldMatrix[5] * matrix[4] + oldMatrix[6] * matrix[8] + oldMatrix[7] * matrix[12]
|
||||
newMatrix[5] = oldMatrix[4] * matrix[1] + oldMatrix[5] * matrix[5] + oldMatrix[6] * matrix[9] + oldMatrix[7] * matrix[13]
|
||||
newMatrix[6] = oldMatrix[4] * matrix[2] + oldMatrix[5] * matrix[6] + oldMatrix[6] * matrix[10] + oldMatrix[7] * matrix[14]
|
||||
newMatrix[7] = oldMatrix[4] * matrix[3] + oldMatrix[5] * matrix[7] + oldMatrix[6] * matrix[11] + oldMatrix[7] * matrix[15]
|
||||
newMatrix[8] = oldMatrix[8] * matrix[0] + oldMatrix[9] * matrix[4] + oldMatrix[10] * matrix[8] + oldMatrix[11] * matrix[12]
|
||||
newMatrix[9] = oldMatrix[8] * matrix[1] + oldMatrix[9] * matrix[5] + oldMatrix[10] * matrix[9] + oldMatrix[11] * matrix[13]
|
||||
newMatrix[10] = oldMatrix[8] * matrix[2] + oldMatrix[9] * matrix[6] + oldMatrix[10] * matrix[10] + oldMatrix[11] * matrix[14]
|
||||
newMatrix[11] = oldMatrix[8] * matrix[3] + oldMatrix[9] * matrix[7] + oldMatrix[10] * matrix[11] + oldMatrix[11] * matrix[15]
|
||||
newMatrix[12] = oldMatrix[12] * matrix[0] + oldMatrix[13] * matrix[4] + oldMatrix[14] * matrix[8] + oldMatrix[15] * matrix[12]
|
||||
newMatrix[13] = oldMatrix[12] * matrix[1] + oldMatrix[13] * matrix[5] + oldMatrix[14] * matrix[9] + oldMatrix[15] * matrix[13]
|
||||
newMatrix[14] = oldMatrix[12] * matrix[2] + oldMatrix[13] * matrix[6] + oldMatrix[14] * matrix[10] + oldMatrix[15] * matrix[14]
|
||||
newMatrix[15] = oldMatrix[12] * matrix[3] + oldMatrix[13] * matrix[7] + oldMatrix[14] * matrix[11] + oldMatrix[15] * matrix[15]
|
||||
matrix = newMatrix
|
||||
if 'node' in node:
|
||||
for n in node['node']:
|
||||
self._ProcessNode2(n, matrix)
|
||||
if 'instance_geometry' in node:
|
||||
for instance_geometry in node['instance_geometry']:
|
||||
mesh = self._idMap[instance_geometry['_url']]['mesh'][0]
|
||||
|
||||
if 'triangles' in mesh:
|
||||
for triangles in mesh['triangles']:
|
||||
for input in triangles['input']:
|
||||
if input['_semantic'] == 'VERTEX':
|
||||
vertices = self._idMap[input['_source']]
|
||||
for input in vertices['input']:
|
||||
if input['_semantic'] == 'POSITION':
|
||||
vertices = self._idMap[input['_source']]
|
||||
indexList = map(int, triangles['p'][0]['__data'].split())
|
||||
positionList = map(float, vertices['float_array'][0]['__data'].split())
|
||||
|
||||
startIndex = len(self.vertexes)
|
||||
for idx in xrange(0, len(positionList)/3):
|
||||
x = positionList[idx*3] * self._scale
|
||||
y = positionList[idx*3+1] * self._scale
|
||||
z = positionList[idx*3+2] * self._scale
|
||||
if matrix != None:
|
||||
self.vertexes.append(Vector3(x * matrix[0] + y * matrix[1] + z * matrix[2] + matrix[3], x * matrix[4] + y * matrix[5] + z * matrix[6] + matrix[7], x * matrix[8] + y * matrix[9] + z * matrix[10] + matrix[11]))
|
||||
else:
|
||||
self.vertexes.append(Vector3(x, y, z))
|
||||
stepSize = len(indexList) / (int(triangles['_count']) * 3)
|
||||
for i in xrange(0, int(triangles['_count'])):
|
||||
idx = i * stepSize * 3
|
||||
f = face.Face()
|
||||
f.index = len(self.faces)
|
||||
f.vertexIndexes.append(startIndex + indexList[idx])
|
||||
f.vertexIndexes.append(startIndex + indexList[idx+stepSize])
|
||||
f.vertexIndexes.append(startIndex + indexList[idx+stepSize*2])
|
||||
self.faces.append(f)
|
||||
|
||||
if 'instance_node' in node:
|
||||
for instance_node in node['instance_node']:
|
||||
self._ProcessNode2(self._idMap[instance_node['_url']], matrix)
|
||||
|
||||
def _StartElementHandler(self, name, attributes):
|
||||
name = name.lower()
|
||||
if not name in self._cur:
|
||||
self._cur[name] = []
|
||||
new = {'__name': name, '__parent': self._cur}
|
||||
self._cur[name].append(new)
|
||||
self._cur = new
|
||||
for k in attributes.keys():
|
||||
self._cur['_' + k] = attributes[k]
|
||||
|
||||
if 'id' in attributes:
|
||||
self._idMap['#' + attributes['id']] = self._cur
|
||||
|
||||
def _EndElementHandler(self, name):
|
||||
self._cur = self._cur['__parent']
|
||||
|
||||
def _CharacterDataHandler(self, data):
|
||||
if len(data.strip()) < 1:
|
||||
return
|
||||
if '__data' in self._cur:
|
||||
self._cur['__data'] += data
|
||||
else:
|
||||
self._cur['__data'] = data
|
||||
|
||||
def _GetWithKey(self, item, basename, key, value):
|
||||
input = basename
|
||||
while input in item:
|
||||
if item[basename]['_'+key] == value:
|
||||
return self._idMap[item[input]['_source']]
|
||||
basename += "!"
|
|
@ -1,77 +0,0 @@
|
|||
"""
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
__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
|
|
@ -1,75 +0,0 @@
|
|||
"""
|
||||
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
|
||||
|
||||
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 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] ) )
|
|
@ -1,185 +0,0 @@
|
|||
"""
|
||||
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
|
||||
|
||||
from fabmetheus_utilities.vector3 import Vector3
|
||||
from fabmetheus_utilities import euclidean
|
||||
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(object):
|
||||
"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(object):
|
||||
"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()
|
|
@ -1,108 +0,0 @@
|
|||
"""
|
||||
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
|
||||
|
||||
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 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] ) )
|
|
@ -1,104 +0,0 @@
|
|||
"""
|
||||
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
|
||||
|
||||
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 svg_writer
|
||||
from fabmetheus_utilities import xml_simple_writer
|
||||
|
||||
__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(object):
|
||||
'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
|
|
@ -1,441 +0,0 @@
|
|||
"""
|
||||
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
|
||||
|
||||
from fabmetheus_utilities.vector3 import Vector3
|
||||
from fabmetheus_utilities import archive
|
||||
from fabmetheus_utilities import euclidean
|
||||
|
||||
import math
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] < 3:
|
||||
import cStringIO
|
||||
else:
|
||||
import io as cStringIO
|
||||
|
||||
__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(object):
|
||||
'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(object):
|
||||
'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])
|
|
@ -1,53 +0,0 @@
|
|||
"""
|
||||
Drill negative solid.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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.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 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(object):
|
||||
"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
|
|
@ -1,58 +0,0 @@
|
|||
"""
|
||||
Svg reader.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"Class to hold svg variables."
|
||||
def __init__(self, elementNode):
|
||||
'Set defaults.'
|
||||
self.svgReader = svg_reader.SVGReader()
|
||||
self.svgReader.parseSVGByElementNode(elementNode)
|
|
@ -1,74 +0,0 @@
|
|||
"""
|
||||
Polygon path.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"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')
|
|
@ -1,50 +0,0 @@
|
|||
"""
|
||||
Boolean geometry concatenation.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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
|
||||
|
||||
|
||||
__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(object):
|
||||
'Class to hold concatenate variables.'
|
||||
def __init__(self, elementNode):
|
||||
'Initialize.'
|
||||
self.target = evaluate.getTransformedPathsByKey([], elementNode, 'target')
|
|
@ -1,438 +0,0 @@
|
|||
"""
|
||||
Boolean geometry extrusion.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
'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(object):
|
||||
'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(object):
|
||||
'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
|
@ -1,163 +0,0 @@
|
|||
"""
|
||||
Grid path points.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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
|
||||
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(object):
|
||||
'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')
|
|
@ -1,202 +0,0 @@
|
|||
"""
|
||||
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
|
||||
|
||||
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.vector3index import Vector3Index
|
||||
from fabmetheus_utilities import archive
|
||||
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 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(object):
|
||||
'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__)
|
|
@ -1,173 +0,0 @@
|
|||
"""
|
||||
Boolean geometry extrusion.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"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)
|
|
@ -1,101 +0,0 @@
|
|||
"""
|
||||
Square path.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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
|
||||
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(object):
|
||||
"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')
|
|
@ -1,245 +0,0 @@
|
|||
"""
|
||||
Linear bearing cage.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
'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)
|
|
@ -1,305 +0,0 @@
|
|||
"""
|
||||
Polygon path.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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 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(object):
|
||||
'Class to hold lineation variables.'
|
||||
def __init__(self, elementNode):
|
||||
'Set defaults.'
|
||||
self.target = evaluate.getTransformedPathsByKey([], elementNode, 'target')
|
||||
|
||||
|
||||
class SideLoop(object):
|
||||
'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(object):
|
||||
'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
|
|
@ -1,255 +0,0 @@
|
|||
"""
|
||||
Mechaslab.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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.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(object):
|
||||
'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(object):
|
||||
'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(object):
|
||||
'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__)
|
|
@ -1,101 +0,0 @@
|
|||
"""
|
||||
Peg.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
'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')
|
|
@ -1,67 +0,0 @@
|
|||
"""
|
||||
Polygon path.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"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')
|
|
@ -1,80 +0,0 @@
|
|||
"""
|
||||
Shaft path.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"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')
|
|
@ -1,174 +0,0 @@
|
|||
"""
|
||||
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
|
||||
|
||||
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 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 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(object):
|
||||
'Class to hold solid variables.'
|
||||
def __init__(self, elementNode):
|
||||
'Set defaults.'
|
||||
self.targets = evaluate.getElementNodesByKey(elementNode, 'target')
|
|
@ -1,155 +0,0 @@
|
|||
"""
|
||||
Sponge slice.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"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(object):
|
||||
"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))
|
|
@ -1,78 +0,0 @@
|
|||
"""
|
||||
Square path.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"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')
|
|
@ -1,114 +0,0 @@
|
|||
"""
|
||||
Teardrop path.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"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)
|
|
@ -1,61 +0,0 @@
|
|||
"""
|
||||
Text vertexes.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"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')
|
|
@ -1,176 +0,0 @@
|
|||
"""
|
||||
Boolean geometry dictionary object.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
'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))
|
|
@ -1,162 +0,0 @@
|
|||
"""
|
||||
Face of a triangle mesh.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
|
||||
from fabmetheus_utilities import xml_simple_reader
|
||||
from fabmetheus_utilities import xml_simple_writer
|
||||
import cStringIO
|
||||
|
||||
|
||||
__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(object):
|
||||
"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(object):
|
||||
"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 )
|
|
@ -1,202 +0,0 @@
|
|||
"""
|
||||
Path.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
'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
|
|
@ -1,48 +0,0 @@
|
|||
"""
|
||||
Arc vertexes.
|
||||
|
||||
From:
|
||||
http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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
|
||||
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)
|
|
@ -1,56 +0,0 @@
|
|||
"""
|
||||
Cubic vertexes.
|
||||
|
||||
From:
|
||||
http://www.w3.org/TR/SVG/paths.html#PathDataCubicBezierCommands
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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)
|
|
@ -1,43 +0,0 @@
|
|||
"""
|
||||
Quadratic vertexes.
|
||||
|
||||
From:
|
||||
http://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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)
|
|
@ -1,43 +0,0 @@
|
|||
"""
|
||||
Vertex of a triangle mesh.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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))
|
|
@ -1,193 +0,0 @@
|
|||
"""
|
||||
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
|
||||
|
||||
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 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(object):
|
||||
'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
|
|
@ -1,229 +0,0 @@
|
|||
"""
|
||||
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
|
||||
|
||||
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 import euclidean
|
||||
from fabmetheus_utilities import intercircle
|
||||
|
||||
__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
|
@ -1,56 +0,0 @@
|
|||
"""
|
||||
Boolean geometry utilities.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
|
||||
from fabmetheus_utilities import archive
|
||||
|
||||
|
||||
__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(object):
|
||||
'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)
|
|
@ -1,95 +0,0 @@
|
|||
"""
|
||||
Boolean geometry utilities.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
|
||||
__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(object):
|
||||
'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)
|
|
@ -1,178 +0,0 @@
|
|||
"""
|
||||
Boolean geometry utilities.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
'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)
|
|
@ -1,100 +0,0 @@
|
|||
"""
|
||||
Dictionary object attributes.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
'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)
|
|
@ -1,121 +0,0 @@
|
|||
"""
|
||||
List object attributes.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
'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)
|
|
@ -1,134 +0,0 @@
|
|||
"""
|
||||
String object attributes.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
'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)
|
|
@ -1,99 +0,0 @@
|
|||
"""
|
||||
Boolean geometry utilities.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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}
|
|
@ -1,93 +0,0 @@
|
|||
"""
|
||||
Boolean geometry utilities.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
'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}
|
|
@ -1,59 +0,0 @@
|
|||
"""
|
||||
Boolean geometry utilities.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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):
|
||||
'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}
|
|
@ -1,34 +0,0 @@
|
|||
"""
|
||||
Boolean geometry utilities.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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}
|
|
@ -1,22 +0,0 @@
|
|||
"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,492 +0,0 @@
|
|||
"""
|
||||
Boolean geometry four by four matrix.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
|
||||
from fabmetheus_utilities.vector3 import Vector3
|
||||
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(object):
|
||||
'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))
|
|
@ -1,65 +0,0 @@
|
|||
"""
|
||||
Boolean geometry scale.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from fabmetheus_utilities.geometry.creation import solid
|
||||
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 = 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(object):
|
||||
"Class to hold scale variables."
|
||||
def __init__(self, elementNode):
|
||||
'Set defaults.'
|
||||
self.scaleTetragrid = matrix.getScaleTetragrid(elementNode, '')
|
|
@ -1,62 +0,0 @@
|
|||
"""
|
||||
Boolean geometry rotate.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from fabmetheus_utilities.geometry.creation import solid
|
||||
from fabmetheus_utilities.geometry.geometry_utilities import matrix
|
||||
|
||||
|
||||
__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(object):
|
||||
"Class to hold rotate variables."
|
||||
def __init__(self, elementNode, prefix):
|
||||
'Set defaults.'
|
||||
self.rotateTetragrid = matrix.getRotateTetragrid(elementNode, prefix)
|
|
@ -1,62 +0,0 @@
|
|||
"""
|
||||
Boolean geometry transform.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from fabmetheus_utilities.geometry.creation import solid
|
||||
from fabmetheus_utilities.geometry.geometry_utilities import matrix
|
||||
|
||||
|
||||
__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(object):
|
||||
"Class to hold transform variables."
|
||||
def __init__(self, elementNode, prefix):
|
||||
'Set defaults.'
|
||||
self.transformTetragrid = matrix.getTransformTetragrid(elementNode, prefix)
|
|
@ -1,66 +0,0 @@
|
|||
"""
|
||||
Boolean geometry translation.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"Class to hold translate variables."
|
||||
def __init__(self, elementNode):
|
||||
'Set defaults.'
|
||||
self.translateTetragrid = matrix.getTranslateTetragrid(elementNode, '')
|
|
@ -1,126 +0,0 @@
|
|||
"""
|
||||
Boolean geometry array.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"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')
|
|
@ -1,93 +0,0 @@
|
|||
"""
|
||||
Boolean geometry carve.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"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')
|
|
@ -1,70 +0,0 @@
|
|||
"""
|
||||
Boolean geometry copy.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"Class to hold copy variables."
|
||||
def __init__(self, elementNode):
|
||||
'Set defaults.'
|
||||
self.target = evaluate.getElementNodeByKey(elementNode, 'target')
|
|
@ -1,112 +0,0 @@
|
|||
"""
|
||||
Boolean geometry disjoin.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"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')
|
|
@ -1,93 +0,0 @@
|
|||
"""
|
||||
Boolean geometry group of solids.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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
|
||||
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(object):
|
||||
"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')
|
|
@ -1,94 +0,0 @@
|
|||
"""
|
||||
Boolean geometry write.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
|
||||
from fabmetheus_utilities.geometry.geometry_utilities import matrix
|
||||
from fabmetheus_utilities import archive
|
||||
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(object):
|
||||
"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')
|
|
@ -1,68 +0,0 @@
|
|||
"""
|
||||
Add material to support overhang or remove material at the overhang angle.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from fabmetheus_utilities.geometry.creation import lineation
|
||||
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(object):
|
||||
"Class to hold bevel variables."
|
||||
def __init__(self, elementNode, prefix, sideLength):
|
||||
'Set defaults.'
|
||||
self.radius = lineation.getFloatByPrefixSide(0.0, elementNode, prefix + 'radius', sideLength)
|
|
@ -1,35 +0,0 @@
|
|||
"""
|
||||
Create outline.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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)
|
|
@ -1,50 +0,0 @@
|
|||
"""
|
||||
Create outline.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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 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(object):
|
||||
"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')
|
|
@ -1,395 +0,0 @@
|
|||
"""
|
||||
Add material to support overhang or remove material at the overhang angle.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"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(object):
|
||||
"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(object):
|
||||
"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(object):
|
||||
"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
|
|
@ -1,92 +0,0 @@
|
|||
"""
|
||||
Add material to support overhang or remove material at the overhang angle.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"Class to hold round variables."
|
||||
def __init__(self, elementNode, prefix, sideLength):
|
||||
'Set defaults.'
|
||||
self.radius = lineation.getFloatByPrefixSide(0.0, elementNode, prefix + 'radius', sideLength)
|
|
@ -1,164 +0,0 @@
|
|||
"""
|
||||
Add material to support overhang or remove material at the overhang angle.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"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(object):
|
||||
'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)
|
|
@ -1,41 +0,0 @@
|
|||
"""
|
||||
Add material to support overhang or remove material at the overhang angle.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"Class to hold wedge variables."
|
||||
def __init__(self, elementNode, prefix):
|
||||
'Set defaults.'
|
||||
self.center = evaluate.getVector3ByPrefix(Vector3(), elementNode, prefix + 'center')
|
|
@ -1,102 +0,0 @@
|
|||
"""
|
||||
Boolean geometry bottom.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"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)
|
|
@ -1,81 +0,0 @@
|
|||
"""
|
||||
Create inset.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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.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(object):
|
||||
"Class to hold inset variables."
|
||||
def __init__(self, elementNode, prefix):
|
||||
'Set defaults.'
|
||||
self.radius = evaluate.getEvaluatedFloat(2.0 * setting.getEdgeWidth(elementNode), elementNode, prefix + 'radius')
|
|
@ -1,73 +0,0 @@
|
|||
"""
|
||||
Create inset.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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.solids import triangle_mesh
|
||||
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(object):
|
||||
"Class to hold outset variables."
|
||||
def __init__(self, elementNode, prefix):
|
||||
'Set defaults.'
|
||||
self.radius = evaluate.getEvaluatedFloat(2.0 * setting.getEdgeWidth(elementNode), elementNode, prefix + 'radius')
|
|
@ -1,114 +0,0 @@
|
|||
"""
|
||||
Equation for vertexes.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"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(object):
|
||||
"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()
|
|
@ -1,90 +0,0 @@
|
|||
"""
|
||||
Add material to support overhang or remove material at the overhang angle.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"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()
|
|
@ -1,46 +0,0 @@
|
|||
"""
|
||||
Add material to support overhang or remove material at the overhang angle.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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 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)
|
|
@ -1,76 +0,0 @@
|
|||
"""
|
||||
Boolean geometry cube.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"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)
|
|
@ -1,109 +0,0 @@
|
|||
"""
|
||||
Boolean geometry cylinder.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
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(object):
|
||||
"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')
|
|
@ -1,41 +0,0 @@
|
|||
"""
|
||||
Boolean geometry difference of solids.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from fabmetheus_utilities.geometry.geometry_utilities import boolean_solid
|
||||
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
|
||||
from fabmetheus_utilities.geometry.solids import group
|
||||
|
||||
|
||||
__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 convertElementNode(elementNode, geometryOutput):
|
||||
"Convert the xml element to a difference xml element."
|
||||
group.convertContainerElementNode(elementNode, geometryOutput, Difference())
|
||||
|
||||
def getNewDerivation(elementNode):
|
||||
'Get new derivation.'
|
||||
return evaluate.EmptyObject(elementNode)
|
||||
|
||||
def processElementNode(elementNode):
|
||||
"Process the xml element."
|
||||
evaluate.processArchivable(Difference, elementNode)
|
||||
|
||||
|
||||
class Difference( boolean_solid.BooleanSolid ):
|
||||
"A difference object."
|
||||
def getLoopsFromObjectLoopsList(self, importRadius, visibleObjectLoopsList):
|
||||
"Get loops from visible object loops list."
|
||||
return self.getDifference(importRadius, visibleObjectLoopsList)
|
||||
|
||||
def getXMLLocalName(self):
|
||||
"Get xml class name."
|
||||
return self.__class__.__name__.lower()
|
|
@ -1,80 +0,0 @@
|
|||
"""
|
||||
Boolean geometry group of solids.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from fabmetheus_utilities.geometry.geometry_tools import dictionary
|
||||
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 convertContainerElementNode(elementNode, geometryOutput, xmlObject):
|
||||
"Convert the xml element to a group xml element."
|
||||
elementNode.linkObject(xmlObject)
|
||||
matrix.getBranchMatrixSetElementNode(elementNode)
|
||||
elementNode.getXMLProcessor().createChildNodes(geometryOutput['shapes'], elementNode)
|
||||
|
||||
def convertElementNode(elementNode, geometryOutput):
|
||||
"Convert the xml element to a group xml element."
|
||||
convertContainerElementNode(elementNode, geometryOutput, Group())
|
||||
|
||||
def getNewDerivation(elementNode):
|
||||
'Get new derivation.'
|
||||
return evaluate.EmptyObject(elementNode)
|
||||
|
||||
def processElementNode(elementNode):
|
||||
"Process the xml element."
|
||||
evaluate.processArchivable(Group, elementNode)
|
||||
|
||||
|
||||
class Group(dictionary.Dictionary):
|
||||
"A group."
|
||||
def __init__(self):
|
||||
"Add empty lists."
|
||||
dictionary.Dictionary.__init__(self)
|
||||
self.matrix4X4 = matrix.Matrix()
|
||||
|
||||
def addXMLInnerSection(self, depth, output):
|
||||
"Add xml inner section for this object."
|
||||
if self.matrix4X4 != None:
|
||||
self.matrix4X4.addXML(depth, output)
|
||||
self.addXMLSection(depth, output)
|
||||
|
||||
def addXMLSection(self, depth, output):
|
||||
"Add the xml section for this object."
|
||||
pass
|
||||
|
||||
def getLoops(self, importRadius, z):
|
||||
"Get loops sliced through shape."
|
||||
visibleObjects = evaluate.getVisibleObjects(self.archivableObjects)
|
||||
loops = []
|
||||
for visibleObject in visibleObjects:
|
||||
loops += visibleObject.getLoops(importRadius, z)
|
||||
return loops
|
||||
|
||||
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 getVisible(self):
|
||||
"Get visible."
|
||||
return euclidean.getBooleanFromDictionary(True, self.getAttributes(), 'visible')
|
||||
|
||||
def setToElementNode(self, elementNode):
|
||||
'Set to elementNode.'
|
||||
self.elementNode = elementNode
|
||||
elementNode.parentNode.xmlObject.archivableObjects.append(self)
|
||||
matrix.getBranchMatrixSetElementNode(elementNode)
|
|
@ -1,37 +0,0 @@
|
|||
"""
|
||||
Boolean geometry intersection of solids.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from fabmetheus_utilities.geometry.geometry_utilities import evaluate
|
||||
from fabmetheus_utilities.geometry.solids import difference
|
||||
from fabmetheus_utilities.geometry.solids import group
|
||||
|
||||
|
||||
__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 convertElementNode(elementNode, geometryOutput):
|
||||
"Convert the xml element to an intersection xml element."
|
||||
group.convertContainerElementNode(elementNode, geometryOutput, Intersection())
|
||||
|
||||
def getNewDerivation(elementNode):
|
||||
'Get new derivation.'
|
||||
return evaluate.EmptyObject(elementNode)
|
||||
|
||||
def processElementNode(elementNode):
|
||||
"Process the xml element."
|
||||
evaluate.processArchivable(Intersection, elementNode)
|
||||
|
||||
|
||||
class Intersection(difference.Difference):
|
||||
"An intersection object."
|
||||
def getLoopsFromObjectLoopsList(self, importRadius, visibleObjectLoopsList):
|
||||
"Get loops from visible object loops list."
|
||||
return self.getIntersection(importRadius, visibleObjectLoopsList)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue