diff --git a/README b/README index 9d8cdf7..55c6837 100644 --- a/README +++ b/README @@ -1,30 +1,35 @@ -Skeinforge_PyPy is a set of patches for different versions of Skeinforge. Made to make use of PyPy easier. -The code consists out of a few patches and a build script. +SkeinPyPy is a patched version of Skeinforge. Made to make use of PyPy in combination with Skeinforge easier. +It's also packaged with a customized version of PrintRun. To make a compleet software package. -The final result is a release package for Windows which should be ready to run without any additional requirements. +It's best to use this package with the Marlin firmware. See: http://wiki.ultimaker.com/Skeinforge_PyPy -====What is THIS?==== -READ THIS! PLEASE! +====How to use=== +Download the package for your operating system. -These source files are just patch files on skeinforge. If you want to use this don't look at the source files. Look at the download section in github at: -https://github.com/daid/Skeinforge_PyPy/downloads +Windows: + Double click the "skeinforge.bat" for Skeinforge, this can be used to slice your models into GCode. + Double click the "Printrun.bat" for "PrintRun" this is a graphical command&control interface for your printer. This can run the generated GCode. + +Linux/MacOSX: (experimental, no packaged python) + First you'll need to have python installed on your system! With pyserial when you want to use PrintRun. You do not need pypy, this is packaged with SkeinPyPy + Then run SkeinPyPy/skeinforge_application/skeinforge.py for skeinforge to slice your model + Or run printrun\\pronterface.py for the PrintRun interface to print your model ====What is changed==== -* All: Do not show settings when ran from command line -* All: Run PyPy to slice a model instead of normal python -* All: Changing "Perimeter width over thickness (ratio)" into "Perimeter width". +* Do not show settings when ran from command line +* Run PyPy to slice a model instead of normal python +* Changing "Perimeter width over thickness (ratio)" into "Perimeter width". With 5D machines this makes more sense, as you have good control over the width. -* All: Save settings in .skeinforge_pypy to not mess up normal skeinforge profiles. -* All: Default settings changed to match Ultimaker with dimension firmware (Marlin or Sprinter) -* All: Modified "Object first layer speed" settings to work on the first 3 layers (configurable) -* SF45: Added "Object first layer travel speed" setting, to slow down the travel on the first layers. +* Save settings in .skeinforge_pypy to not mess up normal skeinforge profiles. +* Default settings changed to match Ultimaker with dimension firmware (Marlin or Sprinter) and PLA. +* Modified "Object first layer speed" settings to work on the first 3 layers (configurable) +* Added "Object first layer travel speed" setting, to slow down the travel on the first layers. Reduces the chance that a travel pulls the first layer lose. -* SF41: Dimensions patch against short pauses -* SF41: Speedup patch in euclidean.py (Merged in mainline SF43) ====Bugs==== The graphical analize plugins don't work (Skeinlayer and Skeiniso) ====How does it work==== -The user interface still runs in normal python (as PyPy with TK is a bit hard to build, especially for windows), and when you slice it will run command line PyPy to slice the model. +The user interface still runs in normal python (as PyPy with TK is a bit hard to build, especially for windows), + and when you slice it will run command line PyPy to slice the model. diff --git a/SkeinPyPy/SkeinforgeVersion b/SkeinPyPy/SkeinforgeVersion new file mode 100644 index 0000000..21e72e8 --- /dev/null +++ b/SkeinPyPy/SkeinforgeVersion @@ -0,0 +1 @@ +48 diff --git a/SkeinPyPy/__init__.py b/SkeinPyPy/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/SkeinPyPy/documentation/contents.html b/SkeinPyPy/documentation/contents.html new file mode 100644 index 0000000..376a8a3 --- /dev/null +++ b/SkeinPyPy/documentation/contents.html @@ -0,0 +1,104 @@ + + + Contents + + +

+Previous / Next / Contents +

+Fabmetheus Utilities
+    Interpret Plugins
+      Gts
+      Obj
+      Slc
+      Stl
+      Svg
+      Xml
+        Artofillusion
+        Fabmetheus
+  Geometry
+    Creation
+      Gear
+    Geometry Utilities
+      Boolean Geometry
+      Boolean Solid
+    Solids
+  Skeinforge
+    Analyze
+        Display Line
+        View Move
+        View Rotate
+        Zoom In
+        Zoom Out
+      Clairvoyance
+      Comment
+        Postscript
+        Scalable Vector Graphics
+      Interpret
+      Skeiniso
+      Skeinlayer
+      Statistic
+      Synopsis
+      Vectorwrite
+    Craft
+      Alteration
+      Bottom
+      Carve
+      Chamber
+      Chop
+      Cleave
+      Clip
+      Coil
+      Comb
+      Cool
+      Dimension
+      Drill
+      Export
+        Binary 16 Byte
+        Gcode Step
+        Gcode Time Segment
+          Gcode Small
+      Feed
+      Fill
+      Fillet
+      Flow
+      Home
+      Hop
+      Inset
+      Jitter
+      Lash
+      Lift
+      Limit
+      Mill
+      Multiply
+      Oozebane
+      Outset
+      Preface
+      Raft
+      Scale
+      Skin
+      Skirt
+      Smooth
+      Speed
+      Splodge
+      Stretch
+      Temperature
+      Tower
+      Unpause
+      Whittle
+      Widen
+      Wipe
+    Help
+    Meta
+      Description
+      Polyfile
+    Profile
+      Cutting
+      Extrusion
+      Milling
+      Winding
+

+Previous / Next / Contents +

+ + diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.archive.html b/SkeinPyPy/documentation/fabmetheus_utilities.archive.html new file mode 100644 index 0000000..c5366aa --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.archive.html @@ -0,0 +1,109 @@ + + +Python: module fabmetheus_utilities.archive + + + + +
 
+ 
fabmetheus_utilities.archive ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/archive.py
+

Boolean geometry utilities.

+

+ + + + + +
 
+Modules
       
__init__
+
os
+
sys
+
traceback
+

+ + + + + +
 
+Functions
       
addToNamePathDictionary(directoryPath, namePathDictionary)
Add to the name path dictionary.
+
getAbsoluteFolderPath(filePath, folderName='')
Get the absolute folder path.
+
getAbsoluteFrozenFolderPath(filePath, folderName='')
Get the absolute frozen folder path.
+
getAnalyzePluginsDirectoryPath(subName='')
Get the analyze plugins directory path.
+
getCraftPluginsDirectoryPath(subName='')
Get the craft plugins directory path.
+
getDocumentationPath(subName='')
Get the documentation file path.
+
getElementsPath(subName='')
Get the evaluate_elements directory path.
+
getEndsWithList(word, wordEndings)
Determine if the word ends with a list.
+
getFabmetheusPath(subName='')
Get the fabmetheus directory path.
+
getFabmetheusToolsPath(subName='')
Get the fabmetheus tools directory path.
+
getFabmetheusUtilitiesPath(subName='')
Get the fabmetheus utilities directory path.
+
getFileNamesByFilePaths(pluginFilePaths)
Get the file names of the plugins by the file paths.
+
getFilePathWithUnderscoredBasename(fileName, suffix)
Get the file path with all spaces in the basename replaced with underscores.
+
getFilePaths(fileInDirectory='')
Get the file paths in the directory of the file in directory.
+
getFilePathsByDirectory(directoryName)
Get the file paths in the directory of the file in directory.
+
getFilePathsRecursively(fileInDirectory='')
Get the file paths in the directory of the file in directory.
+
getFileText(fileName, printWarning=True, readMode='r')
Get the entire text of a file.
+
getFileTextInFileDirectory(fileInDirectory, fileName, readMode='r')
Get the entire text of a file in the directory of the file in directory.
+
getFilesWithFileTypeWithoutWords(fileType, words=[], fileInDirectory='')
Get files which have a given file type, but with do not contain a word in a list.
+
getFilesWithFileTypesWithoutWords(fileTypes, words=[], fileInDirectory='')
Get files which have a given file type, but with do not contain a word in a list.
+
getFilesWithFileTypesWithoutWordsRecursively(fileTypes, words=[], fileInDirectory='')
Get files recursively which have a given file type, but with do not contain a word in a list.
+
getFundamentalsPath(subName='')
Get the evaluate_fundamentals directory path.
+
getGeometryDictionary(folderName)
Get to the geometry name path dictionary.
+
getGeometryPath(subName='')
Get the geometry directory path.
+
getGeometryToolsPath(subName='')
Get the geometry tools directory path.
+
getGeometryUtilitiesPath(subName='')
Get the geometry_utilities directory path.
+
getInterpretPluginsPath(subName='')
Get the interpret plugins directory path.
+
getJoinedPath(path, subName='')
Get the joined file path.
+
getModuleWithDirectoryPath(directoryPath, fileName)
Get the module from the fileName and folder name.
+
getModuleWithPath(path)
Get the module from the path.
+
getPluginFileNamesFromDirectoryPath(directoryPath)
Get the file names of the python plugins in the directory path.
+
getProfilesPath(subName='')
Get the profiles directory path, which is the settings directory joined with profiles.
+
getPythonDirectoryNames(directoryName)
Get the python directories.
+
getPythonDirectoryNamesRecursively(directoryName='')
Get the python directories recursively.
+
getPythonFileNamesExceptInit(fileInDirectory='')
Get the python fileNames of the directory which the fileInDirectory is in, except for the __init__.py file.
+
getPythonFileNamesExceptInitRecursively(directoryName='')
Get the python fileNames of the directory recursively, except for the __init__.py files.
+
getSettingsPath(subName='')
Get the settings directory path, which is the home directory joined with .skeinforge.
+
getSkeinforgePath(subName='')
Get the skeinforge directory path.
+
getSkeinforgePluginsPath(subName='')
Get the skeinforge plugins directory path.
+
getSummarizedFileName(fileName)
Get the fileName basename if the file is in the current working directory, otherwise return the original full name.
+
getTemplatesPath(subName='')
Get the templates directory path.
+
getTextIfEmpty(fileName, text)
Get the text from a file if it the text is empty.
+
getTextLines(text)
Get the all the lines of text of a text.
+
getUntilDot(text)
Get the text until the last dot, if any.
+
getVersionFileName()
Get the file name of the version date.getFabmetheusUtilitiesPath(subName=)
+
isFileWithFileTypeWithoutWords(fileType, fileName, words)
Determine if file has a given file type, but with does not contain a word in a list.
+
makeDirectory(directoryPath)
Make a directory if it does not already exist.
+
removeBackupFilesByType(fileType)
Remove backup files by type.
+
removeBackupFilesByTypes(fileTypes)
Remove backup files by types.
+
writeFileMessageEnd(end, fileName, fileText, message)
Write to a fileName with a suffix and print a message.
+
writeFileText(fileName, fileText, writeMode='w+')
Write a text to a file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalTemporarySettingsPath = '/home/enrique/.skeinforge'

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.euclidean.html b/SkeinPyPy/documentation/fabmetheus_utilities.euclidean.html new file mode 100644 index 0000000..949e37d --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.euclidean.html @@ -0,0 +1,490 @@ + + +Python: module fabmetheus_utilities.euclidean + + + + +
 
+ 
fabmetheus_utilities.euclidean ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/euclidean.py
+

Euclidean is a collection of python utilities for complex numbers, paths, polygons & Vector3s.
+
+To use euclidean, install python 2.x on your machine, which is avaliable from http://www.python.org/download/
+
+Then in the folder which euclidean is in, type 'python' in a shell to run the python interpreter. Finally type 'import euclidean' to import these utilities and 'from vector3 import Vector3' to import the Vector3 class.
+
+
+Below are examples of euclidean use.
+
+>>> from euclidean import *
+>>> origin=complex()
+>>> right=complex(1.0,0.0)
+>>> back=complex(0.0,1.0)
+>>> getMaximum(right,back)
+1.0, 1.0
+>>> polygon=[origin, right, back]
+>>> getLoopLength(polygon)
+3.4142135623730949
+>>> getAreaLoop(polygon)
+0.5

+

+ + + + + +
 
+Modules
       
__init__
+cStringIO
+
math
+random
+
fabmetheus_utilities.xml_simple_writer
+

+ + + + + +
 
+Classes
       
+
DistanceIndex +
Endpoint +
LoopLayer +
NestedRing +
+
+
NestedBand +
+
+
PathZ +
ProjectiveSpace +
XIntersectionIndex +
+

+ + + + + + + +
 
+class DistanceIndex
   A class to hold the distance and the index of the loop.
 
 Methods defined here:
+
__init__(self, distance, index)
Initialize.
+ +
__repr__(self)
Get the string representation of this distance index.
+ +

+ + + + + + + +
 
+class Endpoint
   The endpoint of a segment.
 
 Methods defined here:
+
__repr__(self)
Get the string representation of this Endpoint.
+ +
getClosestEndpoint(self, endpoints)
Get closest endpoint.
+ +
getClosestMiss(self, endpoints, path, pixelDictionary, width)
Get the closest endpoint which the segment to that endpoint misses the other extrusions.
+ +
getClosestMissCheckEndpointPath(self, endpoints, path, pixelDictionary, width)
Get the closest endpoint which the segment to that endpoint misses the other extrusions, also checking the path of the endpoint.
+ +
getFromOtherPoint(self, otherEndpoint, point)
Initialize from other endpoint.
+ +

+ + + + + + + +
 
+class LoopLayer
   Loops with a z.
 
 Methods defined here:
+
__init__(self, z)
Initialize.
+ +
__repr__(self)
Get the string representation of this loop layer.
+ +

+ + + + + + + +
 
+class NestedBand(NestedRing)
   A loop that surrounds paths.
 
 Methods defined here:
+
__init__(self)
Initialize.
+ +
__repr__(self)
Get the string representation of this nested ring.
+ +
addPerimeterInner(self, extrusionHalfWidth, oldOrderedLocation, skein, threadSequence)
Add to the edge and the inner island.
+ +
addToBoundary(self, vector3)
Add vector3 to boundary.
+ +
addToLoop(self, vector3)
Add vector3 to loop.
+ +
addToThreads(self, extrusionHalfWidth, oldOrderedLocation, skein, threadSequence)
Add to paths from the last location.
+ +
getFillLoops(self, penultimateFillLoops)
Get last fill loops from the outside loop and the loops inside the inside loops.
+ +
getLoopsToBeFilled(self)
Get last fill loops from the outside loop and the loops inside the inside loops.
+ +
getSurroundingBoundaries(self)
Get the boundary of the surronding loop plus any boundaries of the innerNestedRings.
+ +
transferClosestFillLoops(self, extrusionHalfWidth, oldOrderedLocation, skein, threadSequence)
Transfer closest fill loops.
+ +
transferInfillPaths(self, extrusionHalfWidth, oldOrderedLocation, skein, threadSequence)
Transfer the infill paths.
+ +
transferPaths(self, paths)
Transfer paths.
+ +
+Methods inherited from NestedRing:
+
addFlattenedNestedRings(self, flattenedNestedRings)
Add flattened nested rings.
+ +
getFromInsideSurroundings(self, inputSurroundingInsides)
Initialize from inside nested rings.
+ +

+ + + + + + + +
 
+class NestedRing
   A nested ring.
 
 Methods defined here:
+
__init__(self)
Initialize.
+ +
__repr__(self)
Get the string representation of this nested ring.
+ +
addFlattenedNestedRings(self, flattenedNestedRings)
Add flattened nested rings.
+ +
getFromInsideSurroundings(self, inputSurroundingInsides)
Initialize from inside nested rings.
+ +

+ + + + + + + +
 
+class PathZ
   Complex path with a z.
 
 Methods defined here:
+
__init__(self, z)
+ +
__repr__(self)
Get the string representation of this path z.
+ +

+ + + + + + + +
 
+class ProjectiveSpace
   Class to define a projective space.
 
 Methods defined here:
+
__init__(self, basisX=(1.0, 0.0, 0.0), basisY=(0.0, 1.0, 0.0), basisZ=(0.0, 0.0, 1.0))
Initialize the basis vectors.
+ +
__repr__(self)
Get the string representation of this ProjectivePlane.
+ +
getByBasisXZ(self, basisX, basisZ)
Get by x basis x and y basis.
+ +
getByBasisZFirst(self, basisZ, firstVector3)
Get by basisZ and first.
+ +
getByBasisZTop(self, basisZ, top)
Get by basisZ and top.
+ +
getByLatitudeLongitude(self, viewpointLatitude, viewpointLongitude)
Get by latitude and longitude.
+ +
getByTilt(self, tilt)
Get by latitude and longitude.
+ +
getComplexByComplex(self, pointComplex)
Get complex by complex point.
+ +
getCopy(self)
Get copy.
+ +
getDotComplex(self, point)
Get the dot complex.
+ +
getDotVector3(self, point)
Get the dot vector3.
+ +
getNextSpace(self, nextNormal)
Get next space by next normal.
+ +
getSpaceByXYScaleAngle(self, angle, scale)
Get space by angle and scale.
+ +
getVector3ByPoint(self, point)
Get vector3 by point.
+ +
normalize(self)
Normalize.
+ +
unbuckle(self, maximumUnbuckling, normal)
Unbuckle space.
+ +

+ + + + + + + +
 
+class XIntersectionIndex
   A class to hold the x intersection position and the index of the loop which intersected.
 
 Methods defined here:
+
__cmp__(self, other)
Get comparison in order to sort x intersections in ascending order of x.
+ +
__eq__(self, other)
Determine whether this XIntersectionIndex is identical to other one.
+ +
__init__(self, index, x)
Initialize.
+ +
__ne__(self, other)
Determine whether this XIntersectionIndex is not identical to other one.
+ +
__repr__(self)
Get the string representation of this x intersection.
+ +

+ + + + + +
 
+Functions
       
addElementToListDictionary(element, key, listDictionary)
Add an element to the list table.
+
addElementToListDictionaryIfNotThere(element, key, listDictionary)
Add the value to the lists.
+
addElementToPixelList(element, pixelDictionary, x, y)
Add an element to the pixel list.
+
addElementToPixelListFromPoint(element, pixelDictionary, point)
Add an element to the pixel list.
+
addHorizontallyBoundedPoint(begin, center, end, horizontalBegin, horizontalEnd, path)
Add point if it is within the horizontal bounds.
+
addListToListTable(elementList, key, listDictionary)
Add a list to the list table.
+
addLoopToPixelTable(loop, pixelDictionary, width)
Add loop to the pixel table.
+
addNestedRingBeginning(distanceFeedRate, loop, z)
Add nested ring beginning to gcode output.
+
addPathToPixelTable(path, pixelDictionary, value, width)
Add path to the pixel table.
+
addPixelTableToPixelTable(fromPixelTable, intoPixelTable)
Add from pixel table to the into pixel table.
+
addPixelToPixelTableWithSteepness(isSteep, pixelDictionary, value, x, y)
Add pixels to the pixel table with steepness.
+
addPointToPath(path, pixelDictionary, point, value, width)
Add a point to a path and the pixel table.
+
addSegmentToPixelTable(beginComplex, endComplex, pixelDictionary, shortenDistanceBegin, shortenDistanceEnd, width)
Add line segment to the pixel table.
+
addSquareTwoToPixelDictionary(pixelDictionary, point, value, width)
Add square with two pixels around the center to pixel dictionary.
+
addToThreadsFromLoop(extrusionHalfWidth, gcodeType, loop, oldOrderedLocation, skein)
Add to threads from the last location from loop.
+
addToThreadsRemove(extrusionHalfWidth, nestedRings, oldOrderedLocation, skein, threadSequence)
Add to threads from the last location from nested rings.
+
addValueSegmentToPixelTable(beginComplex, endComplex, pixelDictionary, value, width)
Add line segment to the pixel table.
+
addValueToOutput(depth, keyInput, output, value)
Add value to the output.
+
addXIntersectionIndexesFromLoopListsY(loopLists, xIntersectionIndexList, y)
Add the x intersection indexes for the loop lists.
+
addXIntersectionIndexesFromLoopY(loop, solidIndex, xIntersectionIndexList, y)
Add the x intersection indexes for a loop.
+
addXIntersectionIndexesFromLoopsY(loops, solidIndex, xIntersectionIndexList, y)
Add the x intersection indexes for the loops.
+
addXIntersectionIndexesFromSegment(index, segment, xIntersectionIndexList)
Add the x intersection indexes from the segment.
+
addXIntersectionIndexesFromSegments(index, segments, xIntersectionIndexList)
Add the x intersection indexes from the segments.
+
addXIntersectionIndexesFromXIntersections(index, xIntersectionIndexList, xIntersections)
Add the x intersection indexes from the XIntersections.
+
addXIntersections(loop, xIntersections, y)
Add the x intersections for a loop.
+
addXIntersectionsFromLoopForTable(loop, xIntersectionsTable, width)
Add the x intersections for a loop into a table.
+
addXIntersectionsFromLoops(loops, xIntersections, y)
Add the x intersections for the loops.
+
addXIntersectionsFromLoopsForTable(loops, xIntersectionsTable, width)
Add the x intersections for a loop into a table.
+
compareSegmentLength(endpoint, otherEndpoint)
Get comparison in order to sort endpoints in ascending order of segment length.
+
concatenateRemovePath(connectedPaths, pathIndex, paths, pixelDictionary, segments, width)
Get connected paths from paths.
+
getAngleAroundZAxisDifference(subtractFromVec3, subtractVec3)
Get the angle around the Z axis difference between a pair of Vector3s.
+
getAngleDifferenceByComplex(subtractFromComplex, subtractComplex)
Get the angle between a pair of normalized complexes.
+
getAreaLoop(loop)
Get the area of a complex polygon.
+
getAreaLoopAbsolute(loop)
Get the absolute area of a complex polygon.
+
getAreaLoops(loops)
Get the area of a list of complex polygons.
+
getAreaVector3LoopAbsolute(loop)
Get the absolute area of a vector3 polygon.
+
getAroundLoop(begin, end, loop)
Get an arc around a loop.
+
getAwayPath(path, radius)
Get a path with only the points that are far enough away from each other, except for the last point.
+
getAwayPoints(points, radius)
Get a path with only the points that are far enough away from each other.
+
getBooleanFromDictionary(defaultBoolean, dictionary, key)
Get boolean from the dictionary and key.
+
getBooleanFromValue(value)
Get boolean from the word.
+
getBottomByPath(path)
Get the bottom of the path.
+
getBottomByPaths(paths)
Get the bottom of the paths.
+
getClippedAtEndLoopPath(clip, loopPath)
Get a clipped loop path.
+
getClippedLoopPath(clip, loopPath)
Get a clipped loop path.
+
getClippedSimplifiedLoopPath(clip, loopPath, radius)
Get a clipped and simplified loop path.
+
getClosestDistanceIndexToLine(point, loop)
Get the distance squared to the closest segment of the loop and index of that segment.
+
getClosestPointOnSegment(segmentBegin, segmentEnd, point)
Get the closest point on the segment.
+
getComplexByCommaString(valueCommaString)
Get the commaString as a complex.
+
getComplexByWords(words, wordIndex=0)
Get the complex by the first two words.
+
getComplexDefaultByDictionary(defaultComplex, dictionary, key)
Get the value as a complex.
+
getComplexDefaultByDictionaryKeys(defaultComplex, dictionary, keyX, keyY)
Get the value as a complex.
+
getComplexPath(vector3Path)
Get the complex path from the vector3 path.
+
getComplexPathByMultiplier(multiplier, path)
Get the multiplied complex path.
+
getComplexPaths(vector3Paths)
Get the complex paths from the vector3 paths.
+
getComplexPolygon(center, radius, sides, startAngle=0.0)
Get the complex polygon.
+
getComplexPolygonByComplexRadius(radius, sides, startAngle=0.0)
Get the complex polygon.
+
getComplexPolygonByStartEnd(endAngle, radius, sides, startAngle=0.0)
Get the complex polygon by start and end angle.
+
getConcatenatedList(originalLists)
Get the lists as one concatenated list.
+
getConnectedPaths(paths, pixelDictionary, width)
Get connected paths from paths.
+
getCrossProduct(firstComplex, secondComplex)
Get z component cross product of a pair of complexes.
+
getDecimalPlacesCarried(extraDecimalPlaces, value)
Get decimal places carried by the decimal places of the value plus the extraDecimalPlaces.
+
getDiagonalFlippedLoop(loop)
Get loop flipped over the dialogonal, in other words with the x and y swapped.
+
getDiagonalFlippedLoops(loops)
Get loops flipped over the dialogonal, in other words with the x and y swapped.
+
getDictionaryString(dictionary)
Get the dictionary string.
+
getDistanceToLine(begin, end, point)
Get the distance from a vector3 point to an infinite line.
+
getDistanceToLineByPath(begin, end, path)
Get the maximum distance from a path to an infinite line.
+
getDistanceToLineByPaths(begin, end, paths)
Get the maximum distance from paths to an infinite line.
+
getDistanceToPlaneSegment(segmentBegin, segmentEnd, point)
Get the distance squared from a point to the x & y components of a segment.
+
getDotProduct(firstComplex, secondComplex)
Get the dot product of a pair of complexes.
+
getDotProductPlusOne(firstComplex, secondComplex)
Get the dot product plus one of the x and y components of a pair of Vector3s.
+
getDurationString(seconds)
Get the duration string.
+
getEndpointFromPath(path, pathIndex)
Get endpoint segment from a path.
+
getEndpointsFromSegmentTable(segmentTable)
Get the endpoints from the segment table.
+
getEndpointsFromSegments(segments)
Get endpoints from segments.
+
getEnumeratorKeys(enumerator, keys)
Get enumerator keys.
+
getEnumeratorKeysAlwaysList(enumerator, keys)
Get enumerator keys.
+
getEnumeratorKeysExceptForOneArgument(enumerator, keys)
Get enumerator keys, except when there is one argument.
+
getFillOfSurroundings(nestedRings, penultimateFillLoops)
Get extra fill loops of nested rings.
+
getFlattenedNestedRings(nestedRings)
Get flattened nested rings.
+
getFloatDefaultByDictionary(defaultFloat, dictionary, key)
Get the value as a float.
+
getFloatFromValue(value)
Get the value as a float.
+
getFourSignificantFigures(number)
Get number rounded to four significant figures as a string.
+
getHalfSimplifiedLoop(loop, radius, remainder)
Get the loop with half of the points inside the channel removed.
+
getHalfSimplifiedPath(path, radius, remainder)
Get the path with half of the points inside the channel removed.
+
getHorizontallyBoundedPath(horizontalBegin, horizontalEnd, path)
Get horizontally bounded path.
+
getIncrementFromRank(rank)
Get the increment from the rank which is 0 at 1 and increases by three every power of ten.
+
getInsidesAddToOutsides(loops, outsides)
Add loops to either the insides or outsides.
+
getIntFromValue(value)
Get the value as an int.
+
getIntermediateLocation(alongWay, begin, end)
Get the intermediate location between begin and end.
+
getIntersectionOfXIntersectionIndexes(totalSolidSurfaceThickness, xIntersectionIndexList)
Get x intersections from surrounding layers.
+
getIntersectionOfXIntersectionsTables(xIntersectionsTables)
Get the intersection of the XIntersections tables.
+
getIsInFilledRegion(loops, point)
Determine if the point is in the filled region of the loops.
+
getIsInFilledRegionByPaths(loops, paths)
Determine if the point of any path is in the filled region of the loops.
+
getIsRadianClose(firstRadian, secondRadian)
Determine if the firstRadian is close to the secondRadian.
+
getIsWiddershinsByVector3(polygon)
Determine if the polygon goes round in the widdershins direction.
+
getJoinOfXIntersectionIndexes(xIntersectionIndexList)
Get joined x intersections from surrounding layers.
+
getLargestLoop(loops)
Get largest loop from loops.
+
getLeftPoint(points)
Get the leftmost complex point in the points.
+
getLeftPointIndex(points)
Get the index of the leftmost complex point in the points.
+
getListTableElements(listDictionary)
Get all the element in a list table.
+
getLoopCentroid(polygonComplex)
Get the area of a complex polygon using http://en.wikipedia.org/wiki/Centroid.
+
getLoopConvex(points)
Get convex hull of points using gift wrap algorithm.
+
getLoopConvexCentroid(polygonComplex)
Get centroid of the convex hull of a complex polygon.
+
getLoopInsideContainingLoop(containingLoop, loops)
Get a loop that is inside the containing loop.
+
getLoopLength(polygon)
Get the length of a polygon perimeter.
+
getLoopStartingClosest(extrusionHalfWidth, location, loop)
Add to threads from the last location from loop.
+
getLoopWithoutCloseEnds(close, loop)
Get loop without close ends.
+
getLoopWithoutCloseSequentialPoints(close, loop)
Get loop without close sequential points.
+
getMaximum(firstComplex, secondComplex)
Get a complex with each component the maximum of the respective components of a pair of complexes.
+
getMaximumByComplexPath(path)
Get a complex with each component the maximum of the respective components of a complex path.
+
getMaximumByComplexPaths(paths)
Get a complex with each component the maximum of the respective components of complex paths.
+
getMaximumByVector3Path(path)
Get a vector3 with each component the maximum of the respective components of a vector3 path.
+
getMaximumByVector3Paths(paths)
Get a complex with each component the maximum of the respective components of a complex path.
+
getMaximumSpan(loop)
Get the maximum span of the loop.
+
getMinimum(firstComplex, secondComplex)
Get a complex with each component the minimum of the respective components of a pair of complexes.
+
getMinimumByComplexPath(path)
Get a complex with each component the minimum of the respective components of a complex path.
+
getMinimumByComplexPaths(paths)
Get a complex with each component the minimum of the respective components of complex paths.
+
getMinimumByVector3Path(path)
Get a vector3 with each component the minimum of the respective components of a vector3 path.
+
getMinimumByVector3Paths(paths)
Get a complex with each component the minimum of the respective components of a complex path.
+
getMirrorPath(path)
Get mirror path.
+
getNormal(begin, center, end)
Get normal.
+
getNormalByPath(path)
Get normal by path.
+
getNormalWeighted(begin, center, end)
Get weighted normal.
+
getNormalized(complexNumber)
Get the normalized complex.
+
getNumberOfIntersectionsToLeft(loop, point)
Get the number of intersections through the loop for the line going left.
+
getNumberOfIntersectionsToLeftOfLoops(loops, point)
Get the number of intersections through the loop for the line starting from the left point and going left.
+
getOrderedNestedRings(nestedRings)
Get ordered nestedRings from nestedRings.
+
getPathCopy(path)
Get path copy.
+
getPathLength(path)
Get the length of a path ( an open polyline ).
+
getPathsFromEndpoints(endpoints, maximumConnectionLength, pixelDictionary, width)
Get paths from endpoints.
+
getPlaneDot(vec3First, vec3Second)
Get the dot product of the x and y components of a pair of Vector3s.
+
getPluralString(number, suffix)
Get the plural string.
+
getPointPlusSegmentWithLength(length, point, segment)
Get point plus a segment scaled to a given length.
+
getPointsByHorizontalDictionary(width, xIntersectionsDictionary)
Get points from the horizontalXIntersectionsDictionary.
+
getPointsByVerticalDictionary(width, xIntersectionsDictionary)
Get points from the verticalXIntersectionsDictionary.
+
getRadiusArealizedMultiplier(sides)
Get the radius multiplier for a polygon of equal area.
+
getRandomComplex(begin, end)
Get random complex.
+
getRank(width)
Get the rank which is 0 at 1 and increases by three every power of ten.
+
getRotatedComplexLists(planeAngle, pointLists)
Get point lists rotated by the plane angle
+
getRotatedComplexes(planeAngle, points)
Get points rotated by the plane angle
+
getRotatedWiddershinsQuarterAroundZAxis(vector3)
Get Vector3 rotated a quarter widdershins turn around Z axis.
+
getRoundZAxisByPlaneAngle(planeAngle, vector3)
Get Vector3 rotated by a plane angle.
+
getRoundedPoint(point)
Get point with each component rounded.
+
getRoundedToPlaces(decimalPlaces, number)
Get number rounded to a number of decimal places.
+
getRoundedToPlacesString(decimalPlaces, number)
Get number rounded to a number of decimal places as a string, without exponential formatting.
+
getRoundedToThreePlaces(number)
Get number rounded to three places as a string.
+
getSegmentFromPath(path, pathIndex)
Get endpoint segment from a path.
+
getSegmentFromPoints(begin, end)
Get endpoint segment from a pair of points.
+
getSegmentsFromXIntersectionIndexes(xIntersectionIndexList, y)
Get endpoint segments from the x intersection indexes.
+
getSegmentsFromXIntersections(xIntersections, y)
Get endpoint segments from the x intersections.
+
getSimplifiedLoop(loop, radius)
Get loop with points inside the channel removed.
+
getSimplifiedLoops(loops, radius)
Get the simplified loops.
+
getSimplifiedPath(path, radius)
Get path with points inside the channel removed.
+
getSquareIsOccupied(pixelDictionary, x, y)
Determine if a square around the x and y pixel coordinates is occupied.
+
getSquareLoopWiddershins(beginComplex, endComplex)
Get a square loop from the beginning to the end and back.
+
getSquareValues(pixelDictionary, x, y)
Get a list of the values in a square around the x and y pixel coordinates.
+
getSquareValuesFromPoint(pixelDictionary, point)
Get a list of the values in a square around the point.
+
getStepKeyFromPoint(point)
Get step key for the point.
+
getThreeSignificantFigures(number)
Get number rounded to three significant figures as a string.
+
getTopPath(path)
Get the top of the path.
+
getTopPaths(paths)
Get the top of the paths.
+
getTransferClosestNestedRing(extrusionHalfWidth, nestedRings, oldOrderedLocation, skein, threadSequence)
Get and transfer the closest remaining nested ring.
+
getTransferredNestedRings(insides, loop)
Get transferred paths from inside nested rings.
+
getTransferredPaths(insides, loop)
Get transferred paths from inside paths.
+
getTranslatedComplexPath(path, translateComplex)
Get the translated complex path.
+
getVector3Path(complexPath, z=0.0)
Get the vector3 path from the complex path.
+
getVector3Paths(complexPaths, z=0.0)
Get the vector3 paths from the complex paths.
+
getWiddershinsUnitPolar(angle)
Get polar complex from counterclockwise angle from 1, 0.
+
getXIntersectionIfExists(beginComplex, endComplex, y)
Get the x intersection if it exists.
+
getXIntersectionsFromIntersections(xIntersectionIndexList)
Get x intersections from the x intersection index list, in other words subtract non negative intersections from negatives.
+
getXYComplexFromVector3(vector3)
Get an xy complex from a vector3 if it exists, otherwise return None.
+
getYIntersectionIfExists(beginComplex, endComplex, x)
Get the y intersection if it exists.
+
getZComponentCrossProduct(vec3First, vec3Second)
Get z component cross product of a pair of Vector3s.
+
isInsideOtherLoops(loopIndex, loops)
Determine if a loop in a list is inside another loop in that list.
+
isLineIntersectingInsideXSegment(beginComplex, endComplex, segmentFirstX, segmentSecondX, y)
Determine if the line is crossing inside the x segment.
+
isLineIntersectingLoop(loop, pointBegin, pointEnd)
Determine if the line is intersecting loops.
+
isLineIntersectingLoops(loops, pointBegin, pointEnd)
Determine if the line is intersecting loops.
+
isLoopIntersectingInsideXSegment(loop, segmentFirstX, segmentSecondX, segmentYMirror, y)
Determine if the loop is intersecting inside the x segment.
+
isLoopIntersectingLoop(loop, otherLoop)
Determine if the loop is intersecting the other loop.
+
isLoopIntersectingLoops(loop, otherLoops)
Determine if the loop is intersecting other loops.
+
isLoopListIntersecting(loops)
Determine if a loop in the list is intersecting the other loops.
+
isLoopListIntersectingInsideXSegment(loopList, segmentFirstX, segmentSecondX, segmentYMirror, y)
Determine if the loop list is crossing inside the x segment.
+
isPathEntirelyInsideLoop(loop, path)
Determine if a path is entirely inside another loop.
+
isPathEntirelyInsideLoops(loops, path)
Determine if a path is entirely inside another loop in a list.
+
isPathInsideLoop(loop, path)
Determine if a path is inside another loop.
+
isPathInsideLoops(loops, path)
Determine if a path is inside another loop in a list.
+
isPixelTableIntersecting(bigTable, littleTable, maskTable={})
Add path to the pixel table.
+
isPointInsideLoop(loop, point)
Determine if a point is inside another loop.
+
isSegmentCompletelyInX(segment, xFirst, xSecond)
Determine if the segment overlaps within x.
+
isWiddershins(polygonComplex)
Determine if the complex polygon goes round in the widdershins direction.
+
isWithinChannel(channelRadius, pointIndex, loop)
Determine if the the point is within the channel between two adjacent points.
+
isXSegmentIntersectingPath(path, segmentFirstX, segmentSecondX, segmentYMirror, y)
Determine if a path is crossing inside the x segment.
+
isXSegmentIntersectingPaths(paths, segmentFirstX, segmentSecondX, segmentYMirror, y)
Determine if a path list is crossing inside the x segment.
+
joinSegmentTables(fromTable, intoTable)
Join both segment tables and put the join into the intoTable.
+
joinXIntersectionsTables(fromTable, intoTable)
Join both XIntersections tables and put the join into the intoTable.
+
overwriteDictionary(fromDictionary, keys, toDictionary)
Overwrite the dictionary.
+
removeElementFromDictionary(dictionary, key)
Remove element from the dictionary.
+
removeElementFromListTable(element, key, listDictionary)
Remove an element from the list table.
+
removeElementFromPixelListFromPoint(element, pixelDictionary, point)
Remove an element from the pixel list.
+
removeElementsFromDictionary(dictionary, keys)
Remove list from the dictionary.
+
removePixelTableFromPixelTable(pixelDictionaryToBeRemoved, pixelDictionaryToBeRemovedFrom)
Remove pixel from the pixel table.
+
removePrefixFromDictionary(dictionary, prefix)
Remove the attributes starting with the prefix from the dictionary.
+
removeTrueFromDictionary(dictionary, key)
Remove key from the dictionary in the value is true.
+
removeTrueListFromDictionary(dictionary, keys)
Remove list from the dictionary in the value is true.
+
subtractXIntersectionsTable(subtractFromTable, subtractTable)
Subtract the subtractTable from the subtractFromTable.
+
swapList(elements, indexBegin, indexEnd)
Swap the list elements.
+
toggleHashtable(hashtable, key, value)
Toggle a hashtable between having and not having a key.
+
transferClosestFillLoop(extrusionHalfWidth, oldOrderedLocation, remainingFillLoops, skein)
Transfer the closest remaining fill loop.
+
transferClosestPath(oldOrderedLocation, remainingPaths, skein)
Transfer the closest remaining path.
+
transferClosestPaths(oldOrderedLocation, remainingPaths, skein)
Transfer the closest remaining paths.
+
transferPathsToNestedRings(nestedRings, paths)
Transfer paths to nested rings.
+
translateVector3Path(path, translateVector3)
Translate the vector3 path.
+
translateVector3Paths(paths, translateVector3)
Translate the vector3 paths.
+
unbuckleBasis(basis, maximumUnbuckling, normal)
Unbuckle space.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalGoldenAngle = 3.883222077450933
+globalGoldenRatio = 1.618033988749895
+globalTau = 6.283185307179586

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.alphabetize.html b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.alphabetize.html new file mode 100644 index 0000000..ac11656 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.alphabetize.html @@ -0,0 +1,121 @@ + + +Python: module fabmetheus_utilities.fabmetheus_tools.alphabetize + + + + +
 
+ 
fabmetheus_utilities.fabmetheus_tools.alphabetize ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/fabmetheus_tools/alphabetize.py
+

Alphabetize is a script to alphabetize functions and signatures.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.archive
+
cStringIO
+
os
+

+ + + + + +
 
+Classes
       
+
EndCharacterMonad +
ParameterMonad +
Snippet +
+

+ + + + + + + +
 
+class EndCharacterMonad
   A monad to return the parent monad when it encounters the end character.
 
 Methods defined here:
+
__init__(self, endCharacter, parentMonad)
Initialize.
+ +
getNextMonad(self, character)
Get the next monad.
+ +
getSnippet(self)
Get the snippet.
+ +

+ + + + + + + +
 
+class ParameterMonad
   A monad to handle parameters.
 
 Methods defined here:
+
__init__(self, snippet)
Initialize.
+ +
addParameter(self)
Add parameter to the snippet.
+ +
getNextMonad(self, character)
Get the next monad.
+ +
getSnippet(self)
Get the snippet.
+ +

+ + + + + + + +
 
+class Snippet
   A class to get the variables for a function.
 
 Methods defined here:
+
__init__(self, characterIndex, fileText)
Initialize.
+ +
__repr__(self)
Get the string representation of this Snippet.
+ +

+ + + + + +
 
+Functions
       
addTogetherList(functionList, togetherLists)
Add the togetherList to the togetherLists is the sorted is different.
+
compareFunctionName(first, second)
Compare the function names.
+
getConvertedName(name)
Get converted name with init at the beginning and main at the endCompare the function names.
+
getFunctionLists(fileName)
Get the function lists in the file.
+
getFunctionsWithStringByFileName(fileName, searchString)
Get the functions with the search string in the file.
+
getFunctionsWithStringByFileNames(fileNames, searchString)
Get the functions with the search string in the files.
+
getParameterSequence(functionName)
Get the parameter sequence.
+
getSnippetsByFileName(fileName, functionName)
Get the function signature snippets by the file name.
+
getTogetherLists(fileName)
Get the lists of the unsorted and sorted functions in the file.
+
getTokenEnd(characterIndex, fileText, token)
Get the token end index for the file text and token.
+
main()
Run main function.
+
printTogetherListsByFileNames(fileNames)
Print the together lists of the file names, if the file name has a together list.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret.html b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret.html new file mode 100644 index 0000000..d9225ab --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret.html @@ -0,0 +1,87 @@ + + +Python: module fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret + + + + +
 
+ 
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/fabmetheus_tools/fabmetheus_interpret.py
+

Fabmetheus interpret is a fabmetheus utility to interpret a file, turning it into fabmetheus constructive solid geometry xml.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.gcodec
+os
+fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+time
+

+ + + + + +
 
+Classes
       
+
InterpretRepository +
+

+ + + + + + + +
 
+class InterpretRepository
   A class to handle the interpret settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Write button has been clicked.
+ +

+ + + + + +
 
+Functions
       
getCarving(fileName)
Get carving.
+
getGNUTranslatorFilesUnmodified()
Get the file types from the translators in the import plugins folder.
+
getGNUTranslatorGcodeFileTypeTuples()
Get the file type tuples from the translators in the import plugins folder plus gcode.
+
getImportPluginFileNames()
Get interpret plugin fileNames.
+
getInterpretPlugin(fileName)
Get the interpret plugin for the file.
+
getNewRepository()
Get new repository.
+
getPluginsDirectoryPath()
Get the plugins directory path.
+
getTranslatorFileTypeTuples()
Get the file types from the translators in the import plugins folder.
+
getWindowAnalyzeFile(fileName)
Get file interpretion.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.html b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.html new file mode 100644 index 0000000..e92df5d --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.html @@ -0,0 +1,35 @@ + + +Python: package fabmetheus_utilities.fabmetheus_tools + + + + +
 
+ 
fabmetheus_utilities.fabmetheus_tools
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/fabmetheus_tools/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
alphabetize
+fabmetheus_interpret
+
interpret_plugins (package)
+prepare
+
wikifier
+

+ + + + + +
 
+Data
       level = 2
+numberOfLevelsDeepInPackageHierarchy = 2
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.gts.html b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.gts.html new file mode 100644 index 0000000..523579b --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.gts.html @@ -0,0 +1,86 @@ + + +Python: module fabmetheus_utilities.fabmetheus_tools.interpret_plugins.gts + + + + +
 
+ 
fabmetheus_utilities.fabmetheus_tools.interpret_plugins.gts ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/gts.py
+

+Previous / Next / 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."
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+
fabmetheus_utilities.geometry.geometry_tools.face
+fabmetheus_utilities.gcodec
+
fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Functions
       
getCarving(fileName)
Get the carving for the gts file.
+
getFromGNUTriangulatedSurfaceText(gnuTriangulatedSurfaceText, triangleMesh)
Initialize from a GNU Triangulated Surface Text.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://hydraraptor.blogspot.com/>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.html b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.html new file mode 100644 index 0000000..1fedc78 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.html @@ -0,0 +1,49 @@ + + +Python: package fabmetheus_utilities.fabmetheus_tools.interpret_plugins + + + + +
 
+ 
fabmetheus_utilities.fabmetheus_tools.interpret_plugins
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/__init__.py
+

+Previous / Next / Contents +

+


+This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Package Contents
       
csv
+gts
+
obj
+slc
+
stl
+svg
+
xml
+xml_plugins (package)
+

+ + + + + +
 
+Data
       level = 3
+numberOfLevelsDeepInPackageHierarchy = 3
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.obj.html b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.obj.html new file mode 100644 index 0000000..d187c2a --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.obj.html @@ -0,0 +1,91 @@ + + +Python: module fabmetheus_utilities.fabmetheus_tools.interpret_plugins.obj + + + + +
 
+ 
fabmetheus_utilities.fabmetheus_tools.interpret_plugins.obj ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/obj.py
+

+Previous / Next / 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
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+
fabmetheus_utilities.geometry.geometry_tools.face
+fabmetheus_utilities.gcodec
+
fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Functions
       
addFacesGivenText(objText, triangleMesh)
Add faces given obj text.
+
getCarving(fileName='')
Get the triangle mesh for the obj file.
+
getFaceGivenLine(line, triangleMesh)
Add face given line index and lines.
+
getVertexGivenLine(line)
Get vertex given obj vertex line.
+
unpack(...)
Unpack the string containing packed C structure data, according to fmt.
+Requires len(string) == calcsize(fmt).
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://hydraraptor.blogspot.com/>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.slc.html b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.slc.html new file mode 100644 index 0000000..947314f --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.slc.html @@ -0,0 +1,150 @@ + + +Python: module fabmetheus_utilities.fabmetheus_tools.interpret_plugins.slc + + + + +
 
+ 
fabmetheus_utilities.fabmetheus_tools.interpret_plugins.slc ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/slc.py
+

+Previous / Next / Contents +

+


+The slc.py script is an import translator plugin to get a carving from an 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.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.gcodec
+math
+
fabmetheus_utilities.svg_writer
+sys
+

+ + + + + +
 
+Classes
       
+
SLCCarving +
SampleTableEntry +
+

+ + + + + + + +
 
+class SLCCarving
   An slc carving.
 
 Methods defined here:
+
__init__(self)
Add empty lists.
+ +
__repr__(self)
Get the string representation of this carving.
+ +
addXML(self, depth, output)
Add xml for this object.
+ +
getCarveBoundaryLayers(self)
Get the  boundary layers.
+ +
getCarveCornerMaximum(self)
Get the corner maximum of the vertexes.
+ +
getCarveCornerMinimum(self)
Get the corner minimum of the vertexes.
+ +
getCarveLayerHeight(self)
Get the layer height.
+ +
getCarvedSVG(self)
Get the carved svg text.
+ +
getFabmetheusXML(self)
Return the fabmetheus XML.
+ +
getInterpretationSuffix(self)
Return the suffix for a carving.
+ +
processContourLayers(self, file)
Process a contour layer at a time until the top of the part.
+ +
readFile(self, fileName)
Read SLC and store the layers.
+ +
readTableEntry(self, file)
Read in the sampling table section. It contains a table length (byte) and the table entries.
+ +
setCarveImportRadius(self, importRadius)
Set the import radius.
+ +
setCarveIsCorrectMesh(self, isCorrectMesh)
Set the is correct mesh flag.
+ +
setCarveLayerHeight(self, layerHeight)
Set the layer height.
+ +

+ + + + + + + +
 
+class SampleTableEntry
   Sample table entry.
 
 Methods defined here:
+
__init__(self, file)
Read in the sampling table section. It contains a table length (byte) and the table entries.
+ +
__repr__(self)
Get the string representation of this sample table entry.
+ +

+ + + + + +
 
+Functions
       
getCarving(fileName='')
Get the triangle mesh for the slc file.
+
getLittleEndianFloatGivenFile(file)
Get little endian float given a file.
+
getLittleEndianUnsignedLongGivenFile(file)
Get little endian float given a file.
+
getPointsFromFile(numPoints, file)
Process the vertice points for a given boundary.
+
main()
Display the inset dialog.
+
readHeader(file)
Read the slc header.
+
unpack(...)
Unpack the string containing packed C structure data, according to fmt.
+Requires len(string) == calcsize(fmt).
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://hydraraptor.blogspot.com/>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.stl.html b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.stl.html new file mode 100644 index 0000000..fbb89ae --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.stl.html @@ -0,0 +1,91 @@ + + +Python: module fabmetheus_utilities.fabmetheus_tools.interpret_plugins.stl + + + + +
 
+ 
fabmetheus_utilities.fabmetheus_tools.interpret_plugins.stl ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/stl.py
+

+Previous / Next / 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
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+
fabmetheus_utilities.geometry.geometry_tools.face
+fabmetheus_utilities.gcodec
+
fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Functions
       
addFacesGivenBinary(stlData, triangleMesh, vertexIndexTable)
Add faces given stl binary.
+
addFacesGivenText(stlText, triangleMesh, vertexIndexTable)
Add faces given stl text.
+
addFacesGivenVertexes(triangleMesh, vertexIndexTable, vertexes)
Add faces given stl text.
+
getCarving(fileName='')
Get the triangle mesh for the stl file.
+
getFaceGivenLines(triangleMesh, vertexStartIndex, vertexIndexTable, vertexes)
Add face given line index and lines.
+
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.
+
getFloatGivenBinary(byteIndex, stlData)
Get vertex given stl vertex line.
+
getVertexGivenBinary(byteIndex, stlData)
Get vertex given stl vertex line.
+
getVertexGivenLine(line)
Get vertex given stl vertex line.
+
unpack(...)
Unpack the string containing packed C structure data, according to fmt.
+Requires len(string) == calcsize(fmt).
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://hydraraptor.blogspot.com/>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.svg.html b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.svg.html new file mode 100644 index 0000000..2c9ce52 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.svg.html @@ -0,0 +1,127 @@ + + +Python: module fabmetheus_utilities.fabmetheus_tools.interpret_plugins.svg + + + + +
 
+ 
fabmetheus_utilities.fabmetheus_tools.interpret_plugins.svg ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/svg.py
+

+Previous / Next / 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.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+
fabmetheus_utilities.euclidean
+fabmetheus_utilities.gcodec
+
math
+fabmetheus_utilities.svg_writer
+
fabmetheus_utilities.xml_simple_writer
+

+ + + + + +
 
+Classes
       
+
SVGCarving +
+

+ + + + + + + +
 
+class SVGCarving
   An svg carving.
 
 Methods defined here:
+
__init__(self)
Add empty lists.
+ +
__repr__(self)
Get the string representation of this carving.
+ +
addXML(self, depth, output)
Add xml for this object.
+ +
getCarveBoundaryLayers(self)
Get the  boundary layers.
+ +
getCarveCornerMaximum(self)
Get the corner maximum of the vertexes.
+ +
getCarveCornerMinimum(self)
Get the corner minimum of the vertexes.
+ +
getCarveLayerHeight(self)
Get the layer height.
+ +
getCarvedSVG(self)
Get the carved svg text.
+ +
getFabmetheusXML(self)
Return the fabmetheus XML.
+ +
getInterpretationSuffix(self)
Return the suffix for a carving.
+ +
parseSVG(self, fileName, svgText)
Parse SVG text and store the layers.
+ +
setCarveImportRadius(self, importRadius)
Set the import radius.
+ +
setCarveIsCorrectMesh(self, isCorrectMesh)
Set the is correct mesh flag.
+ +
setCarveLayerHeight(self, layerHeight)
Set the layer height.
+ +

+ + + + + +
 
+Functions
       
getCarving(fileName='')
Get the triangle mesh for the gts file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://hydraraptor.blogspot.com/>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml.html b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml.html new file mode 100644 index 0000000..9d8aeba --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml.html @@ -0,0 +1,155 @@ + + +Python: module fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml + + + + +
 
+ 
fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/xml.py
+

+Previous / Next / Contents +

+


+The xml.py script is an import translator plugin to get a carving from an 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 example of an xml boolean geometry format file follows below.
+
+<?xml version='1.0' ?>
+<fabmetheus version="2010-03-29">
+ <difference id="cube_cylinder_difference">
+ <matrix m14="-10.0" m24="20.0" m34="5.0" />
+ <cube id="Cube 5" halfx="5.0" halfy="5.0" halfz="5.0">
+ </cube>
+ <cylinder id="Cylinder 5" height="10.0" radiusx="5.0" radiusy="5.0" topOverBottom="1.0">
+ <matrix m14="5.0" m24="-5.0" />
+ </cylinder>
+ </difference>
+</fabmetheus>
+
+In the 'fabmetheus' format, all class names are lower case. The defined geometric objects are cube, cylinder, difference, group, sphere, trianglemesh and union. The id attribute is not necessary. The default matrix is a four by four identity matrix. The attributes of the cube, cylinder and sphere default to one. The attributes of the vertexes in the triangle mesh default to zero. The boolean solids are difference, intersection and union. The difference solid is the first solid minus the remaining solids. The combined_shape.xml example in the xml_models folder in the models folder is pasted below.
+
+<?xml version='1.0' ?>
+<fabmetheus version="2010-03-29">
+ <difference id="cube_cylinder_difference">
+ <matrix m14="-10.0" m24="20.0" m34="5.0" />
+ <cube id="Cube 5" halfx="5.0" halfy="5.0" halfz="5.0">
+ </cube>
+ <cylinder id="Cylinder 5" height="10.0" radiusx="5.0" radiusy="5.0" topOverBottom="1.0">
+ <matrix m14="5.0" m24="-5.0" />
+ </cylinder>
+ </difference>
+ <intersection id="cube_cylinder_intersection">
+ <matrix m14="-10.0" m34="5.0" />
+ <cube id="Cube 5" halfx="5.0" halfy="5.0" halfz="5.0">
+ </cube>
+ <cylinder id="Cylinder 5" height="10.0" radiusx="5.0" radiusy="5.0" topOverBottom="1.0">
+ <matrix m14="5.0" m24="-5.0" />
+ </cylinder>
+ </intersection>
+ <union id="cube_cylinder_union">
+ <matrix m14="-10.0" m24="-20.0" m34="5.0" />
+ <cube id="Cube 5" halfx="5.0" halfy="5.0" halfz="5.0">
+ </cube>
+ <cylinder id="Cylinder 5" height="10.0" radiusx="5.0" radiusy="5.0" topOverBottom="1.0">
+ <matrix m14="5.0" m24="-5.0" />
+ </cylinder>
+ </union>
+ <group id="sphere_tetrahedron_group">
+ <matrix m14="10.0" m24="-20.0" m34="5.0" />
+ <sphere id="Group Sphere 5" radiusx="5.0" radiusy="5.0" radiusz="5.0">
+ </sphere>
+ <trianglemesh id="Group Tetrahedron 5">
+ <matrix m14="15.0" />
+ <vertex x="-5.0" y="-5.0" z="-5.0" />
+ <vertex x="5.0" y="-5.0" z="-5.0" />
+ <vertex y="5.0" z="-5.0" />
+ <vertex z="5.0" />
+ <face vertex0="0" vertex1="2" vertex2="1" />
+ <face vertex0="3" vertex1="1" vertex2="2" />
+ <face vertex0="3" vertex1="2" vertex2="0" />
+ <face vertex0="3" vertex1="0" vertex2="1" />
+ </trianglemesh>
+ </group>
+ <sphere id="Sphere 5" radiusx="5.0" radiusy="5.0" radiusz="5.0">
+ <matrix m14="10.0" m34="5.0" />
+ </sphere>
+ <trianglemesh id="Tetrahedron 5">
+ <matrix m14="10.0" m24="20.0" m34="5.0" />
+ <vertex x="-5.0" y="-5.0" z="-5.0" />
+ <vertex x="5.0" y="-5.0" z="-5.0" />
+ <vertex y="5.0" z="-5.0" />
+ <vertex z="5.0" />
+ <face vertex0="0" vertex1="2" vertex2="1" />
+ <face vertex0="3" vertex1="1" vertex2="2" />
+ <face vertex0="3" vertex1="2" vertex2="0" />
+ <face vertex0="3" vertex1="0" vertex2="1" />
+ </trianglemesh>
+</fabmetheus>
+
+The 'fabmetheus' xml format is the preferred skeinforge format. When the Interpret button in the Interpret tool in Analyze is clicked, any xml format for which there is a plugin will be converted to the 'fabmetheus' format.
+
+There is a plugin for the 'Art of Illusion' xml format. 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, the artofillusion 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.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+
fabmetheus_utilities.gcodec
+os
+
sys
+

+ + + + + +
 
+Functions
       
getCarving(fileName='')
Get the carving for the xml file.
+
getPluginsDirectoryPath()
Get the plugins directory path.
+
main()
Display the inset dialog.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://hydraraptor.blogspot.com/>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml_plugins.artofillusion.html b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml_plugins.artofillusion.html new file mode 100644 index 0000000..7e59e3a --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml_plugins.artofillusion.html @@ -0,0 +1,704 @@ + + +Python: module fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml_plugins.artofillusion + + + + +
 
+ 
fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml_plugins.artofillusion ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/xml_plugins/artofillusion.py
+

+Previous / Next / 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.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.geometry.geometry_utilities.boolean_geometry
+fabmetheus_utilities.geometry.geometry_utilities.boolean_solid
+
fabmetheus_utilities.geometry.solids.cube
+fabmetheus_utilities.geometry.solids.cylinder
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_tools.face
+fabmetheus_utilities.geometry.solids.group
+fabmetheus_utilities.geometry.geometry_utilities.matrix
+
fabmetheus_utilities.geometry.solids.sphere
+fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
fabmetheus_utilities.geometry.geometry_utilities.boolean_solid.BooleanSolid(fabmetheus_utilities.geometry.solids.group.Group) +
+
+
BooleanSolid +
+
+
fabmetheus_utilities.geometry.solids.cube.Cube(fabmetheus_utilities.geometry.solids.triangle_mesh.TriangleMesh) +
+
+
Cube +
+
+
fabmetheus_utilities.geometry.solids.cylinder.Cylinder(fabmetheus_utilities.geometry.solids.cube.Cube) +
+
+
Cylinder +
+
+
fabmetheus_utilities.geometry.solids.group.Group(fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary) +
+
+
Group +
+
+
fabmetheus_utilities.geometry.solids.sphere.Sphere(fabmetheus_utilities.geometry.solids.cube.Cube) +
+
+
Sphere +
+
+
fabmetheus_utilities.geometry.solids.triangle_mesh.TriangleMesh(fabmetheus_utilities.geometry.solids.group.Group) +
+
+
TriangleMesh +
+
+
+

+ + + + + + + +
 
+class BooleanSolid(fabmetheus_utilities.geometry.geometry_utilities.boolean_solid.BooleanSolid)
   An Art of Illusion CSG object info.
 
 
Method resolution order:
+
BooleanSolid
+
fabmetheus_utilities.geometry.geometry_utilities.boolean_solid.BooleanSolid
+
fabmetheus_utilities.geometry.solids.group.Group
+
fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary
+
+
+Methods defined here:
+
setToArtOfIllusionDictionary(self)
Set the shape of this carvable object info.
+ +
+Methods inherited from fabmetheus_utilities.geometry.geometry_utilities.boolean_solid.BooleanSolid:
+
getDifference(self, importRadius, visibleObjectLoopsList)
Get subtracted loops sliced through shape.
+ +
getIntersection(self, importRadius, visibleObjectLoopsList)
Get intersected loops sliced through shape.
+ +
getLoops(self, importRadius, z)
Get loops sliced through shape.
+ +
getLoopsFromObjectLoopsList(self, importRadius, visibleObjectLoopsList)
Get loops from visible object loops list.
+ +
getTransformedPaths(self)
Get all transformed paths.
+ +
getUnion(self, importRadius, visibleObjectLoopsList)
Get joined loops sliced through shape.
+ +
getXMLLocalName(self)
Get xml class name.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.group.Group:
+
__init__(self)
Add empty lists.
+ +
addXMLInnerSection(self, depth, output)
Add xml inner section for this object.
+ +
addXMLSection(self, depth, output)
Add the xml section for this object.
+ +
getMatrix4X4(self)
Get the matrix4X4.
+ +
getMatrixChainTetragrid(self)
Get the matrix chain tetragrid.
+ +
getVisible(self)
Get visible.
+ +
setToElementNode(self, elementNode)
Set to elementNode.
+ +
+Methods inherited from fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary:
+
__repr__(self)
Get the string representation of this object info.
+ +
addXML(self, depth, output)
Add xml for this object.
+ +
addXMLArchivableObjects(self, depth, output)
Add xml for this object.
+ +
createShape(self)
Create the shape.
+ +
getAttributes(self)
Get attribute table.
+ +
getComplexTransformedPathLists(self)
Get complex transformed path lists.
+ +
getFabricationExtension(self)
Get fabrication extension.
+ +
getFabricationText(self, addLayerTemplate)
Get fabrication text.
+ +
getGeometryOutput(self)
Get geometry output dictionary.
+ +
getMinimumZ(self)
Get the minimum z.
+ +
getPaths(self)
Get all paths.
+ +
getTransformedVertexes(self)
Get all transformed vertexes.
+ +
getTriangleMeshes(self)
Get all triangleMeshes.
+ +
getType(self)
Get type.
+ +
getVertexes(self)
Get all vertexes.
+ +
transformGeometryOutput(self, geometryOutput)
Transform the geometry output by the local matrix4x4.
+ +

+ + + + + + + +
 
+class Cube(fabmetheus_utilities.geometry.solids.cube.Cube)
   An Art of Illusion Cube object.
 
 
Method resolution order:
+
Cube
+
fabmetheus_utilities.geometry.solids.cube.Cube
+
fabmetheus_utilities.geometry.solids.triangle_mesh.TriangleMesh
+
fabmetheus_utilities.geometry.solids.group.Group
+
fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary
+
+
+Methods defined here:
+
setToArtOfIllusionDictionary(self)
Set the shape of this carvable object info.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.cube.Cube:
+
addXMLSection(self, depth, output)
Add the xml section for this object.
+ +
createShape(self)
Create the shape.
+ +
setToElementNode(self, elementNode)
Set to elementNode.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.triangle_mesh.TriangleMesh:
+
__init__(self)
Add empty lists.
+ +
getCarveBoundaryLayers(self)
Get the boundary layers.
+ +
getCarveCornerMaximum(self)
Get the corner maximum of the vertexes.
+ +
getCarveCornerMinimum(self)
Get the corner minimum of the vertexes.
+ +
getCarveLayerHeight(self)
Get the layer height.
+ +
getFabmetheusXML(self)
Return the fabmetheus XML.
+ +
getGeometryOutput(self)
Get geometry output dictionary.
+ +
getInterpretationSuffix(self)
Return the suffix for a triangle mesh.
+ +
getLoops(self, importRadius, z)
Get loops sliced through shape.
+ +
getLoopsFromMesh(self, z)
Get loops from a carve of a mesh.
+ +
getMinimumZ(self)
Get the minimum z.
+ +
getTransformedVertexes(self)
Get all transformed vertexes.
+ +
getTriangleMeshes(self)
Get all triangleMeshes.
+ +
getVertexes(self)
Get all vertexes.
+ +
setCarveImportRadius(self, importRadius)
Set the import radius.
+ +
setCarveIsCorrectMesh(self, isCorrectMesh)
Set the is correct mesh flag.
+ +
setCarveLayerHeight(self, layerHeight)
Set the layer height.
+ +
setEdgesForAllFaces(self)
Set the face edges of all the faces.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.group.Group:
+
addXMLInnerSection(self, depth, output)
Add xml inner section for this object.
+ +
getMatrix4X4(self)
Get the matrix4X4.
+ +
getMatrixChainTetragrid(self)
Get the matrix chain tetragrid.
+ +
getVisible(self)
Get visible.
+ +
+Methods inherited from fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary:
+
__repr__(self)
Get the string representation of this object info.
+ +
addXML(self, depth, output)
Add xml for this object.
+ +
addXMLArchivableObjects(self, depth, output)
Add xml for this object.
+ +
getAttributes(self)
Get attribute table.
+ +
getComplexTransformedPathLists(self)
Get complex transformed path lists.
+ +
getFabricationExtension(self)
Get fabrication extension.
+ +
getFabricationText(self, addLayerTemplate)
Get fabrication text.
+ +
getPaths(self)
Get all paths.
+ +
getTransformedPaths(self)
Get all transformed paths.
+ +
getType(self)
Get type.
+ +
getXMLLocalName(self)
Get xml local name.
+ +
transformGeometryOutput(self, geometryOutput)
Transform the geometry output by the local matrix4x4.
+ +

+ + + + + + + +
 
+class Cylinder(fabmetheus_utilities.geometry.solids.cylinder.Cylinder)
   An Art of Illusion Cylinder object.
 
 
Method resolution order:
+
Cylinder
+
fabmetheus_utilities.geometry.solids.cylinder.Cylinder
+
fabmetheus_utilities.geometry.solids.cube.Cube
+
fabmetheus_utilities.geometry.solids.triangle_mesh.TriangleMesh
+
fabmetheus_utilities.geometry.solids.group.Group
+
fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary
+
+
+Methods defined here:
+
setToArtOfIllusionDictionary(self)
Set the shape of this carvable object info.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.cylinder.Cylinder:
+
__init__(self)
Add empty lists.
+ +
createShape(self)
Create the shape.
+ +
setToElementNode(self, elementNode)
Set to elementNode.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.cube.Cube:
+
addXMLSection(self, depth, output)
Add the xml section for this object.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.triangle_mesh.TriangleMesh:
+
getCarveBoundaryLayers(self)
Get the boundary layers.
+ +
getCarveCornerMaximum(self)
Get the corner maximum of the vertexes.
+ +
getCarveCornerMinimum(self)
Get the corner minimum of the vertexes.
+ +
getCarveLayerHeight(self)
Get the layer height.
+ +
getFabmetheusXML(self)
Return the fabmetheus XML.
+ +
getGeometryOutput(self)
Get geometry output dictionary.
+ +
getInterpretationSuffix(self)
Return the suffix for a triangle mesh.
+ +
getLoops(self, importRadius, z)
Get loops sliced through shape.
+ +
getLoopsFromMesh(self, z)
Get loops from a carve of a mesh.
+ +
getMinimumZ(self)
Get the minimum z.
+ +
getTransformedVertexes(self)
Get all transformed vertexes.
+ +
getTriangleMeshes(self)
Get all triangleMeshes.
+ +
getVertexes(self)
Get all vertexes.
+ +
setCarveImportRadius(self, importRadius)
Set the import radius.
+ +
setCarveIsCorrectMesh(self, isCorrectMesh)
Set the is correct mesh flag.
+ +
setCarveLayerHeight(self, layerHeight)
Set the layer height.
+ +
setEdgesForAllFaces(self)
Set the face edges of all the faces.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.group.Group:
+
addXMLInnerSection(self, depth, output)
Add xml inner section for this object.
+ +
getMatrix4X4(self)
Get the matrix4X4.
+ +
getMatrixChainTetragrid(self)
Get the matrix chain tetragrid.
+ +
getVisible(self)
Get visible.
+ +
+Methods inherited from fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary:
+
__repr__(self)
Get the string representation of this object info.
+ +
addXML(self, depth, output)
Add xml for this object.
+ +
addXMLArchivableObjects(self, depth, output)
Add xml for this object.
+ +
getAttributes(self)
Get attribute table.
+ +
getComplexTransformedPathLists(self)
Get complex transformed path lists.
+ +
getFabricationExtension(self)
Get fabrication extension.
+ +
getFabricationText(self, addLayerTemplate)
Get fabrication text.
+ +
getPaths(self)
Get all paths.
+ +
getTransformedPaths(self)
Get all transformed paths.
+ +
getType(self)
Get type.
+ +
getXMLLocalName(self)
Get xml local name.
+ +
transformGeometryOutput(self, geometryOutput)
Transform the geometry output by the local matrix4x4.
+ +

+ + + + + + + +
 
+class Group(fabmetheus_utilities.geometry.solids.group.Group)
   An Art of Illusion Group object.
 
 
Method resolution order:
+
Group
+
fabmetheus_utilities.geometry.solids.group.Group
+
fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary
+
+
+Methods defined here:
+
setToArtOfIllusionDictionary(self)
Set the shape of this group.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.group.Group:
+
__init__(self)
Add empty lists.
+ +
addXMLInnerSection(self, depth, output)
Add xml inner section for this object.
+ +
addXMLSection(self, depth, output)
Add the xml section for this object.
+ +
getLoops(self, importRadius, z)
Get loops sliced through shape.
+ +
getMatrix4X4(self)
Get the matrix4X4.
+ +
getMatrixChainTetragrid(self)
Get the matrix chain tetragrid.
+ +
getVisible(self)
Get visible.
+ +
setToElementNode(self, elementNode)
Set to elementNode.
+ +
+Methods inherited from fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary:
+
__repr__(self)
Get the string representation of this object info.
+ +
addXML(self, depth, output)
Add xml for this object.
+ +
addXMLArchivableObjects(self, depth, output)
Add xml for this object.
+ +
createShape(self)
Create the shape.
+ +
getAttributes(self)
Get attribute table.
+ +
getComplexTransformedPathLists(self)
Get complex transformed path lists.
+ +
getFabricationExtension(self)
Get fabrication extension.
+ +
getFabricationText(self, addLayerTemplate)
Get fabrication text.
+ +
getGeometryOutput(self)
Get geometry output dictionary.
+ +
getMinimumZ(self)
Get the minimum z.
+ +
getPaths(self)
Get all paths.
+ +
getTransformedPaths(self)
Get all transformed paths.
+ +
getTransformedVertexes(self)
Get all transformed vertexes.
+ +
getTriangleMeshes(self)
Get all triangleMeshes.
+ +
getType(self)
Get type.
+ +
getVertexes(self)
Get all vertexes.
+ +
getXMLLocalName(self)
Get xml local name.
+ +
transformGeometryOutput(self, geometryOutput)
Transform the geometry output by the local matrix4x4.
+ +

+ + + + + + + +
 
+class Sphere(fabmetheus_utilities.geometry.solids.sphere.Sphere)
   An Art of Illusion Sphere object.
 
 
Method resolution order:
+
Sphere
+
fabmetheus_utilities.geometry.solids.sphere.Sphere
+
fabmetheus_utilities.geometry.solids.cube.Cube
+
fabmetheus_utilities.geometry.solids.triangle_mesh.TriangleMesh
+
fabmetheus_utilities.geometry.solids.group.Group
+
fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary
+
+
+Methods defined here:
+
setToArtOfIllusionDictionary(self)
Set the shape of this carvable object.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.sphere.Sphere:
+
createShape(self)
Create the shape.
+ +
setToElementNode(self, elementNode)
Set to elementNode.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.cube.Cube:
+
addXMLSection(self, depth, output)
Add the xml section for this object.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.triangle_mesh.TriangleMesh:
+
__init__(self)
Add empty lists.
+ +
getCarveBoundaryLayers(self)
Get the boundary layers.
+ +
getCarveCornerMaximum(self)
Get the corner maximum of the vertexes.
+ +
getCarveCornerMinimum(self)
Get the corner minimum of the vertexes.
+ +
getCarveLayerHeight(self)
Get the layer height.
+ +
getFabmetheusXML(self)
Return the fabmetheus XML.
+ +
getGeometryOutput(self)
Get geometry output dictionary.
+ +
getInterpretationSuffix(self)
Return the suffix for a triangle mesh.
+ +
getLoops(self, importRadius, z)
Get loops sliced through shape.
+ +
getLoopsFromMesh(self, z)
Get loops from a carve of a mesh.
+ +
getMinimumZ(self)
Get the minimum z.
+ +
getTransformedVertexes(self)
Get all transformed vertexes.
+ +
getTriangleMeshes(self)
Get all triangleMeshes.
+ +
getVertexes(self)
Get all vertexes.
+ +
setCarveImportRadius(self, importRadius)
Set the import radius.
+ +
setCarveIsCorrectMesh(self, isCorrectMesh)
Set the is correct mesh flag.
+ +
setCarveLayerHeight(self, layerHeight)
Set the layer height.
+ +
setEdgesForAllFaces(self)
Set the face edges of all the faces.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.group.Group:
+
addXMLInnerSection(self, depth, output)
Add xml inner section for this object.
+ +
getMatrix4X4(self)
Get the matrix4X4.
+ +
getMatrixChainTetragrid(self)
Get the matrix chain tetragrid.
+ +
getVisible(self)
Get visible.
+ +
+Methods inherited from fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary:
+
__repr__(self)
Get the string representation of this object info.
+ +
addXML(self, depth, output)
Add xml for this object.
+ +
addXMLArchivableObjects(self, depth, output)
Add xml for this object.
+ +
getAttributes(self)
Get attribute table.
+ +
getComplexTransformedPathLists(self)
Get complex transformed path lists.
+ +
getFabricationExtension(self)
Get fabrication extension.
+ +
getFabricationText(self, addLayerTemplate)
Get fabrication text.
+ +
getPaths(self)
Get all paths.
+ +
getTransformedPaths(self)
Get all transformed paths.
+ +
getType(self)
Get type.
+ +
getXMLLocalName(self)
Get xml local name.
+ +
transformGeometryOutput(self, geometryOutput)
Transform the geometry output by the local matrix4x4.
+ +

+ + + + + + + +
 
+class TriangleMesh(fabmetheus_utilities.geometry.solids.triangle_mesh.TriangleMesh)
   An Art of Illusion triangle mesh object.
 
 
Method resolution order:
+
TriangleMesh
+
fabmetheus_utilities.geometry.solids.triangle_mesh.TriangleMesh
+
fabmetheus_utilities.geometry.solids.group.Group
+
fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary
+
+
+Methods defined here:
+
setToArtOfIllusionDictionary(self)
Set the shape of this carvable object info.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.triangle_mesh.TriangleMesh:
+
__init__(self)
Add empty lists.
+ +
addXMLSection(self, depth, output)
Add the xml section for this object.
+ +
getCarveBoundaryLayers(self)
Get the boundary layers.
+ +
getCarveCornerMaximum(self)
Get the corner maximum of the vertexes.
+ +
getCarveCornerMinimum(self)
Get the corner minimum of the vertexes.
+ +
getCarveLayerHeight(self)
Get the layer height.
+ +
getFabmetheusXML(self)
Return the fabmetheus XML.
+ +
getGeometryOutput(self)
Get geometry output dictionary.
+ +
getInterpretationSuffix(self)
Return the suffix for a triangle mesh.
+ +
getLoops(self, importRadius, z)
Get loops sliced through shape.
+ +
getLoopsFromMesh(self, z)
Get loops from a carve of a mesh.
+ +
getMinimumZ(self)
Get the minimum z.
+ +
getTransformedVertexes(self)
Get all transformed vertexes.
+ +
getTriangleMeshes(self)
Get all triangleMeshes.
+ +
getVertexes(self)
Get all vertexes.
+ +
setCarveImportRadius(self, importRadius)
Set the import radius.
+ +
setCarveIsCorrectMesh(self, isCorrectMesh)
Set the is correct mesh flag.
+ +
setCarveLayerHeight(self, layerHeight)
Set the layer height.
+ +
setEdgesForAllFaces(self)
Set the face edges of all the faces.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.group.Group:
+
addXMLInnerSection(self, depth, output)
Add xml inner section for this object.
+ +
getMatrix4X4(self)
Get the matrix4X4.
+ +
getMatrixChainTetragrid(self)
Get the matrix chain tetragrid.
+ +
getVisible(self)
Get visible.
+ +
setToElementNode(self, elementNode)
Set to elementNode.
+ +
+Methods inherited from fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary:
+
__repr__(self)
Get the string representation of this object info.
+ +
addXML(self, depth, output)
Add xml for this object.
+ +
addXMLArchivableObjects(self, depth, output)
Add xml for this object.
+ +
createShape(self)
Create the shape.
+ +
getAttributes(self)
Get attribute table.
+ +
getComplexTransformedPathLists(self)
Get complex transformed path lists.
+ +
getFabricationExtension(self)
Get fabrication extension.
+ +
getFabricationText(self, addLayerTemplate)
Get fabrication text.
+ +
getPaths(self)
Get all paths.
+ +
getTransformedPaths(self)
Get all transformed paths.
+ +
getType(self)
Get type.
+ +
getXMLLocalName(self)
Get xml local name.
+ +
transformGeometryOutput(self, geometryOutput)
Transform the geometry output by the local matrix4x4.
+ +

+ + + + + +
 
+Functions
       
getCarvableObject(elementNode, globalObject, object)
Get new carvable object info.
+
getCarvingFromParser(xmlParser)
Get the carving for the parser.
+
getTransformElementNode(coords, transformName)
Get the transform attributes.
+
processAppendElementNode(archivableObjects, elementNode, parentNode)
Add the object info if it is carvable.
+
processElementNode(elementNode)
Process the xml element.
+
removeListArtOfIllusionFromDictionary(dictionary, scrubKeys)
Remove the list and art of illusion keys from the dictionary.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalCarvableClassObjectTable = {'CSGObject': <class fabmetheus_utilities.fabmetheus_tools.int...t_plugins.xml_plugins.artofillusion.BooleanSolid>, 'Cube': <class fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml_plugins.artofillusion.Cube>, 'Cylinder': <class fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml_plugins.artofillusion.Cylinder>, 'Sphere': <class fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml_plugins.artofillusion.Sphere>, 'TriangleMesh': <class fabmetheus_utilities.fabmetheus_tools.int...t_plugins.xml_plugins.artofillusion.TriangleMesh>, 'artofillusion.object.NullObject': <class fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml_plugins.artofillusion.Group>}

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://hydraraptor.blogspot.com/>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml_plugins.fabmetheus.html b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml_plugins.fabmetheus.html new file mode 100644 index 0000000..79037d2 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml_plugins.fabmetheus.html @@ -0,0 +1,117 @@ + + +Python: module fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml_plugins.fabmetheus + + + + +
 
+ 
fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml_plugins.fabmetheus ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/xml_plugins/fabmetheus.py
+

+Previous / Next / 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.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.geometry.geometry_utilities.boolean_geometry
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.geometry.solids.group
+
os
+fabmetheus_utilities.settings
+sys
+
traceback
+fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml
+fabmetheus_utilities.xml_simple_reader
+

+ + + + + +
 
+Classes
       
+
XMLBooleanGeometryProcessor +
+

+ + + + + + + +
 
+class XMLBooleanGeometryProcessor
   A class to process xml boolean geometry elements.
 
 Methods defined here:
+
__init__(self)
Initialize processor.
+ +
__repr__(self)
Get the string representation of this XMLBooleanGeometryProcessor.
+ +
convertElementNode(self, elementNode, geometryOutput)
Convert the xml element.
+ +
createChildNodes(self, geometryOutput, parentNode)
Create childNodes for the parentNode.
+ +
processChildNodes(self, elementNode)
Process the childNodes of the xml element.
+ +
processElementNode(self, elementNode)
Process the xml element.
+ +

+ + + + + +
 
+Functions
       
getCarvingFromParser(xmlParser)
Get the carving for the parser.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://hydraraptor.blogspot.com/>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml_plugins.html b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml_plugins.html new file mode 100644 index 0000000..11d6ff7 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml_plugins.html @@ -0,0 +1,32 @@ + + +Python: package fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml_plugins + + + + +
 
+ 
fabmetheus_utilities.fabmetheus_tools.interpret_plugins.xml_plugins
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/xml_plugins/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
artofillusion
+
fabmetheus
+

+ + + + + +
 
+Data
       level = 4
+numberOfLevelsDeepInPackageHierarchy = 4
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.prepare.html b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.prepare.html new file mode 100644 index 0000000..baa183b --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.prepare.html @@ -0,0 +1,57 @@ + + +Python: module fabmetheus_utilities.fabmetheus_tools.prepare + + + + +
 
+ 
fabmetheus_utilities.fabmetheus_tools.prepare ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/fabmetheus_tools/prepare.py
+

Prepare is a script to remove the generated files, run wikifier, and finally zip the package.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.archive
+
os
+
fabmetheus_utilities.fabmetheus_tools.wikifier
+

+ + + + + +
 
+Functions
       
main()
Run main function.
+
prepareWikify()
Remove generated files, then wikify the file comments.
+
removeCSVFile(csvFilePath)
Remove csv file.
+
removeGcodeFile(gcodeFilePath)
Remove gcode file.
+
removeGeneratedFiles()
Remove generated files.
+
removeSVGFile(svgFilePath)
Remove svg file.
+
removeXMLFile(xmlFilePath)
Remove xml file.
+
removeZip()
Remove the zip file, then generate a new one.zip -r reprap_python_beanshell * -x \*.pyc \*~
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.wikifier.html b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.wikifier.html new file mode 100644 index 0000000..37207ca --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.fabmetheus_tools.wikifier.html @@ -0,0 +1,91 @@ + + +Python: module fabmetheus_utilities.fabmetheus_tools.wikifier + + + + +
 
+ 
fabmetheus_utilities.fabmetheus_tools.wikifier ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/fabmetheus_tools/wikifier.py
+

Wikifier is a script to add spaces to the pydoc files and move them to the documentation folder.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+
cStringIO
+fabmetheus_utilities.gcodec
+
os
+fabmetheus_utilities.settings
+

+ + + + + +
 
+Classes
       
+
Heading +
+

+ + + + + + + +
 
+class Heading
   A class to hold the heading and subheadings.
 
 Methods defined here:
+
__init__(self, depth=0)
Initialize.
+ +
addToOutput(self, output)
Add to the output.
+ +
getFromLine(self, headingLineTable, line)
Get the heading from a line.
+ +

+ + + + + +
 
+Functions
       
addToHeadings(headingLineTable, headings, line)
Add the line to the headings.
+
getLinkLine(line)
Get the link line with the wiki style link converted into a hypertext link.
+
getNavigationHypertext(fileText, transferredFileNameIndex, transferredFileNames)
Get the hypertext help with navigation lines.
+
getNavigationLine(contentsLinkText, previousLinkText, nextLinkText)
Get the wrapped pydoc hypertext help.
+
getNextLinkText(hypertextFiles, nextIndex)
Get the next link text.
+
getWrappedHypertext(fileText, hypertextFileIndex, hypertextFiles)
Get the wrapped pydoc hypertext help.
+
main()
Display the craft dialog.
+
readWriteDeleteHypertextHelp(documentDirectoryPath, hypertextFileIndex, hypertextFiles, transferredFileNames)
Read the pydoc hypertext help documents, write them in the documentation folder then delete the originals.
+
readWriteNavigationHelp(documentDirectoryPath, transferredFileNameIndex, transferredFileNames)
Read the hypertext help documents, and add the navigation lines to them.
+
removeFilesInDirectory(directoryPath)
Remove all the files in a directory.
+
writeContentsFile(documentDirectoryPath, hypertextFiles)
Write the contents file.
+
writeContentsLine(hypertextFile, output)
Write a line of the contents file.
+
writeHypertext()
Run pydoc, then read, write and delete each of the files.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalWikiLinkStart = '[<a href='

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.gcodec.html b/SkeinPyPy/documentation/fabmetheus_utilities.gcodec.html new file mode 100644 index 0000000..7e58b2b --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.gcodec.html @@ -0,0 +1,192 @@ + + +Python: module fabmetheus_utilities.gcodec + + + + +
 
+ 
fabmetheus_utilities.gcodec ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/gcodec.py
+

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
+G21
+M103
+M105
+M106
+M110 S60.0
+M111 S30.0
+M108 S210.0
+M104 S235.0
+G1 X0.37 Y-4.07 Z1.9 F60.0
+M101
+
+..
+many lines of text
+..

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+
cStringIO
+fabmetheus_utilities.euclidean
+
math
+os
+
sys
+traceback
+

+ + + + + +
 
+Classes
       
+
BoundingRectangle +
DistanceFeedRate +
+

+ + + + + + + +
 
+class BoundingRectangle
   A class to get the corners of a gcode text.
 
 Methods defined here:
+
getFromGcodeLines(self, lines, radius)
Parse gcode text and get the minimum and maximum corners.
+ +
isPointInside(self, point)
Determine if the point is inside the bounding rectangle.
+ +
parseCorner(self, line)
Parse a gcode line and use the location to update the bounding corners.
+ +

+ + + + + + + +
 
+class DistanceFeedRate
   A class to limit the z feed rate and round values.
 
 Methods defined here:
+
__init__(self)
Initialize.
+ +
addGcodeFromFeedRateThreadZ(self, feedRateMinute, thread, travelFeedRateMinute, z)
Add a thread to the output.
+ +
addGcodeFromLoop(self, loop, z)
Add the gcode loop.
+ +
addGcodeFromThreadZ(self, thread, z)
Add a thread to the output.
+ +
addGcodeMovementZ(self, point, z)
Add a movement to the output.
+ +
addGcodeMovementZWithFeedRate(self, feedRateMinute, point, z)
Add a movement to the output.
+ +
addLine(self, line)
Add a line of text and a newline to the output.
+ +
addLineCheckAlteration(self, line)
Add a line of text and a newline to the output and check to see if it is an alteration line.
+ +
addLines(self, lines)
Add lines of text to the output.
+ +
addLinesSetAbsoluteDistanceMode(self, lines)
Add lines of text to the output and ensure the absolute mode is set.
+ +
addParameter(self, firstWord, parameter)
Add the parameter.
+ +
addPerimeterBlock(self, loop, z)
Add the edge gcode block for the loop.
+ +
addTagBracketedLine(self, tagName, value)
Add a begin tag, value and end tag.
+ +
addTagBracketedProcedure(self, procedure)
Add a begin procedure tag, procedure and end procedure tag.
+ +
addTagRoundedLine(self, tagName, value)
Add a begin tag, rounded value and end tag.
+ +
getBoundaryLine(self, location)
Get boundary gcode line.
+ +
getFirstWordMovement(self, firstWord, location)
Get the start of the arc line.
+ +
getInfillBoundaryLine(self, location)
Get infill boundary gcode line.
+ +
getIsAlteration(self, line)
Determine if it is an alteration.
+ +
getLineWithFeedRate(self, feedRateMinute, line, splitLine)
Get the line with a feed rate.
+ +
getLineWithX(self, line, splitLine, x)
Get the line with an x.
+ +
getLineWithY(self, line, splitLine, y)
Get the line with a y.
+ +
getLineWithZ(self, line, splitLine, z)
Get the line with a z.
+ +
getLinearGcodeMovement(self, point, z)
Get a linear gcode movement.
+ +
getLinearGcodeMovementWithFeedRate(self, feedRateMinute, point, z)
Get a z limited gcode movement.
+ +
getRounded(self, number)
Get number rounded to the number of carried decimal places as a string.
+ +
parseSplitLine(self, firstWord, splitLine)
Parse gcode split line and store the parameters.
+ +

+ + + + + +
 
+Functions
       
addLineAndNewlineIfNecessary(line, output)
Add the line and if the line does not end with a newline add a newline.
+
addLinesToCString(cString, lines)
Add lines which have something to cStringIO.
+
getArcDistance(relativeLocation, splitLine)
Get arc distance.
+
getDoubleAfterFirstLetter(word)
Get the double value of the word after the first letter.
+
getDoubleForLetter(letter, splitLine)
Get the double value of the word after the first occurence of the letter in the split line.
+
getDoubleFromCharacterSplitLine(character, splitLine)
Get the double value of the string after the first occurence of the character in the split line.
+
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.
+
getFeedRateMinute(feedRateMinute, splitLine)
Get the feed rate per minute if the split line has a feed rate.
+
getFirstWord(splitLine)
Get the first word of a split line.
+
getFirstWordFromLine(line)
Get the first word of a line.
+
getFirstWordIndexReverse(firstWord, lines, startIndex)
Parse gcode in reverse order until the first word if there is one, otherwise return -1.
+
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.
+
getGcodeWithoutDuplication(duplicateWord, gcodeText)
Get gcode text without duplicate first words.
+
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
+
getLineWithValueString(character, line, splitLine, valueString)
Get the line with a valueString.
+
getLocationFromSplitLine(oldLocation, splitLine)
Get the location from the split line.
+
getRotationBySplitLine(splitLine)
Get the complex rotation from the split gcode line.
+
getSplitLineBeforeBracketSemicolon(line)
Get the split line before a bracket or semicolon.
+
getStringFromCharacterSplitLine(character, splitLine)
Get the string after the first occurence of the character in the split line.
+
getTagBracketedLine(tagName, value)
Get line with a begin tag, value and end tag.
+
getTagBracketedProcedure(procedure)
Get line with a begin procedure tag, procedure and end procedure tag.
+
isProcedureDone(gcodeText, procedure)
Determine if the procedure has been done on the gcode text.
+
isProcedureDoneOrFileIsEmpty(gcodeText, procedure)
Determine if the procedure has been done on the gcode text or the file is empty.
+
isThereAFirstWord(firstWord, lines, startIndex)
Parse gcode until the first word if there is one.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation._drill.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation._drill.html new file mode 100644 index 0000000..b014374 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation._drill.html @@ -0,0 +1,88 @@ + + +Python: module fabmetheus_utilities.geometry.creation._drill + + + + +
 
+ 
fabmetheus_utilities.geometry.creation._drill ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/creation/_drill.py
+

Drill negative solid.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+fabmetheus_utilities.geometry.geometry_utilities.evaluate
+
fabmetheus_utilities.geometry.creation.extrude
+fabmetheus_utilities.geometry.creation.lineation
+math
+
fabmetheus_utilities.geometry.creation.solid
+fabmetheus_utilities.geometry.creation.teardrop
+fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
DrillDerivation +
+

+ + + + + + + +
 
+class DrillDerivation
   Class to hold drill variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getGeometryOutput(derivation, elementNode)
Get vector3 vertexes from attribute dictionary.
+
getGeometryOutputByArguments(arguments, elementNode)
Get vector3 vertexes from attribute dictionary by arguments.
+
getNewDerivation(elementNode)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation._svg.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation._svg.html new file mode 100644 index 0000000..d571534 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation._svg.html @@ -0,0 +1,86 @@ + + +Python: module fabmetheus_utilities.geometry.creation._svg + + + + +
 
+ 
fabmetheus_utilities.geometry.creation._svg ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/creation/_svg.py
+

Svg reader.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.creation.lineation
+fabmetheus_utilities.geometry.geometry_tools.path
+
fabmetheus_utilities.svg_reader
+

+ + + + + +
 
+Classes
       
+
SVGDerivation +
+

+ + + + + + + +
 
+class SVGDerivation
   Class to hold svg variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getGeometryOutput(derivation, elementNode)
Get vector3 vertexes from attribute dictionary.
+
getGeometryOutputByArguments(arguments, elementNode)
Get vector3 vertexes from attribute dictionary by arguments.
+
getGeometryOutputBySVGReader(elementNode, svgReader)
Get vector3 vertexes from svgReader.
+
getNewDerivation(elementNode)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://hydraraptor.blogspot.com/>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.circle.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.circle.html new file mode 100644 index 0000000..0755755 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.circle.html @@ -0,0 +1,85 @@ + + +Python: module fabmetheus_utilities.geometry.creation.circle + + + + +
 
+ 
fabmetheus_utilities.geometry.creation.circle ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/creation/circle.py
+

Polygon path.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.creation.lineation
+
math
+fabmetheus_utilities.geometry.geometry_tools.path
+

+ + + + + +
 
+Classes
       
+
CircleDerivation +
+

+ + + + + + + +
 
+class CircleDerivation
   Class to hold circle variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getGeometryOutput(derivation, elementNode)
Get vector3 vertexes from attribute dictionary.
+
getGeometryOutputByArguments(arguments, elementNode)
Get vector3 vertexes from attribute dictionary by arguments.
+
getNewDerivation(elementNode)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.concatenate.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.concatenate.html new file mode 100644 index 0000000..efe9bd3 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.concatenate.html @@ -0,0 +1,85 @@ + + +Python: module fabmetheus_utilities.geometry.creation.concatenate + + + + +
 
+ 
fabmetheus_utilities.geometry.creation.concatenate ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/creation/concatenate.py
+

Boolean geometry concatenation.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.creation.lineation
+
math
+fabmetheus_utilities.geometry.geometry_tools.path
+

+ + + + + +
 
+Classes
       
+
ConcatenateDerivation +
+

+ + + + + + + +
 
+class ConcatenateDerivation
   Class to hold concatenate variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Initialize.
+ +

+ + + + + +
 
+Functions
       
getGeometryOutput(derivation, elementNode)
Get triangle mesh from attribute dictionary.
+
getGeometryOutputByArguments(arguments, elementNode)
Get triangle mesh from attribute dictionary by arguments.
+
getNewDerivation(elementNode)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.extrude.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.extrude.html new file mode 100644 index 0000000..2f5d3a5 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.extrude.html @@ -0,0 +1,157 @@ + + +Python: module fabmetheus_utilities.geometry.creation.extrude + + + + +
 
+ 
fabmetheus_utilities.geometry.creation.extrude ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/creation/extrude.py
+

Boolean geometry extrusion.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.creation.lineation
+
math
+fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.setting
+
fabmetheus_utilities.geometry.creation.solid
+fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
ExtrudeDerivation +
Interpolation +
PortionDirection +
+

+ + + + + + + +
 
+class ExtrudeDerivation
   Class to hold extrude variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Initialize.
+ +

+ + + + + + + +
 
+class Interpolation
   Class to interpolate a path.
 
 Methods defined here:
+
__init__(self)
Set index.
+ +
__repr__(self)
Get the string representation of this Interpolation.
+ +
getByDistances(self)
Get by distances.
+ +
getByPrefixAlong(self, elementNode, path, prefix)
Get interpolation from prefix and xml element along the path.
+ +
getByPrefixX(self, elementNode, path, prefix)
Get interpolation from prefix and xml element in the z direction.
+ +
getByPrefixZ(self, elementNode, path, prefix)
Get interpolation from prefix and xml element in the z direction.
+ +
getComparison(self, first, second)
Compare the first with the second.
+ +
getComplexByPortion(self, portionDirection)
Get complex from z portion.
+ +
getInnerPortion(self)
Get inner x portion.
+ +
getVector3ByPortion(self, portionDirection)
Get vector3 from z portion.
+ +
getYByPortion(self, portionDirection)
Get y from x portion.
+ +
setInterpolationIndex(self, portionDirection)
Set the interpolation index.
+ +
setInterpolationIndexFromTo(self, portionDirection)
Set the interpolation index, the start vertex and the end vertex.
+ +

+ + + + + + + +
 
+class PortionDirection
   Class to hold a portion and direction.
 
 Methods defined here:
+
__init__(self, portion)
Initialize.
+ +
__repr__(self)
Get the string representation of this PortionDirection.
+ +

+ + + + + +
 
+Functions
       
addLoop(derivation, endMultiplier, loopLists, path, portionDirectionIndex, portionDirections, vertexes)
Add an indexed loop to the vertexes.
+
addNegatives(derivation, negatives, paths)
Add pillars output to negatives.
+
addNegativesPositives(derivation, negatives, paths, positives)
Add pillars output to negatives and positives.
+
addOffsetAddToLists(loop, offset, vector3Index, vertexes)
Add an indexed loop to the vertexes.
+
addPositives(derivation, paths, positives)
Add pillars output to positives.
+
addSpacedPortionDirection(portionDirection, spacedPortionDirections)
Add spaced portion directions.
+
addTwistPortions(interpolationTwist, remainderPortionDirection, twistPrecision)
Add twist portions.
+
comparePortionDirection(portionDirection, otherPortionDirection)
Comparison in order to sort portion directions in ascending order of portion then direction.
+
getGeometryOutput(derivation, elementNode)
Get triangle mesh from attribute dictionary.
+
getGeometryOutputByArguments(arguments, elementNode)
Get triangle mesh from attribute dictionary by arguments.
+
getGeometryOutputByLoops(derivation, loops)
Get geometry output by sorted, nested loops.
+
getGeometryOutputByNegativesPositives(elementNode, negatives, positives)
Get triangle mesh from elementNode, negatives and positives.
+
getGeometryOutputByNestedRing(derivation, nestedRing, portionDirections)
Get geometry output by sorted, nested loops.
+
getLoopListsByPath(derivation, endMultiplier, path, portionDirections)
Get loop lists from path.
+
getNewDerivation(elementNode)
Get new derivation.
+
getNormalAverage(normals)
Get normal.
+
getNormals(interpolationOffset, offset, portionDirection)
Get normals.
+
getSpacedPortionDirections(interpolationDictionary)
Get sorted portion directions.
+
insertTwistPortions(derivation, elementNode)
Insert twist portions and radian the twist.
+
processElementNode(elementNode)
Process the xml element.
+
setElementNodeToEndStart(elementNode, end, start)
Set elementNode attribute dictionary to a tilt following path from the start to end.
+
setOffsetByMultiplier(begin, end, multiplier, offset)
Set the offset by the multiplier.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.gear.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.gear.html new file mode 100644 index 0000000..1f46831 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.gear.html @@ -0,0 +1,731 @@ + + +Python: module fabmetheus_utilities.geometry.creation.gear + + + + +
 
+ 
fabmetheus_utilities.geometry.creation.gear ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/creation/gear.py
+

+Previous / Next / Contents +

+


+The gear script can generate a spur gear couple, a bevel gear couple, a ring gear couple and a rack & pinion couple.
+
+A helix pattern can be added to each gear type. All the gear types have a clearance and all the teeth can be beveled. A keyway, shaft and lightening holes can be added to all the round gears, and rack holes can be added to the rack. The script can output solid gears or only the gear profiles. Both gears of the couple can be generated or just one.
+
+The couple has a pinion gear and a complement.
+
+
+Examples
+  Bevel
+  Collar
+  Gear
+  Keyway
+  Rack
+  Rack Hole
+  Ring
+  Shaft
+  Shaft Top
+  Spur Helix
+  Spur Herringbone
+  Spur Parabolic
+  Spur Profile
+Parameters
+  Center Distance
+  Clearance Couplet
+    Clearance Over Wavelength
+    Clearance
+  Collar Addendum Couplet
+    Collar Addendum Over Radius
+    Collar Addendum
+  Complement Collar Length Couplet
+    Complement Collar Length Over Face Width
+    Complement Collar Length
+  Creation Type
+    Both
+    Complement
+    Pinion
+  Face Width
+  Gear Hole Paths
+  Helix Angle
+  Helix Path
+  Helix Type
+    Basic
+    Herringbone
+    Parabolic
+  Keyway Radius Couplet
+    Keyway Radius Over Radius
+    Keyway Radius
+  Lightening Hole Margin Couplet
+    Lightening Hole Margin Over Rim Dedendum
+    Lightening Hole Margin
+  Lightening Hole Minimum Radius
+  Move Type
+    None
+    Mesh
+    Separate
+    Vertical
+  Operating Angle
+  Pinion Collar Length Couplet
+    Pinion Collar Length Over Face Width
+    Pinion Collar Length
+  Pitch Radius
+  Plate Clearance Couplet
+    Plate Clearance Over Length
+    Plate Clearance
+  Plate Length Couplet
+    Plate Length Over Face Width
+    Plate Length
+  Pressure Angle
+  Profile Surfaces
+  Rack Hole Below Over Width Couplet
+    Rack Hole Below Over Width
+    Rack Hole Below
+  Rack Hole Radius Couplet
+    Rack Hole Radius Over Width
+    Rack Hole Radius
+  Rack Hole Step Over Width Couplet
+    Rack Hole Step Over Width
+    Rack Hole Step
+  Rack Length Over Radius Couplet
+    Rack Length Over Radius
+    Rack Length
+  Rack Width Couplet
+    Rack Width Over Face Width
+    Rack Width
+  Rim Dedendum Couplet
+    Rim Dedendum Over Radius
+    Rim Dedendum
+  Root Bevel Couplet
+    Root Bevel Over Clearance
+    Root Bevel
+  Shaft Depth Bottom Couplet
+    Shaft Depth Bottom Over Radius
+    Shaft Depth Bottom
+  Shaft Depth Top Couplet
+    Shaft Depth Top Over Radius
+    Shaft Depth Top
+  Shaft Path
+  Shaft Radius Couplet
+    Shaft Radius Over Pitch Radius
+    Shaft Radius
+  Shaft Sides
+  Teeth Pinion
+  Teeth Complement
+  Tip Bevel Couplet
+    Tip Bevel Over Clearance
+    Tip Bevel
+  Tooth Thickness Multiplier
+
+

Examples

+
+ +The link text includes the distinguishing parameters. Each svg page was generated from an xml page of the same root name using carve. For example, gear.svg was generated by clicking 'Carve' on the carve tool panel and choosing gear.xml in the file chooser.
+
+Each generated svg file has the xml fabmetheus element without comments towards the end of the file. To see it, open the svg file in a text editor and search for 'fabmetheus' If you copy that into a new text document, add the line '<?xml version='1.0' ?>' at the beginning and then give it a file name with the extension '.xml', you could then generate another svg file using carve.
+
+

Bevel

+ +Bevel gear couple.
+
+gear operatingAngle=90
+
+

Collar

+ +Spur gear couple and each gear has a collar.
+
+gear complementCollarLengthOverFaceWidth='1' pinionCollarLengthOverFaceWidth='1' shaftRadius='5'
+
+

Gear

+ +Default spur gear with no parameters.
+
+gear
+
+

Keyway

+ +Spur gear couple and each gear has a collar and defined keyway.
+
+gear complementCollarLengthOverFaceWidth='1' keywayRadius='2' pinionCollarLengthOverFaceWidth='1' shaftRadius='5'
+
+

Rack

+ +Rack and pinion couple.
+
+gear teethComplement='0'
+
+

Rack Hole

+ +Rack and pinion couple, with holes in the rack.
+
+gear rackHoleRadiusOverWidth='0.2' rackWidthOverFaceWidth='2' teethComplement='0'
+
+

Ring

+ +Pinion and ring gear.
+
+gear teethComplement='-23'
+
+

Shaft

+ +Spur gear couple and each gear has a square shaft hole.
+
+gear shaftRadius='5'
+
+

Shaft Top

+ +Spur gear couple and each gear has a round shaft hole, truncated on top.
+
+gear shaftRadius='5' shaftSides='13' shaftDepthTop='2'
+
+

Spur Helix

+ +Spur gear couple with the gear teeth following a helix path.
+
+gear helixAngle='45'
+
+

Spur Herringbone

+ +Spur gear couple with the gear teeth following a herringbone path.
+
+gear helixAngle='45' helixType='herringbone'
+
+

Spur Parabolic

+ +Spur gear couple with the gear teeth following a parabolic path.
+
+gear helixAngle='45' helixType='parabolic'
+
+

Spur Profile

+ +Spur gear couple profile. Since this is just a horizontal path, it can not be sliced, so the path is then extruded to create a solid which can be sliced and viewed.
+
+gear id='spurProfile' faceWidth='0' | extrude target='=document.getElementByID(spurProfile)
+
+

Parameters

+
+ +

Center Distance

+ +Default is such that the pitch radius works out to twenty.
+
+Defines the distance between the gear centers.
+
+

Clearance Couplet

+ +

Clearance Over Wavelength

+ +Default is 0.1.
+
+Defines the ratio of the clearance over the wavelength of the gear profile. The wavelength is the arc distance between the gear teeth.
+
+

Clearance

+ +Default is the 'Clearance Over Wavelength' times the wavelength.
+
+Defines the clearance between the gear tooth and the other gear of the couple. If the clearance is zero, the outside of the gear tooth will touch the other gear. If the clearance is too high, the gear teeth will be long and weak.
+
+

Collar Addendum Couplet

+ +

Collar Addendum Over Radius

+ +Default is one.
+
+Defines the ratio of the collar addendum over the shaft radius.
+
+

Collar Addendum

+ +Default is the 'Collar Addendum Over Radius' times the shaft radius.
+
+Defines the collar addendum.
+
+

Complement Collar Length Couplet

+ +

Complement Collar Length Over Face Width

+ +Default is zero.
+
+Defines the ratio of the complement collar length over the face width.
+
+

Complement Collar Length

+ +Default is the 'Complement Collar Length Over Face Width' times the face width.
+
+Defines the complement collar length. If the complement collar length is zero, there will not be a collar on the complement gear.
+
+

Creation Type

+ +Default is 'both'.
+
+

Both

+ +When selected, the pinion and complement will be generated.
+
+

Complement

+ +When selected, only the complement gear or rack will be generated.
+
+

Pinion

+ +When selected, only the pinion will be generated.
+
+

Face Width

+ +Default is ten.
+
+Defines the face width.
+
+

Gear Hole Paths

+ +Default is empty.
+
+Defines the centers of the gear holes. If the gear hole paths parameter is the default empty, then the centers of the gear holes will be generated from other parameters.
+
+

Helix Angle

+ +Default is zero.
+
+

Helix Path

+ +Default is empty.
+
+Defines the helix path of the gear teeth. If the helix path is the default empty, then the helix will be generated from the helix angle and helix type.
+
+

Helix Type

+ +Default is 'basic'.
+
+

Basic

+ +When selected, the helix will be basic.
+
+

Herringbone

+ +When selected, the helix will have a herringbone pattern.
+
+

Parabolic

+ +When selected, the helix will have a parabolic pattern.
+
+

Keyway Radius Couplet

+ +

Keyway Radius Over Radius

+ +Default is half.
+
+Defines the ratio of the keyway radius over the shaft radius.
+
+

Keyway Radius

+ +Default is the 'Keyway Radius Over Radius' times the shaft radius.
+
+Defines the keyway radius. If the keyway radius is zero, there will not be a keyway on the collar.
+
+

Lightening Hole Margin Couplet

+ +

Lightening Hole Margin Over Rim Dedendum

+ +Default is one.
+
+Defines the ratio of the lightening hole margin over the rim dedendum.
+
+

Lightening Hole Margin

+ +Default is the 'Lightening Hole Margin Over Rim Dedendum' times the rim dedendum.
+
+Defines the minimum margin between lightening holes.
+
+

Lightening Hole Minimum Radius

+ +Default is one.
+
+Defines the minimum radius of the lightening holes.
+
+

Move Type

+ +Default is 'separate'.
+
+

None

+ +When selected, the gears will be not be moved and will therefore overlap. Afterwards the write plugin could be used to write each gear to a different file, so they can be fabricated in separate operations.
+
+

Mesh

+ +When selected, the gears will be separated horizontally so that they just mesh. This is useful to test if the gears mesh properly.
+
+

Separate

+ +When selected, the gears will be separated horizontally with a gap between them.
+
+

Vertical

+ +When selected, the gears will be separated vertically.
+
+

Operating Angle

+ +Default is 180 degrees.
+
+Defines the operating angle between the gear axes. If the operating angle is not 180 degrees, a bevel gear couple will be generated.
+
+

Pinion Collar Length Couplet

+ +

Pinion Collar Length Over Face Width

+ +Default is zero.
+
+Defines the ratio of the pinion collar length over the face width.
+
+

Pinion Collar Length

+ +Default is the 'Pinion Collar Length Over Face Width' times the face width.
+
+Defines the pinion collar length. If the pinion collar length is zero, there will not be a collar on the pinion gear.
+
+

Pitch Radius

+ +Default is twenty if the pitch radius has not been set. If the center distance is set, the default pitch radius is the center distance times the number of pinion teeth divided by the total number of gear teeth.
+
+Defines the pinion pitch radius.
+
+

Plate Clearance Couplet

+ +

Plate Clearance Over Length

+ +Default is 0.2.
+
+Defines the ratio of the plate clearance over the plate length.
+
+

Plate Clearance

+ +Default is the 'Plate Clearance Over Length' times the plate length.
+
+Defines the clearance between the pinion and the plate of the ring gear. If the clearance is zero, they will touch.
+
+

Plate Length Couplet

+ +

Plate Length Over Face Width

+ +Default is half.
+
+Defines the ratio of the plate length over the face width.
+
+

Plate Length

+ +Default is the 'Plate Length Over Face Width' times the face width.
+
+Defines the length of the plate of the ring gear.
+
+

Pressure Angle

+ +Default is twenty degrees.
+
+Defines the pressure angle of the gear couple.
+
+

Profile Surfaces

+ +Default is eleven.
+
+Defines the number of profile surfaces.
+
+

Rack Hole Below Over Width Couplet

+ +

Rack Hole Below Over Width

+ +Default is 0.6.
+
+Defines the ratio of the distance below the pitch of the rack holes over the rack width.
+
+

Rack Hole Below

+ +Default is the 'Rack Hole Below Over Width' times the rack width.
+
+Defines the the distance below the pitch of the rack holes.
+
+

Rack Hole Radius Couplet

+ +

Rack Hole Radius Over Width

+ +Default is zero.
+
+Defines the ratio of the rack hole radius over the rack width.
+
+

Rack Hole Radius

+ +Default is the 'Rack Hole Radius Over Width' times the rack width.
+
+Defines the radius of the rack holes. If the rack hole radius is zero, there won't be any rack holes.
+
+

Rack Hole Step Over Width Couplet

+ +

Rack Hole Step Over Width

+ +Default is one.
+
+Defines the ratio of the rack hole step over the rack width.
+
+

Rack Hole Step

+ +Default is the 'Rack Hole Step Over Width' times the rack width.
+
+Defines the horizontal step distance between the rack holes.
+
+

Rack Length Over Radius Couplet

+ +

Rack Length Over Radius

+ +Default is two times pi.
+
+Defines the ratio of the rack length over the pitch radius.
+
+

Rack Length

+ +Default is the 'Rack Length Over Radius' times the pitch radius.
+
+Defines the rack length.
+
+

Rack Width Couplet

+ +

Rack Width Over Face Width

+ +Default is one.
+
+Defines the ratio of the rack width over the face width.
+
+

Rack Width

+ +Default is the 'Rack Width Over Face Width' times the face width.
+
+Defines the rack width.
+
+

Rim Dedendum Couplet

+ +

Rim Dedendum Over Radius

+ +Default is 0.2.
+
+Defines the ratio of the rim dedendum over the pitch radius.
+
+

Rim Dedendum

+ +Default is the 'Rim Dedendum Over Radius' times the pitch radius.
+
+Defines the rim dedendum of the gear.
+
+

Root Bevel Couplet

+ +

Root Bevel Over Clearance

+ +Default is half.
+
+Defines the ratio of the root bevel over the clearance.
+
+

Root Bevel

+ +Default is the 'Root Bevel Over Clearance' times the clearance.
+
+Defines the bevel at the root of the gear tooth.
+
+

Shaft Depth Bottom Couplet

+ +

Shaft Depth Bottom Over Radius

+ +Default is zero.
+
+Defines the ratio of the bottom shaft depth over the shaft radius.
+
+

Shaft Depth Bottom

+ +Default is the 'Shaft Depth Bottom Over Radius' times the shaft radius.
+
+Defines the bottom shaft depth.
+
+

Shaft Depth Top Couplet

+ +

Shaft Depth Top Over Radius

+ +Default is zero.
+
+Defines the ratio of the top shaft depth over the shaft radius.
+
+

Shaft Depth Top

+ +Default is the 'Shaft Depth Top Over Radius' times the shaft radius.
+
+Defines the top shaft depth.
+
+

Shaft Path

+ +Default is empty.
+
+Defines the path of the shaft hole. If the shaft path is the default empty, then the shaft path will be generated from the shaft depth bottom, shaft depth top, shaft radius and shaft sides.
+
+

Shaft Radius Couplet

+ +

Shaft Radius Over Pitch Radius

+ +Default is zero.
+
+Defines the ratio of the shaft radius over the pitch radius.
+
+

Shaft Radius

+ +Default is the 'Shaft Radius Over Pitch Radius' times the pitch radius.
+
+Defines the shaft radius. If the shaft radius is zero there will not be a shaft hole.
+
+

Shaft Sides

+ +Default is four.
+
+Defines the number of shaft sides.
+
+

Teeth Pinion

+ +Default is seven.
+
+Defines the number of teeth in the pinion.
+
+

Teeth Complement

+ +Default is seventeen.
+
+Defines the number of teeth in the complement of the gear couple. If the number of teeth is positive, the gear couple will be a spur or bevel type. If the number of teeth is zero, the gear couple will be a rack and pinion. If the number of teeth is negative, the gear couple will be a spur and ring.
+
+

Tip Bevel Couplet

+ +

Tip Bevel Over Clearance

+ +Default is 0.1.
+
+Defines the ratio of the tip bevel over the clearance.
+
+

Tip Bevel

+ +Default is the 'Tip Bevel Over Clearance' times the clearance.
+
+Defines the bevel at the tip of the gear tooth.
+
+

Tooth Thickness Multiplier

+ +Default is 0.99999.
+
+Defines the amount the thickness of the tooth will multiplied. If when the gears are produced, they mesh too tightly, you can reduce the tooth thickness multiplier so that they mesh with reasonable tightness.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.creation.extrude
+
fabmetheus_utilities.geometry.creation.lineation
+math
+fabmetheus_utilities.geometry.geometry_utilities.matrix
+fabmetheus_utilities.geometry.geometry_tools.path
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.setting
+fabmetheus_utilities.geometry.creation.shaft
+fabmetheus_utilities.geometry.creation.solid
+fabmetheus_utilities.geometry.creation.teardrop
+
fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
GearDerivation +
+

+ + + + + + + +
 
+class GearDerivation
   Class to hold gear variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
addBevelGear(derivation, extrudeDerivation, pitchRadius, positives, teeth, vector3GearProfile)
Get extrude output for a cylinder gear.
+
addBottomLoop(deltaZ, loops)
Add bottom loop to loops.
+
addCollarShaft(collarLength, derivation, elementNode, negatives, positives)
Add collar.
+
addCollarShaftSetDerivation(collarDerivation, collarLength, derivation, elementNode, negatives, positives)
Add collar and shaft.
+
addLighteningHoles(derivation, gearHolePaths, negatives, pitchRadius, positives)
Add lightening holes.
+
addRackHole(derivation, elementNode, vector3RackProfiles, x)
Add rack hole to vector3RackProfiles.
+
addRackHoles(derivation, elementNode, vector3RackProfiles)
Add rack holes to vector3RackProfiles.
+
addShaft(derivation, negatives, positives)
Add shaft.
+
getAxialMargin(circleRadius, numberOfSides, polygonRadius)
Get axial margin.
+
getBevelPath(begin, bevel, center, end)
Get bevel path.
+
getGearPaths(derivation, pitchRadius, teeth, toothProfile)
Get gear paths.
+
getGearProfileAnnulus(derivation, pitchRadius, teeth, toothProfile)
Get gear profile for an annulus gear.
+
getGearProfileCylinder(teeth, toothProfile)
Get gear profile for a cylinder gear.
+
getGearProfileRack(derivation, toothProfile)
Get gear profile for rack.
+
getGeometryOutput(derivation, elementNode)
Get vector3 vertexes from attribute dictionary.
+
getGeometryOutputByArguments(arguments, elementNode)
Get vector3 vertexes from attribute dictionary by arguments.
+
getHalfwave(pitchRadius, teeth)
Get tooth halfwave.
+
getHelixComplexPath(derivation, elementNode)
Set gear helix path.
+
getLiftedOutput(derivation, geometryOutput)
Get extrude output for a rack.
+
getLighteningHoles(derivation, gearHolePaths, pitchRadius)
Get cutout circles.
+
getNewDerivation(elementNode)
Get new derivation.
+
getOutputCylinder(collarLength, derivation, elementNode, gearHolePaths, pitchRadius, teeth, twist, vector3GearProfile)
Get extrude output for a cylinder gear.
+
getOutputRack(derivation, elementNode, vector3GearProfile)
Get extrude output for a rack.
+
getPathOutput(creationFirst, derivation, elementNode, translation, vector3ComplementPaths, vector3PinionProfile)
Get gear path output.
+
getThicknessMultipliedPath(path, thicknessMultiplier)
Get thickness multiplied path.
+
getToothProfile(derivation, pitchRadius, teeth)
Get profile for one tooth.
+
getToothProfileAnnulus(derivation, pitchRadius, teeth)
Get profile for one tooth of an annulus.
+
getToothProfileCylinder(derivation, pitchRadius, teeth)
Get profile for one tooth of a cylindrical gear.
+
getToothProfileCylinderByProfile(derivation, pitchRadius, teeth, toothProfileHalf)
Get profile for one tooth of a cylindrical gear.
+
getToothProfileHalfCylinder(derivation, pitchRadius)
Get profile for half of a one tooth of a cylindrical gear.
+
getToothProfileRack(derivation)
Get profile for one rack tooth.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.grid.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.grid.html new file mode 100644 index 0000000..bbef05e --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.grid.html @@ -0,0 +1,91 @@ + + +Python: module fabmetheus_utilities.geometry.creation.grid + + + + +
 
+ 
fabmetheus_utilities.geometry.creation.grid ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/creation/grid.py
+

Grid path points.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.creation.lineation
+
math
+fabmetheus_utilities.geometry.geometry_tools.path
+
random
+

+ + + + + +
 
+Classes
       
+
GridDerivation +
+

+ + + + + + + +
 
+class GridDerivation
   Class to hold grid variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
addGridRow(diameter, gridPath, loopsComplex, maximumComplex, rowIndex, x, y, zigzag)
Add grid row.
+
getGeometryOutput(elementNode)
Get vector3 vertexes from attribute dictionary.
+
getGeometryOutputByArguments(arguments, elementNode)
Get vector3 vertexes from attribute dictionary by arguments.
+
getHexagonalGrid(diameter, loopsComplex, maximumComplex, minimumComplex, zigzag)
Get hexagonal grid.
+
getIsPointInsideZoneAwayOthers(diameterReciprocal, loopsComplex, point, pixelDictionary)
Determine if the point is inside the loops zone and and away from the other points.
+
getNewDerivation(elementNode)
Get new derivation.
+
getRandomGrid(derivation, diameter, elementNode, loopsComplex, maximumComplex, minimumComplex)
Get rectangular grid.
+
getRectangularGrid(diameter, loopsComplex, maximumComplex, minimumComplex, zigzag)
Get rectangular grid.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.heightmap.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.heightmap.html new file mode 100644 index 0000000..cf30f1b --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.heightmap.html @@ -0,0 +1,101 @@ + + +Python: module fabmetheus_utilities.geometry.creation.heightmap + + + + +
 
+ 
fabmetheus_utilities.geometry.creation.heightmap ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/creation/heightmap.py
+

Heightmap.
+http://www.cs.otago.ac.nz/graphics/Mirage/node59.html
+http://en.wikipedia.org/wiki/Heightmap
+http://en.wikipedia.org/wiki/Netpbm_format

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.creation.lineation
+math
+
fabmetheus_utilities.geometry.geometry_tools.path
+random
+fabmetheus_utilities.geometry.creation.solid
+
fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
HeightmapDerivation +
+

+ + + + + + + +
 
+class HeightmapDerivation
   Class to hold heightmap variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +
__repr__(self)
Get the string representation of this HeightmapDerivation.
+ +

+ + + + + +
 
+Functions
       
addHeightsByBitmap(heights, textLines)
Add heights by bitmap.
+
addHeightsByGraymap(heights, textLines)
Add heights by graymap.
+
getAddIndexedHeightGrid(heightGrid, minimumXY, step, top, vertexes)
Get and add an indexed heightGrid.
+
getAddIndexedSegmentedPerimeter(heightGrid, maximumXY, minimumXY, step, vertexes, z=0.0)
Get and add an indexed segmented perimeter.
+
getGeometryOutput(elementNode)
Get vector3 vertexes from attribute dictionary.
+
getGeometryOutputByArguments(arguments, elementNode)
Get vector3 vertexes from attribute dictionary by arguments.
+
getGeometryOutputByHeightGrid(derivation, elementNode, heightGrid)
Get vector3 vertexes from attribute dictionary.
+
getHeightGrid(fileName)
Get heightGrid by fileName.
+
getNewDerivation(elementNode)
Get new derivation.
+
getRaisedHeightGrid(heightGrid, start)
Get heightGrid raised above start.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.html new file mode 100644 index 0000000..38902b0 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.html @@ -0,0 +1,62 @@ + + +Python: package fabmetheus_utilities.geometry.creation + + + + +
 
+ 
fabmetheus_utilities.geometry.creation
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/creation/__init__.py
+

+Previous / Next / Contents +

+


+This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Package Contents
       
_drill
+_svg
+circle
+concatenate
+extrude
+gear
+
grid
+heightmap
+lathe
+line
+linear_bearing_cage
+lineation
+
mechaslab
+peg
+polygon
+shaft
+solid
+sponge_slice
+
square
+teardrop
+text
+

+ + + + + +
 
+Data
       level = 3
+numberOfLevelsDeepInPackageHierarchy = 3
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.lathe.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.lathe.html new file mode 100644 index 0000000..5e841aa --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.lathe.html @@ -0,0 +1,93 @@ + + +Python: module fabmetheus_utilities.geometry.creation.lathe + + + + +
 
+ 
fabmetheus_utilities.geometry.creation.lathe ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/creation/lathe.py
+

Boolean geometry extrusion.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.creation.lineation
+
math
+fabmetheus_utilities.geometry.creation.solid
+
fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
LatheDerivation +
+

+ + + + + + + +
 
+class LatheDerivation
   Class to hold lathe variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
addLoopByComplex(derivation, endMultiplier, loopLists, path, pointComplex, vertexes)
Add an indexed loop to the vertexes.
+
addNegatives(derivation, negatives, paths)
Add pillars output to negatives.
+
addNegativesPositives(derivation, negatives, paths, positives)
Add pillars output to negatives and positives.
+
addOffsetAddToLists(loop, offset, vector3Index, vertexes)
Add an indexed loop to the vertexes.
+
addPositives(derivation, paths, positives)
Add pillars output to positives.
+
getGeometryOutput(derivation, elementNode)
Get triangle mesh from attribute dictionary.
+
getGeometryOutputByArguments(arguments, elementNode)
Get triangle mesh from attribute dictionary by arguments.
+
getGeometryOutputByNegativesPositives(derivation, elementNode, negatives, positives)
Get triangle mesh from derivation, elementNode, negatives and positives.
+
getLoopListsByPath(derivation, endMultiplier, path)
Get loop lists from path.
+
getNewDerivation(elementNode)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.line.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.line.html new file mode 100644 index 0000000..9a7e48f --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.line.html @@ -0,0 +1,86 @@ + + +Python: module fabmetheus_utilities.geometry.creation.line + + + + +
 
+ 
fabmetheus_utilities.geometry.creation.line ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/creation/line.py
+

Square path.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.creation.lineation
+
math
+fabmetheus_utilities.geometry.geometry_tools.path
+

+ + + + + +
 
+Classes
       
+
LineDerivation +
+

+ + + + + + + +
 
+class LineDerivation
   Class to hold line variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getGeometryOutput(derivation, elementNode)
Get vector3 vertexes from attribute dictionary.
+
getGeometryOutputByArguments(arguments, elementNode)
Get vector3 vertexes from attribute dictionary by arguments.
+
getGeometryOutputByStep(elementNode, end, loop, steps, stepVector)
Get line geometry output by the end, loop, steps and stepVector.
+
getNewDerivation(elementNode)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.linear_bearing_cage.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.linear_bearing_cage.html new file mode 100644 index 0000000..2278ec1 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.linear_bearing_cage.html @@ -0,0 +1,103 @@ + + +Python: module fabmetheus_utilities.geometry.creation.linear_bearing_cage + + + + +
 
+ 
fabmetheus_utilities.geometry.creation.linear_bearing_cage ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/creation/linear_bearing_cage.py
+

Linear bearing cage.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.geometry.solids.cylinder
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.creation.extrude
+fabmetheus_utilities.geometry.creation.lineation
+
math
+fabmetheus_utilities.geometry.geometry_utilities.matrix
+fabmetheus_utilities.geometry.creation.peg
+
fabmetheus_utilities.geometry.creation.solid
+fabmetheus_utilities.geometry.solids.sphere
+fabmetheus_utilities.geometry.manipulation_matrix.translate
+

+ + + + + +
 
+Classes
       
+
LinearBearingCageDerivation +
+

+ + + + + + + +
 
+class LinearBearingCageDerivation
   Class to hold linear bearing cage variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +
setAssemblyCage(self)
Set two piece assembly parameters.
+ +

+ + + + + +
 
+Functions
       
addAssemblyCage(derivation, negatives, positives)
Add assembly linear bearing cage.
+
addCage(derivation, height, negatives, positives)
Add linear bearing cage.
+
addCageGroove(derivation, negatives, positives)
Add cage and groove.
+
addGroove(derivation, negatives)
Add groove on each side of cage.
+
addNegativePeg(derivation, negatives, x, y)
Add negative cylinder at x and y.
+
addNegativeSphere(derivation, negatives, x)
Add negative sphere at x.
+
addPositivePeg(derivation, positives, x, y)
Add positive cylinder at x and y.
+
getBearingCenterXs(bearingCenterX, numberOfSteps, stepX)
Get the bearing center x list.
+
getGeometryOutput(elementNode)
Get vector3 vertexes from attribute dictionary.
+
getGeometryOutputByArguments(arguments, elementNode)
Get vector3 vertexes from attribute dictionary by arguments.
+
getNewDerivation(elementNode)
Get new derivation.
+
getPegCenterXs(numberOfSteps, pegCenterX, stepX)
Get the peg center x list.
+
getRoundedExtendedRectangle(radius, rectangleCenterX, sides)
Get the rounded extended rectangle.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.lineation.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.lineation.html new file mode 100644 index 0000000..edda822 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.lineation.html @@ -0,0 +1,143 @@ + + +Python: module fabmetheus_utilities.geometry.creation.lineation + + + + +
 
+ 
fabmetheus_utilities.geometry.creation.lineation ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/creation/lineation.py
+

Polygon path.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+math
+
fabmetheus_utilities.geometry.geometry_utilities.matrix
+fabmetheus_utilities.geometry.geometry_tools.path
+

+ + + + + +
 
+Classes
       
+
LineationDerivation +
SideLoop +
Spiral +
+

+ + + + + + + +
 
+class LineationDerivation
   Class to hold lineation variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + + + +
 
+class SideLoop
   Class to handle loop, side angle and side length.
 
 Methods defined here:
+
__init__(self, loop, sideAngle=None, sideLength=None)
Initialize.
+ +
getManipulationPluginLoops(self, elementNode)
Get loop manipulated by the plugins in the manipulation paths folder.
+ +
rotate(self, elementNode)
Rotate.
+ +

+ + + + + + + +
 
+class Spiral
   Class to add a spiral.
 
 Methods defined here:
+
__init__(self, spiral, stepRatio)
Initialize.
+ +
__repr__(self)
Get the string representation of this Spiral.
+ +
getSpiralPoint(self, unitPolar, vector3)
Add spiral to the vector.
+ +

+ + + + + +
 
+Functions
       
getComplexByDictionary(dictionary, valueComplex)
Get complex by dictionary.
+
getComplexByDictionaryListValue(value, valueComplex)
Get complex by dictionary, list or value.
+
getComplexByFloatList(floatList, valueComplex)
Get complex by float list.
+
getComplexByMultiplierPrefix(elementNode, multiplier, prefix, valueComplex)
Get complex from multiplier, prefix and xml element.
+
getComplexByMultiplierPrefixes(elementNode, multiplier, prefixes, valueComplex)
Get complex from multiplier, prefixes and xml element.
+
getComplexByPrefix(elementNode, prefix, valueComplex)
Get complex from prefix and xml element.
+
getComplexByPrefixBeginEnd(elementNode, prefixBegin, prefixEnd, valueComplex)
Get complex from element node, prefixBegin and prefixEnd.
+
getComplexByPrefixes(elementNode, prefixes, valueComplex)
Get complex from prefixes and xml element.
+
getComplexIfNone(valueComplex)
Get new complex if the original complex is none.
+
getFloatByPrefixBeginEnd(elementNode, prefixBegin, prefixEnd, valueFloat)
Get float from prefixBegin, prefixEnd and xml element.
+
getFloatByPrefixSide(defaultValue, elementNode, prefix, side)
Get float by prefix and side.
+
getGeometryOutput(derivation, elementNode)
Get geometry output from paths.
+
getGeometryOutputByArguments(arguments, elementNode)
Get vector3 vertexes from attribute dictionary by arguments.
+
getGeometryOutputByLoop(elementNode, sideLoop)
Get geometry output by side loop.
+
getGeometryOutputByManipulation(elementNode, sideLoop)
Get geometry output by manipulation.
+
getInradius(defaultInradius, elementNode)
Get inradius.
+
getMinimumRadius(beginComplexSegmentLength, endComplexSegmentLength, radius)
Get minimum radius.
+
getNewDerivation(elementNode)
Get new derivation.
+
getNumberOfBezierPoints(begin, elementNode, end)
Get the numberOfBezierPoints.
+
getPackedGeometryOutputByLoop(elementNode, sideLoop)
Get packed geometry output by side loop.
+
getRadiusAverage(radiusComplex)
Get average radius from radiusComplex.
+
getRadiusComplex(elementNode, radius)
Get radius complex for elementNode.
+
getStrokeRadiusByPrefix(elementNode, prefix)
Get strokeRadius by prefix.
+
processElementNode(elementNode)
Process the xml element.
+
processElementNodeByFunction(elementNode, manipulationFunction)
Process the xml element by the manipulationFunction.
+
processTargetByFunction(manipulationFunction, target)
Process the target by the manipulationFunction.
+
removeChildNodesFromElementObject(elementNode)
Process the xml element by manipulationFunction.
+
setClosedAttribute(elementNode, revolutions)
Set the closed attribute of the elementNode.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.mechaslab.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.mechaslab.html new file mode 100644 index 0000000..bf14d87 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.mechaslab.html @@ -0,0 +1,133 @@ + + +Python: module fabmetheus_utilities.geometry.creation.mechaslab + + + + +
 
+ 
fabmetheus_utilities.geometry.creation.mechaslab ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/creation/mechaslab.py
+

Mechaslab.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.geometry.solids.cylinder
+fabmetheus_utilities.euclidean
+fabmetheus_utilities.geometry.geometry_utilities.evaluate
+
fabmetheus_utilities.geometry.creation.extrude
+fabmetheus_utilities.geometry.creation.lineation
+math
+fabmetheus_utilities.geometry.geometry_utilities.matrix
+
fabmetheus_utilities.geometry.creation.peg
+fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.setting
+fabmetheus_utilities.geometry.creation.solid
+fabmetheus_utilities.geometry.manipulation_matrix.translate
+
fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
CellExistence +
HollowPegSocket +
MechaslabDerivation +
+

+ + + + + + + +
 
+class CellExistence
   Class to determine if a cell exists.
 
 Methods defined here:
+
__init__(self, columns, rows, value)
Initialize.
+ +
__repr__(self)
Get the string representation of this CellExistence.
+ +
getIsInExistence(self, columnIndex, rowIndex)
Detremine if the cell at the column and row exists.
+ +

+ + + + + + + +
 
+class HollowPegSocket
   Class to hold hollow peg socket variables.
 
 Methods defined here:
+
__init__(self, center)
Initialize.
+ +
__repr__(self)
Get the string representation of this HollowPegSocket.
+ +

+ + + + + + + +
 
+class MechaslabDerivation
   Class to hold mechaslab variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +
__repr__(self)
Get the string representation of this MechaslabDerivation.
+ +

+ + + + + +
 
+Functions
       
addAlongWay(begin, distance, end, loop)
Get the beveled rectangle.
+
addGroove(derivation, negatives)
Add groove on each side of cage.
+
addHollowPegSocket(derivation, hollowPegSocket, negatives, positives)
Add the socket and hollow peg.
+
addSlab(derivation, positives)
Add slab.
+
addXGroove(derivation, negatives, y)
Add x groove.
+
addYGroove(derivation, negatives, x)
Add y groove
+
getBeveledRectangle(bevel, bottomLeft)
Get the beveled rectangle.
+
getGeometryOutput(elementNode)
Get vector3 vertexes from attribute dictionary.
+
getGeometryOutputByArguments(arguments, elementNode)
Get vector3 vertexes from attribute dictionary by arguments.
+
getNewDerivation(elementNode)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.peg.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.peg.html new file mode 100644 index 0000000..c353cf2 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.peg.html @@ -0,0 +1,89 @@ + + +Python: module fabmetheus_utilities.geometry.creation.peg + + + + +
 
+ 
fabmetheus_utilities.geometry.creation.peg ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/creation/peg.py
+

Peg.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.geometry.solids.cylinder
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.creation.extrude
+
fabmetheus_utilities.geometry.creation.lineation
+math
+
fabmetheus_utilities.geometry.creation.solid
+

+ + + + + +
 
+Classes
       
+
PegDerivation +
+

+ + + + + + + +
 
+class PegDerivation
   Class to hold peg variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
addPegOutput(bevel, endZ, outputs, radiusArealized, sides, start, topOverBottom)
Add beveled cylinder to outputs given bevel, endZ, radiusArealized and start.
+
getGeometryOutput(derivation, elementNode)
Get vector3 vertexes from attribute dictionary.
+
getGeometryOutputByArguments(arguments, elementNode)
Get vector3 vertexes from attribute dictionary by arguments.
+
getNewDerivation(elementNode)
Get new derivation.
+
getTopAddBiconicOutput(bottomRadians, height, outputs, radius, sides, start, tipRadius, topRadians)
Get top and add biconic cylinder to outputs.
+
processElementNode(elementNode)
Process the xml element.
+
setTopOverBottomByRadius(derivation, endZ, radius, startZ)
Set the derivation topOverBottom by the angle of the elementNode, the endZ, float radius and startZ.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.polygon.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.polygon.html new file mode 100644 index 0000000..4126548 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.polygon.html @@ -0,0 +1,85 @@ + + +Python: module fabmetheus_utilities.geometry.creation.polygon + + + + +
 
+ 
fabmetheus_utilities.geometry.creation.polygon ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/creation/polygon.py
+

Polygon path.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.creation.lineation
+
math
+fabmetheus_utilities.geometry.geometry_tools.path
+

+ + + + + +
 
+Classes
       
+
PolygonDerivation +
+

+ + + + + + + +
 
+class PolygonDerivation
   Class to hold polygon variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getGeometryOutput(derivation, elementNode)
Get vector3 vertexes from attribute dictionary.
+
getGeometryOutputByArguments(arguments, elementNode)
Get vector3 vertexes from attribute dictionary by arguments.
+
getNewDerivation(elementNode)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.shaft.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.shaft.html new file mode 100644 index 0000000..0115a46 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.shaft.html @@ -0,0 +1,86 @@ + + +Python: module fabmetheus_utilities.geometry.creation.shaft + + + + +
 
+ 
fabmetheus_utilities.geometry.creation.shaft ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/creation/shaft.py
+

Shaft path.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.creation.lineation
+
math
+fabmetheus_utilities.geometry.geometry_tools.path
+

+ + + + + +
 
+Classes
       
+
ShaftDerivation +
+

+ + + + + + + +
 
+class ShaftDerivation
   Class to hold shaft variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getGeometryOutput(derivation, elementNode)
Get vector3 vertexes from attribute dictionary.
+
getGeometryOutputByArguments(arguments, elementNode)
Get vector3 vertexes from attribute dictionary by arguments.
+
getNewDerivation(elementNode)
Get new derivation.
+
getShaftPath(depthBottom, depthTop, radius, sides)
Get shaft with the option of a flat on the top and/or bottom.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.solid.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.solid.html new file mode 100644 index 0000000..632f4d0 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.solid.html @@ -0,0 +1,100 @@ + + +Python: module fabmetheus_utilities.geometry.creation.solid + + + + +
 
+ 
fabmetheus_utilities.geometry.creation.solid ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/creation/solid.py
+

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.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.geometry.geometry_utilities.boolean_geometry
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.creation.lineation
+math
+
fabmetheus_utilities.geometry.geometry_utilities.matrix
+fabmetheus_utilities.geometry.geometry_tools.path
+fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
SolidDerivation +
+

+ + + + + + + +
 
+class SolidDerivation
   Class to hold solid variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getGeometryOutputByFunction(elementNode, geometryFunction)
Get geometry output by manipulationFunction.
+
getGeometryOutputByManipulation(elementNode, geometryOutput)
Get geometryOutput manipulated by the plugins in the manipulation shapes & solids folders.
+
getLoopLayersSetCopy(elementNode, geometryOutput, importRadius, radius)
Get the loop layers and set the copyShallow.
+
getLoopOrEmpty(loopIndex, loopLayers)
Get the loop, or if the loopIndex is out of range, get an empty list.
+
getNewDerivation(elementNode)
Get new derivation.
+
getSolidMatchingPlugins(elementNode)
Get solid plugins in the manipulation matrix, shapes & solids folders.
+
processArchiveRemoveSolid(elementNode, geometryOutput)
Process the target by the manipulationFunction.
+
processElementNode(elementNode)
Process the xml element.
+
processElementNodeByDerivation(derivation, elementNode)
Process the xml element by derivation.
+
processElementNodeByFunction(elementNode, manipulationFunction)
Process the xml element.
+
processElementNodeByFunctionPair(elementNode, geometryFunction, pathFunction)
Process the xml element by the appropriate manipulationFunction.
+
processElementNodeByGeometry(elementNode, geometryOutput)
Process the xml element by geometryOutput.
+
processTarget(target)
Process the target.
+
processTargetByFunctionPair(geometryFunction, pathFunction, target)
Process the target by the manipulationFunction.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.sponge_slice.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.sponge_slice.html new file mode 100644 index 0000000..b8fdd5c --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.sponge_slice.html @@ -0,0 +1,106 @@ + + +Python: module fabmetheus_utilities.geometry.creation.sponge_slice + + + + +
 
+ 
fabmetheus_utilities.geometry.creation.sponge_slice ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/creation/sponge_slice.py
+

Sponge slice.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+fabmetheus_utilities.geometry.geometry_utilities.evaluate
+
fabmetheus_utilities.geometry.creation.lineation
+math
+fabmetheus_utilities.geometry.geometry_tools.path
+
random
+fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.setting
+time
+

+ + + + + +
 
+Classes
       
+
SpongeCircle +
SpongeSliceDerivation +
+

+ + + + + + + +
 
+class SpongeCircle
   Class to hold sponge circle.
 
 Methods defined here:
+
__init__(self, center, radius=0.0)
Initialize.
+ +
getRadius(self, center, derivation, otherCircles, overlapArealRatio)
Get sponge bubble radius.
+ +
moveCircle(self, derivation, otherCircles, overlapArealRatio)
Move circle into an open spot.
+ +

+ + + + + + + +
 
+class SpongeSliceDerivation
   Class to hold sponge slice variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Initialize.
+ +

+ + + + + +
 
+Functions
       
getGeometryOutput(derivation, elementNode)
Get vector3 vertexes from attribute dictionary.
+
getGeometryOutputByArguments(arguments, elementNode)
Get vector3 vertexes from attribute dictionary by arguments.
+
getIsPointAway(minimumDistance, point, spongeCircles)
Determine if the point is at least the minimumDistance away from other points.
+
getNewDerivation(elementNode)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.square.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.square.html new file mode 100644 index 0000000..e4d57ef --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.square.html @@ -0,0 +1,85 @@ + + +Python: module fabmetheus_utilities.geometry.creation.square + + + + +
 
+ 
fabmetheus_utilities.geometry.creation.square ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/creation/square.py
+

Square path.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.creation.lineation
+
math
+fabmetheus_utilities.geometry.geometry_tools.path
+

+ + + + + +
 
+Classes
       
+
SquareDerivation +
+

+ + + + + + + +
 
+class SquareDerivation
   Class to hold square variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getGeometryOutput(derivation, elementNode)
Get vector3 vertexes from attribute dictionary.
+
getGeometryOutputByArguments(arguments, elementNode)
Get vector3 vertexes from attribute dictionary by arguments.
+
getNewDerivation(elementNode)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.teardrop.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.teardrop.html new file mode 100644 index 0000000..f11e092 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.teardrop.html @@ -0,0 +1,91 @@ + + +Python: module fabmetheus_utilities.geometry.creation.teardrop + + + + +
 
+ 
fabmetheus_utilities.geometry.creation.teardrop ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/creation/teardrop.py
+

Teardrop path.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.creation.extrude
+
fabmetheus_utilities.geometry.creation.lineation
+math
+
fabmetheus_utilities.geometry.geometry_tools.path
+fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.setting
+

+ + + + + +
 
+Classes
       
+
TeardropDerivation +
+

+ + + + + + + +
 
+class TeardropDerivation
   Class to hold teardrop variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
addNegativesByRadius(elementNode, end, negatives, radius, start)
Add teardrop drill hole to negatives.
+
getGeometryOutput(derivation, elementNode)
Get vector3 vertexes from attribute dictionary.
+
getGeometryOutputByArguments(arguments, elementNode)
Get vector3 vertexes from attribute dictionary by arguments.
+
getInclination(end, start)
Get inclination.
+
getNewDerivation(elementNode)
Get new derivation.
+
getTeardropPath(inclination, overhangRadians, overhangSpan, radiusArealized, sides)
Get vector3 teardrop path.
+
getTeardropPathByEndStart(elementNode, end, radius, start)
Get vector3 teardrop path by end and start.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.text.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.text.html new file mode 100644 index 0000000..3b3aa67 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.creation.text.html @@ -0,0 +1,85 @@ + + +Python: module fabmetheus_utilities.geometry.creation.text + + + + +
 
+ 
fabmetheus_utilities.geometry.creation.text ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/creation/text.py
+

Text vertexes.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.creation.lineation
+
fabmetheus_utilities.geometry.geometry_tools.path
+fabmetheus_utilities.svg_reader
+

+ + + + + +
 
+Classes
       
+
TextDerivation +
+

+ + + + + + + +
 
+class TextDerivation
   Class to hold text variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getGeometryOutput(derivation, elementNode)
Get vector3 vertexes from attributes.
+
getGeometryOutputByArguments(arguments, elementNode)
Get vector3 vertexes from attribute dictionary by arguments.
+
getNewDerivation(elementNode)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.dictionary.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.dictionary.html new file mode 100644 index 0000000..8b34315 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.dictionary.html @@ -0,0 +1,133 @@ + + +Python: module fabmetheus_utilities.geometry.geometry_tools.dictionary + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_tools.dictionary ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_tools/dictionary.py
+

Boolean geometry dictionary object.

+

+ + + + + +
 
+Modules
       
__init__
+cStringIO
+
fabmetheus_utilities.euclidean
+fabmetheus_utilities.geometry.geometry_utilities.evaluate
+
fabmetheus_utilities.geometry.geometry_utilities.matrix
+fabmetheus_utilities.xml_simple_writer
+

+ + + + + +
 
+Classes
       
+
Dictionary +
+

+ + + + + + + +
 
+class Dictionary
   A dictionary object.
 
 Methods defined here:
+
__init__(self)
Add empty lists.
+ +
__repr__(self)
Get the string representation of this object info.
+ +
addXML(self, depth, output)
Add xml for this object.
+ +
addXMLArchivableObjects(self, depth, output)
Add xml for this object.
+ +
addXMLInnerSection(self, depth, output)
Add xml section for this object.
+ +
createShape(self)
Create the shape.
+ +
getAttributes(self)
Get attribute table.
+ +
getComplexTransformedPathLists(self)
Get complex transformed path lists.
+ +
getFabricationExtension(self)
Get fabrication extension.
+ +
getFabricationText(self, addLayerTemplate)
Get fabrication text.
+ +
getGeometryOutput(self)
Get geometry output dictionary.
+ +
getMatrix4X4(self)
Get the matrix4X4.
+ +
getMatrixChainTetragrid(self)
Get the matrix chain tetragrid.
+ +
getMinimumZ(self)
Get the minimum z.
+ +
getPaths(self)
Get all paths.
+ +
getTransformedPaths(self)
Get all transformed paths.
+ +
getTransformedVertexes(self)
Get all transformed vertexes.
+ +
getTriangleMeshes(self)
Get all triangleMeshes.
+ +
getType(self)
Get type.
+ +
getVertexes(self)
Get all vertexes.
+ +
getVisible(self)
Get visible.
+ +
getXMLLocalName(self)
Get xml local name.
+ +
setToElementNode(self, elementNode)
Set the shape of this carvable object info.
+ +
transformGeometryOutput(self, geometryOutput)
Transform the geometry output by the local matrix4x4.
+ +

+ + + + + +
 
+Functions
       
getAllPaths(paths, xmlObject)
Get all paths.
+
getAllTransformedPaths(transformedPaths, xmlObject)
Get all transformed paths.
+
getAllTransformedVertexes(transformedVertexes, xmlObject)
Get all transformed vertexes.
+
getAllVertexes(vertexes, xmlObject)
Get all vertexes.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://hydraraptor.blogspot.com/>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.face.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.face.html new file mode 100644 index 0000000..38b0a5a --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.face.html @@ -0,0 +1,121 @@ + + +Python: module fabmetheus_utilities.geometry.geometry_tools.face + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_tools.face ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_tools/face.py
+

Face of a triangle mesh.

+

+ + + + + +
 
+Modules
       
__init__
+cStringIO
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+
math
+fabmetheus_utilities.geometry.geometry_utilities.matrix
+fabmetheus_utilities.xml_simple_reader
+
fabmetheus_utilities.xml_simple_writer
+

+ + + + + +
 
+Classes
       
+
Edge +
Face +
+

+ + + + + + + +
 
+class Edge
   An edge of a triangle mesh.
 
 Methods defined here:
+
__init__(self)
Set the face indexes to None.
+ +
__repr__(self)
Get the string representation of this Edge.
+ +
addFaceIndex(self, faceIndex)
Add first None face index to input face index.
+ +
getFromVertexIndexes(self, edgeIndex, vertexIndexes)
Initialize from two vertex indices.
+ +

+ + + + + + + +
 
+class Face
   A face of a triangle mesh.
 
 Methods defined here:
+
__init__(self)
Initialize.
+ +
__repr__(self)
Get the string representation of this object info.
+ +
addToAttributes(self, attributes)
Add to the attribute dictionary.
+ +
addXML(self, depth, output)
Add the xml for this object.
+ +
copy(self)
Get the copy of this face.
+ +
getFromEdgeIndexes(self, edgeIndexes, edges, faceIndex)
Initialize from edge indices.
+ +
setEdgeIndexesToVertexIndexes(self, edges, edgeTable)
Set the edge indexes to the vertex indexes.
+ +

+ + + + + +
 
+Functions
       
addFaces(geometryOutput, faces)
Add the faces.
+
addGeometryList(elementNode, faces)
Add vertex elements to an xml element.
+
getCommonVertexIndex(edgeFirst, edgeSecond)
Get the vertex index that both edges have in common.
+
getFaces(geometryOutput)
Get the faces.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.html new file mode 100644 index 0000000..1fd7c1d --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.html @@ -0,0 +1,35 @@ + + +Python: package fabmetheus_utilities.geometry.geometry_tools + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_tools
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_tools/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
dictionary
+face
+
path
+path_elements (package)
+
vertex
+

+ + + + + +
 
+Data
       level = 3
+numberOfLevelsDeepInPackageHierarchy = 3
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.path.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.path.html new file mode 100644 index 0000000..0a88828 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.path.html @@ -0,0 +1,182 @@ + + +Python: module fabmetheus_utilities.geometry.geometry_tools.path + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_tools.path ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_tools/path.py
+

Path.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.geometry.geometry_tools.dictionary
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.geometry_utilities.matrix
+fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.setting
+
fabmetheus_utilities.svg_writer
+fabmetheus_utilities.geometry.geometry_tools.vertex
+fabmetheus_utilities.xml_simple_reader
+
fabmetheus_utilities.xml_simple_writer
+

+ + + + + +
 
+Classes
       
+
fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary +
+
+
Path +
+
+
SVGFabricationCarving +
+

+ + + + + + + +
 
+class Path(fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary)
   A path.
 
 Methods defined here:
+
__init__(self)
Add empty lists.
+ +
addXMLInnerSection(self, depth, output)
Add the xml section for this object.
+ +
getFabricationExtension(self)
Get fabrication extension.
+ +
getFabricationText(self, addLayerTemplate)
Get fabrication text.
+ +
getMatrix4X4(self)
Get the matrix4X4.
+ +
getMatrixChainTetragrid(self)
Get the matrix chain tetragrid.
+ +
getPaths(self)
Get all paths.
+ +
getTransformedPaths(self)
Get all transformed paths.
+ +
+Methods inherited from fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary:
+
__repr__(self)
Get the string representation of this object info.
+ +
addXML(self, depth, output)
Add xml for this object.
+ +
addXMLArchivableObjects(self, depth, output)
Add xml for this object.
+ +
createShape(self)
Create the shape.
+ +
getAttributes(self)
Get attribute table.
+ +
getComplexTransformedPathLists(self)
Get complex transformed path lists.
+ +
getGeometryOutput(self)
Get geometry output dictionary.
+ +
getMinimumZ(self)
Get the minimum z.
+ +
getTransformedVertexes(self)
Get all transformed vertexes.
+ +
getTriangleMeshes(self)
Get all triangleMeshes.
+ +
getType(self)
Get type.
+ +
getVertexes(self)
Get all vertexes.
+ +
getVisible(self)
Get visible.
+ +
getXMLLocalName(self)
Get xml local name.
+ +
setToElementNode(self, elementNode)
Set the shape of this carvable object info.
+ +
transformGeometryOutput(self, geometryOutput)
Transform the geometry output by the local matrix4x4.
+ +

+ + + + + + + +
 
+class SVGFabricationCarving
   An svg carving.
 
 Methods defined here:
+
__init__(self, addLayerTemplate, elementNode)
Add empty lists.
+ +
__repr__(self)
Get the string representation of this carving.
+ +
addXML(self, depth, output)
Add xml for this object.
+ +
getCarveBoundaryLayers(self)
Get the  boundary layers.
+ +
getCarveCornerMaximum(self)
Get the corner maximum of the vertexes.
+ +
getCarveCornerMinimum(self)
Get the corner minimum of the vertexes.
+ +
getCarveLayerHeight(self)
Get the layer height.
+ +
getCarvedSVG(self)
Get the carved svg text.
+ +
getFabmetheusXML(self)
Return the fabmetheus XML.
+ +
getInterpretationSuffix(self)
Return the suffix for a carving.
+ +
processSVGElement(self, fileName)
Parse SVG element and store the layers.
+ +
setCarveImportRadius(self, importRadius)
Set the import radius.
+ +
setCarveIsCorrectMesh(self, isCorrectMesh)
Set the is correct mesh flag.
+ +
setCarveLayerHeight(self, layerHeight)
Set the layer height.
+ +

+ + + + + +
 
+Functions
       
convertElementNode(elementNode, geometryOutput)
Convert the xml element by geometryOutput.
+
convertElementNodeByPath(elementNode, geometryOutput)
Convert the xml element to a path xml element.
+
convertElementNodeRenameByPaths(elementNode, geometryOutput)
Convert the xml element to a path xml element and add paths.
+
createLinkPath(elementNode)
Create and link a path object.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.path_elements.arc.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.path_elements.arc.html new file mode 100644 index 0000000..cbe8dfb --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.path_elements.arc.html @@ -0,0 +1,64 @@ + + +Python: module fabmetheus_utilities.geometry.geometry_tools.path_elements.arc + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_tools.path_elements.arc ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_tools/path_elements/arc.py
+

Arc vertexes.
+
+From:
+http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.creation.lineation
+
math
+fabmetheus_utilities.svg_reader
+

+ + + + + +
 
+Functions
       
getArcPath(elementNode)
Get the arc path.rx ry x-axis-rotation large-arc-flag sweep-flag
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.path_elements.cubic.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.path_elements.cubic.html new file mode 100644 index 0000000..a56c315 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.path_elements.cubic.html @@ -0,0 +1,63 @@ + + +Python: module fabmetheus_utilities.geometry.geometry_tools.path_elements.cubic + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_tools.path_elements.cubic ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_tools/path_elements/cubic.py
+

Cubic vertexes.
+
+From:
+http://www.w3.org/TR/SVG/paths.html#PathDataCubicBezierCommands

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+
fabmetheus_utilities.geometry.creation.lineation
+
fabmetheus_utilities.svg_reader
+

+ + + + + +
 
+Functions
       
getCubicPath(elementNode)
Get the cubic path.
+
getCubicPathByBeginEnd(begin, controlPoints, elementNode, end)
Get the cubic path by begin and end.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.path_elements.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.path_elements.html new file mode 100644 index 0000000..2abf4ae --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.path_elements.html @@ -0,0 +1,33 @@ + + +Python: package fabmetheus_utilities.geometry.geometry_tools.path_elements + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_tools.path_elements
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_tools/path_elements/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
arc
+
cubic
+
quadratic
+

+ + + + + +
 
+Data
       level = 4
+numberOfLevelsDeepInPackageHierarchy = 4
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.path_elements.quadratic.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.path_elements.quadratic.html new file mode 100644 index 0000000..cd707b0 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.path_elements.quadratic.html @@ -0,0 +1,62 @@ + + +Python: module fabmetheus_utilities.geometry.geometry_tools.path_elements.quadratic + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_tools.path_elements.quadratic ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_tools/path_elements/quadratic.py
+

Quadratic vertexes.
+
+From:
+http://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+
fabmetheus_utilities.geometry.creation.lineation
+
fabmetheus_utilities.svg_reader
+

+ + + + + +
 
+Functions
       
getQuadraticPath(elementNode)
Get the quadratic path.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.vertex.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.vertex.html new file mode 100644 index 0000000..7a82873 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_tools.vertex.html @@ -0,0 +1,60 @@ + + +Python: module fabmetheus_utilities.geometry.geometry_tools.vertex + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_tools.vertex ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_tools/vertex.py
+

Vertex of a triangle mesh.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+
fabmetheus_utilities.xml_simple_reader
+

+ + + + + +
 
+Functions
       
addGeometryList(elementNode, vertexes)
Add vertex elements to an xml element.
+
addVertexToAttributes(attributes, vertex)
Add to the attribute dictionary.
+
getUnboundVertexElement(vertex)
Add vertex element to an xml element.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.boolean_geometry.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.boolean_geometry.html new file mode 100644 index 0000000..27c1de5 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.boolean_geometry.html @@ -0,0 +1,137 @@ + + +Python: module fabmetheus_utilities.geometry.geometry_utilities.boolean_geometry + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_utilities.boolean_geometry ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_utilities/boolean_geometry.py
+

+Previous / Next / 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.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.geometry.geometry_utilities.boolean_solid
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+math
+fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.setting
+
fabmetheus_utilities.settings
+fabmetheus_utilities.geometry.solids.triangle_mesh
+fabmetheus_utilities.xml_simple_writer
+

+ + + + + +
 
+Classes
       
+
BooleanGeometry +
+

+ + + + + + + +
 
+class BooleanGeometry
   A boolean geometry scene.
 
 Methods defined here:
+
__init__(self)
Add empty lists.
+ +
__repr__(self)
Get the string representation of this carving.
+ +
addXML(self, depth, output)
Add xml for this object.
+ +
getCarveBoundaryLayers(self)
Get the boundary layers.
+ +
getCarveCornerMaximum(self)
Get the corner maximum of the vertexes.
+ +
getCarveCornerMinimum(self)
Get the corner minimum of the vertexes.
+ +
getCarveLayerHeight(self)
Get the layer height.
+ +
getFabmetheusXML(self)
Return the fabmetheus XML.
+ +
getInterpretationSuffix(self)
Return the suffix for a boolean carving.
+ +
getMatrix4X4(self)
Get the matrix4X4.
+ +
getMatrixChainTetragrid(self)
Get the matrix chain tetragrid.
+ +
getMinimumZ(self)
Get the minimum z.
+ +
getNumberOfEmptyZLoops(self, z)
Get number of empty z loops.
+ +
setActualMinimumZ(self)
Get the actual minimum z at the lowest rotated boundary layer.
+ +
setCarveImportRadius(self, importRadius)
Set the import radius.
+ +
setCarveIsCorrectMesh(self, isCorrectMesh)
Set the is correct mesh flag.
+ +
setCarveLayerHeight(self, layerHeight)
Set the layer height.
+ +

+ + + + + +
 
+Functions
       
getEmptyZLoops(archivableObjects, importRadius, shouldPrintWarning, z, zoneArrangement)
Get loops at empty z level.
+
getLoopLayers(archivableObjects, importRadius, layerHeight, maximumZ, shouldPrintWarning, z, zoneArrangement)
Get loop layers.
+
getMinimumZ(geometryObject)
Get the minimum of the minimum z of the archivableObjects and the object.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://hydraraptor.blogspot.com/>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.boolean_solid.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.boolean_solid.html new file mode 100644 index 0000000..223d4c3 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.boolean_solid.html @@ -0,0 +1,191 @@ + + +Python: module fabmetheus_utilities.geometry.geometry_utilities.boolean_solid + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_utilities.boolean_solid ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_utilities/boolean_solid.py
+

+Previous / Next / 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.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+fabmetheus_utilities.geometry.geometry_utilities.evaluate
+
fabmetheus_utilities.gcodec
+fabmetheus_utilities.geometry.solids.group
+fabmetheus_utilities.intercircle
+
math
+fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.setting
+fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
fabmetheus_utilities.geometry.solids.group.Group(fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary) +
+
+
BooleanSolid +
+
+
+

+ + + + + + + +
 
+class BooleanSolid(fabmetheus_utilities.geometry.solids.group.Group)
   A boolean solid object.
 
 
Method resolution order:
+
BooleanSolid
+
fabmetheus_utilities.geometry.solids.group.Group
+
fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary
+
+
+Methods defined here:
+
getDifference(self, importRadius, visibleObjectLoopsList)
Get subtracted loops sliced through shape.
+ +
getIntersection(self, importRadius, visibleObjectLoopsList)
Get intersected loops sliced through shape.
+ +
getLoops(self, importRadius, z)
Get loops sliced through shape.
+ +
getLoopsFromObjectLoopsList(self, importRadius, visibleObjectLoopsList)
Get loops from visible object loops list.
+ +
getTransformedPaths(self)
Get all transformed paths.
+ +
getUnion(self, importRadius, visibleObjectLoopsList)
Get joined loops sliced through shape.
+ +
getXMLLocalName(self)
Get xml class name.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.group.Group:
+
__init__(self)
Add empty lists.
+ +
addXMLInnerSection(self, depth, output)
Add xml inner section for this object.
+ +
addXMLSection(self, depth, output)
Add the xml section for this object.
+ +
getMatrix4X4(self)
Get the matrix4X4.
+ +
getMatrixChainTetragrid(self)
Get the matrix chain tetragrid.
+ +
getVisible(self)
Get visible.
+ +
setToElementNode(self, elementNode)
Set to elementNode.
+ +
+Methods inherited from fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary:
+
__repr__(self)
Get the string representation of this object info.
+ +
addXML(self, depth, output)
Add xml for this object.
+ +
addXMLArchivableObjects(self, depth, output)
Add xml for this object.
+ +
createShape(self)
Create the shape.
+ +
getAttributes(self)
Get attribute table.
+ +
getComplexTransformedPathLists(self)
Get complex transformed path lists.
+ +
getFabricationExtension(self)
Get fabrication extension.
+ +
getFabricationText(self, addLayerTemplate)
Get fabrication text.
+ +
getGeometryOutput(self)
Get geometry output dictionary.
+ +
getMinimumZ(self)
Get the minimum z.
+ +
getPaths(self)
Get all paths.
+ +
getTransformedVertexes(self)
Get all transformed vertexes.
+ +
getTriangleMeshes(self)
Get all triangleMeshes.
+ +
getType(self)
Get type.
+ +
getVertexes(self)
Get all vertexes.
+ +
transformGeometryOutput(self, geometryOutput)
Transform the geometry output by the local matrix4x4.
+ +

+ + + + + +
 
+Functions
       
addLineLoopsIntersections(loopLoopsIntersections, loops, pointBegin, pointEnd)
Add intersections of the line with the loops.
+
addLineXSegmentIntersection(lineLoopsIntersections, segmentFirstX, segmentSecondX, vector3First, vector3Second, y)
Add intersections of the line with the x segment.
+
addLoopLoopsIntersections(loop, loopsLoopsIntersections, otherLoops)
Add intersections of the loop with the other loops.
+
addLoopXSegmentIntersections(lineLoopsIntersections, loop, segmentFirstX, segmentSecondX, segmentYMirror, y)
Add intersections of the loop with the x segment.
+
addLoopsXSegmentIntersections(lineLoopsIntersections, loops, segmentFirstX, segmentSecondX, segmentYMirror, y)
Add intersections of the loops with the x segment.
+
getInBetweenLoopsFromLoops(loops, radius)
Get the in between loops from loops.
+
getInsetPointsByInsetLoop(insetLoop, inside, loops, radius)
Get the inset points of the inset loop inside the loops.
+
getInsetPointsByInsetLoops(insetLoops, inside, loops, radius)
Get the inset points of the inset loops inside the loops.
+
getIsInsetPointInsideLoops(inside, loops, pointBegin, pointCenter, pointEnd, radius)
Determine if the inset point is inside the loops.
+
getLoopsDifference(importRadius, loopLists)
Get difference loops.
+
getLoopsIntersection(importRadius, loopLists)
Get intersection loops.
+
getLoopsIntersectionByPair(importRadius, loopsFirst, loopsLast)
Get intersection loops for a pair of loop lists.
+
getLoopsListsIntersections(loopsList)
Get intersections betweens the loops lists.
+
getLoopsLoopsIntersections(loops, otherLoops)
Get all the intersections of the loops with the other loops.
+
getLoopsUnion(importRadius, loopLists)
Get joined loops sliced through shape.
+
getVisibleObjectLoopsList(importRadius, visibleObjects, z)
Get visible object loops list.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://hydraraptor.blogspot.com/>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate.html new file mode 100644 index 0000000..a9c5975 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate.html @@ -0,0 +1,1969 @@ + + +Python: module fabmetheus_utilities.geometry.geometry_utilities.evaluate + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_utilities.evaluate ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_utilities/evaluate.py
+

Evaluate expressions.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.gcodec
+math
+os
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.setting
+fabmetheus_utilities.settings
+sys
+
traceback
+

+ + + + + +
 
+Classes
       
+
BaseFunction +
+
+
ClassFunction +
Function +
+
+
ClassObject +
EmptyObject +
Evaluator +
+
+
EvaluatorAddition +
+
+
EvaluatorAnd +
+
+
EvaluatorOr +
+
+
EvaluatorDivision +
+
+
EvaluatorModulo +
EvaluatorMultiplication +
+
+
EvaluatorEqual +
+
+
EvaluatorGreater +
EvaluatorGreaterEqual +
EvaluatorLess +
EvaluatorLessEqual +
EvaluatorNotEqual +
+
+
EvaluatorPower +
EvaluatorSubtraction +
+
+
EvaluatorNot +
+
+
+
+
EvaluatorAttribute +
EvaluatorBracketCurly +
EvaluatorBracketRound +
EvaluatorBracketSquare +
EvaluatorClass +
EvaluatorComma +
EvaluatorConcatenate +
EvaluatorDictionary +
EvaluatorElement +
+
+
EvaluatorLocal +
EvaluatorSelf +
+
+
EvaluatorFalse +
EvaluatorFunction +
EvaluatorFundamental +
EvaluatorNone +
EvaluatorNumeric +
EvaluatorTrue +
EvaluatorValue +
+
+
FunctionVariable +
KeyValue +
ModuleElementNode +
+

+ + + + + + + +
 
+class BaseFunction
   Class to get equation results.
 
 Methods defined here:
+
__init__(self, elementNode)
Initialize.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
getReturnValue(self)
Get return value.
+ +
processChildNodes(self, elementNode)
Process childNodes if shouldReturn is false.
+ +

+ + + + + + + +
 
+class ClassFunction(BaseFunction)
   Class to get class results.
 
 Methods defined here:
+
getReturnValueByArguments(self, *arguments)
Get return value by arguments.
+ +
getReturnValueWithoutDeletion(self)
Get return value without deleting last function.
+ +
+Methods inherited from BaseFunction:
+
__init__(self, elementNode)
Initialize.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
getReturnValue(self)
Get return value.
+ +
processChildNodes(self, elementNode)
Process childNodes if shouldReturn is false.
+ +

+ + + + + + + +
 
+class ClassObject
   Class to hold class attributes and functions.
 
 Methods defined here:
+
__init__(self, elementNode)
Initialize.
+ +
__repr__(self)
Get the string representation of this Class.
+ +

+ + + + + + + +
 
+class EmptyObject
   An empty object.
 
 Methods defined here:
+
__init__(self)
Do nothing.
+ +

+ + + + + + + +
 
+class Evaluator
   Base evaluator class.
 
 Methods defined here:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorAddition(Evaluator)
   Class to add two evaluators.
 
 Methods defined here:
+
executePair(self, evaluators, evaluatorIndex)
Add two evaluators.
+ +
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
getEvaluatedValues(self, enumerable, keys, value)
Get evaluatedValues.
+ +
getOperationValue(self, leftValue, rightValue)
Get operation value.
+ +
getValueFromValuePair(self, leftValue, rightValue)
Add two values.
+ +
+Methods inherited from Evaluator:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorAnd(EvaluatorAddition)
   Class to compare two evaluators.
 
 
Method resolution order:
+
EvaluatorAnd
+
EvaluatorAddition
+
Evaluator
+
+
+Methods defined here:
+
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
getBooleanFromValuePair(self, leftValue, rightValue)
And two values.
+ +
getValueFromValuePair(self, leftValue, rightValue)
Get value from comparison.
+ +
+Methods inherited from EvaluatorAddition:
+
executePair(self, evaluators, evaluatorIndex)
Add two evaluators.
+ +
getEvaluatedValues(self, enumerable, keys, value)
Get evaluatedValues.
+ +
getOperationValue(self, leftValue, rightValue)
Get operation value.
+ +
+Methods inherited from Evaluator:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorAttribute(Evaluator)
   Class to handle an attribute.
 
 Methods defined here:
+
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
+Methods inherited from Evaluator:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorBracketCurly(Evaluator)
   Class to evaluate a string.
 
 Methods defined here:
+
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
+Methods inherited from Evaluator:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorBracketRound(Evaluator)
   Class to evaluate a string.
 
 Methods defined here:
+
__init__(self, elementNode, word)
Set value to none.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Evaluate the statement and delete the evaluators.
+ +
+Methods inherited from Evaluator:
+
__repr__(self)
Get the string representation of this Class.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorBracketSquare(Evaluator)
   Class to evaluate a string.
 
 Methods defined here:
+
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Evaluate the statement and delete the evaluators.
+ +
+Methods inherited from Evaluator:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorClass(Evaluator)
   Class evaluator class.
 
 Methods defined here:
+
__init__(self, elementNode, word)
Set value to none.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
+Methods inherited from Evaluator:
+
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorComma(Evaluator)
   Class to join two evaluators.
 
 Methods defined here:
+
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
+Methods inherited from Evaluator:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorConcatenate(Evaluator)
   Class to join two evaluators.
 
 Methods defined here:
+
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
+Methods inherited from Evaluator:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorDictionary(Evaluator)
   Class to join two evaluators.
 
 Methods defined here:
+
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
+Methods inherited from Evaluator:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorDivision(EvaluatorAddition)
   Class to divide two evaluators.
 
 
Method resolution order:
+
EvaluatorDivision
+
EvaluatorAddition
+
Evaluator
+
+
+Methods defined here:
+
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
getValueFromValuePair(self, leftValue, rightValue)
Divide two values.
+ +
+Methods inherited from EvaluatorAddition:
+
executePair(self, evaluators, evaluatorIndex)
Add two evaluators.
+ +
getEvaluatedValues(self, enumerable, keys, value)
Get evaluatedValues.
+ +
getOperationValue(self, leftValue, rightValue)
Get operation value.
+ +
+Methods inherited from Evaluator:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorElement(Evaluator)
   Element evaluator class.
 
 Methods defined here:
+
__init__(self, elementNode, word)
Set value to none.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
+Methods inherited from Evaluator:
+
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorEqual(EvaluatorAddition)
   Class to compare two evaluators.
 
 
Method resolution order:
+
EvaluatorEqual
+
EvaluatorAddition
+
Evaluator
+
+
+Methods defined here:
+
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
getBooleanFromValuePair(self, leftValue, rightValue)
Compare two values.
+ +
getValueFromValuePair(self, leftValue, rightValue)
Get value from comparison.
+ +
+Methods inherited from EvaluatorAddition:
+
executePair(self, evaluators, evaluatorIndex)
Add two evaluators.
+ +
getEvaluatedValues(self, enumerable, keys, value)
Get evaluatedValues.
+ +
getOperationValue(self, leftValue, rightValue)
Get operation value.
+ +
+Methods inherited from Evaluator:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorFalse(Evaluator)
   Class to evaluate a string.
 
 Methods defined here:
+
__init__(self, elementNode, word)
Set value to zero.
+ +
+Methods inherited from Evaluator:
+
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorFunction(Evaluator)
   Function evaluator class.
 
 Methods defined here:
+
__init__(self, elementNode, word)
Set value to none.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
+Methods inherited from Evaluator:
+
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorFundamental(Evaluator)
   Fundamental evaluator class.
 
 Methods defined here:
+
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
+Methods inherited from Evaluator:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorGreater(EvaluatorEqual)
   Class to compare two evaluators.
 
 
Method resolution order:
+
EvaluatorGreater
+
EvaluatorEqual
+
EvaluatorAddition
+
Evaluator
+
+
+Methods defined here:
+
getBooleanFromValuePair(self, leftValue, rightValue)
Compare two values.
+ +
+Methods inherited from EvaluatorEqual:
+
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
getValueFromValuePair(self, leftValue, rightValue)
Get value from comparison.
+ +
+Methods inherited from EvaluatorAddition:
+
executePair(self, evaluators, evaluatorIndex)
Add two evaluators.
+ +
getEvaluatedValues(self, enumerable, keys, value)
Get evaluatedValues.
+ +
getOperationValue(self, leftValue, rightValue)
Get operation value.
+ +
+Methods inherited from Evaluator:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorGreaterEqual(EvaluatorEqual)
   Class to compare two evaluators.
 
 
Method resolution order:
+
EvaluatorGreaterEqual
+
EvaluatorEqual
+
EvaluatorAddition
+
Evaluator
+
+
+Methods defined here:
+
getBooleanFromValuePair(self, leftValue, rightValue)
Compare two values.
+ +
+Methods inherited from EvaluatorEqual:
+
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
getValueFromValuePair(self, leftValue, rightValue)
Get value from comparison.
+ +
+Methods inherited from EvaluatorAddition:
+
executePair(self, evaluators, evaluatorIndex)
Add two evaluators.
+ +
getEvaluatedValues(self, enumerable, keys, value)
Get evaluatedValues.
+ +
getOperationValue(self, leftValue, rightValue)
Get operation value.
+ +
+Methods inherited from Evaluator:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorLess(EvaluatorEqual)
   Class to compare two evaluators.
 
 
Method resolution order:
+
EvaluatorLess
+
EvaluatorEqual
+
EvaluatorAddition
+
Evaluator
+
+
+Methods defined here:
+
getBooleanFromValuePair(self, leftValue, rightValue)
Compare two values.
+ +
+Methods inherited from EvaluatorEqual:
+
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
getValueFromValuePair(self, leftValue, rightValue)
Get value from comparison.
+ +
+Methods inherited from EvaluatorAddition:
+
executePair(self, evaluators, evaluatorIndex)
Add two evaluators.
+ +
getEvaluatedValues(self, enumerable, keys, value)
Get evaluatedValues.
+ +
getOperationValue(self, leftValue, rightValue)
Get operation value.
+ +
+Methods inherited from Evaluator:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorLessEqual(EvaluatorEqual)
   Class to compare two evaluators.
 
 
Method resolution order:
+
EvaluatorLessEqual
+
EvaluatorEqual
+
EvaluatorAddition
+
Evaluator
+
+
+Methods defined here:
+
getBooleanFromValuePair(self, leftValue, rightValue)
Compare two values.
+ +
+Methods inherited from EvaluatorEqual:
+
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
getValueFromValuePair(self, leftValue, rightValue)
Get value from comparison.
+ +
+Methods inherited from EvaluatorAddition:
+
executePair(self, evaluators, evaluatorIndex)
Add two evaluators.
+ +
getEvaluatedValues(self, enumerable, keys, value)
Get evaluatedValues.
+ +
getOperationValue(self, leftValue, rightValue)
Get operation value.
+ +
+Methods inherited from Evaluator:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorLocal(EvaluatorElement)
   Class to get a local variable.
 
 
Method resolution order:
+
EvaluatorLocal
+
EvaluatorElement
+
Evaluator
+
+
+Methods defined here:
+
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
+Methods inherited from EvaluatorElement:
+
__init__(self, elementNode, word)
Set value to none.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
+Methods inherited from Evaluator:
+
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorModulo(EvaluatorDivision)
   Class to modulo two evaluators.
 
 
Method resolution order:
+
EvaluatorModulo
+
EvaluatorDivision
+
EvaluatorAddition
+
Evaluator
+
+
+Methods defined here:
+
getValueFromValuePair(self, leftValue, rightValue)
Modulo two values.
+ +
+Methods inherited from EvaluatorDivision:
+
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
+Methods inherited from EvaluatorAddition:
+
executePair(self, evaluators, evaluatorIndex)
Add two evaluators.
+ +
getEvaluatedValues(self, enumerable, keys, value)
Get evaluatedValues.
+ +
getOperationValue(self, leftValue, rightValue)
Get operation value.
+ +
+Methods inherited from Evaluator:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorMultiplication(EvaluatorDivision)
   Class to multiply two evaluators.
 
 
Method resolution order:
+
EvaluatorMultiplication
+
EvaluatorDivision
+
EvaluatorAddition
+
Evaluator
+
+
+Methods defined here:
+
getValueFromValuePair(self, leftValue, rightValue)
Multiply two values.
+ +
+Methods inherited from EvaluatorDivision:
+
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
+Methods inherited from EvaluatorAddition:
+
executePair(self, evaluators, evaluatorIndex)
Add two evaluators.
+ +
getEvaluatedValues(self, enumerable, keys, value)
Get evaluatedValues.
+ +
getOperationValue(self, leftValue, rightValue)
Get operation value.
+ +
+Methods inherited from Evaluator:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorNone(Evaluator)
   Class to evaluate None.
 
 Methods defined here:
+
__init__(self, elementNode, word)
Set value to none.
+ +
+Methods inherited from Evaluator:
+
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorNot(EvaluatorSubtraction)
   Class to compare two evaluators.
 
 
Method resolution order:
+
EvaluatorNot
+
EvaluatorSubtraction
+
EvaluatorAddition
+
Evaluator
+
+
+Methods defined here:
+
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Minus the value to the right.
+ +
getValueFromSingleValue(self, value)
Minus value.
+ +
+Methods inherited from EvaluatorSubtraction:
+
executeLeft(self, evaluators, evaluatorIndex)
Minus the value to the right.
+ +
getNegativeValue(self, value)
Get the negative value.
+ +
getValueFromValuePair(self, leftValue, rightValue)
Subtract two values.
+ +
+Methods inherited from EvaluatorAddition:
+
executePair(self, evaluators, evaluatorIndex)
Add two evaluators.
+ +
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
getEvaluatedValues(self, enumerable, keys, value)
Get evaluatedValues.
+ +
getOperationValue(self, leftValue, rightValue)
Get operation value.
+ +
+Methods inherited from Evaluator:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorNotEqual(EvaluatorEqual)
   Class to compare two evaluators.
 
 
Method resolution order:
+
EvaluatorNotEqual
+
EvaluatorEqual
+
EvaluatorAddition
+
Evaluator
+
+
+Methods defined here:
+
getBooleanFromValuePair(self, leftValue, rightValue)
Compare two values.
+ +
+Methods inherited from EvaluatorEqual:
+
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
getValueFromValuePair(self, leftValue, rightValue)
Get value from comparison.
+ +
+Methods inherited from EvaluatorAddition:
+
executePair(self, evaluators, evaluatorIndex)
Add two evaluators.
+ +
getEvaluatedValues(self, enumerable, keys, value)
Get evaluatedValues.
+ +
getOperationValue(self, leftValue, rightValue)
Get operation value.
+ +
+Methods inherited from Evaluator:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorNumeric(Evaluator)
   Class to evaluate a string.
 
 Methods defined here:
+
__init__(self, elementNode, word)
Set value.
+ +
+Methods inherited from Evaluator:
+
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorOr(EvaluatorAnd)
   Class to compare two evaluators.
 
 
Method resolution order:
+
EvaluatorOr
+
EvaluatorAnd
+
EvaluatorAddition
+
Evaluator
+
+
+Methods defined here:
+
getBooleanFromValuePair(self, leftValue, rightValue)
Or two values.
+ +
+Methods inherited from EvaluatorAnd:
+
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
getValueFromValuePair(self, leftValue, rightValue)
Get value from comparison.
+ +
+Methods inherited from EvaluatorAddition:
+
executePair(self, evaluators, evaluatorIndex)
Add two evaluators.
+ +
getEvaluatedValues(self, enumerable, keys, value)
Get evaluatedValues.
+ +
getOperationValue(self, leftValue, rightValue)
Get operation value.
+ +
+Methods inherited from Evaluator:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorPower(EvaluatorAddition)
   Class to power two evaluators.
 
 
Method resolution order:
+
EvaluatorPower
+
EvaluatorAddition
+
Evaluator
+
+
+Methods defined here:
+
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
getValueFromValuePair(self, leftValue, rightValue)
Power of two values.
+ +
+Methods inherited from EvaluatorAddition:
+
executePair(self, evaluators, evaluatorIndex)
Add two evaluators.
+ +
getEvaluatedValues(self, enumerable, keys, value)
Get evaluatedValues.
+ +
getOperationValue(self, leftValue, rightValue)
Get operation value.
+ +
+Methods inherited from Evaluator:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorSelf(EvaluatorElement)
   Class to handle self.
 
 
Method resolution order:
+
EvaluatorSelf
+
EvaluatorElement
+
Evaluator
+
+
+Methods defined here:
+
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
+Methods inherited from EvaluatorElement:
+
__init__(self, elementNode, word)
Set value to none.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
+Methods inherited from Evaluator:
+
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorSubtraction(EvaluatorAddition)
   Class to subtract two evaluators.
 
 
Method resolution order:
+
EvaluatorSubtraction
+
EvaluatorAddition
+
Evaluator
+
+
+Methods defined here:
+
executeLeft(self, evaluators, evaluatorIndex)
Minus the value to the right.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Minus the value to the right.
+ +
getNegativeValue(self, value)
Get the negative value.
+ +
getValueFromSingleValue(self, value)
Minus value.
+ +
getValueFromValuePair(self, leftValue, rightValue)
Subtract two values.
+ +
+Methods inherited from EvaluatorAddition:
+
executePair(self, evaluators, evaluatorIndex)
Add two evaluators.
+ +
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
getEvaluatedValues(self, enumerable, keys, value)
Get evaluatedValues.
+ +
getOperationValue(self, leftValue, rightValue)
Get operation value.
+ +
+Methods inherited from Evaluator:
+
__init__(self, elementNode, word)
Set value to none.
+ +
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorTrue(Evaluator)
   Class to evaluate a string.
 
 Methods defined here:
+
__init__(self, elementNode, word)
Set value to true.
+ +
+Methods inherited from Evaluator:
+
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class EvaluatorValue(Evaluator)
   Class to evaluate a string.
 
 Methods defined here:
+
__init__(self, word)
Set value to none.
+ +
+Methods inherited from Evaluator:
+
__repr__(self)
Get the string representation of this Class.
+ +
executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators)
Execute the bracket.
+ +
executeCenterOperation(self, evaluators, evaluatorIndex)
Execute operator which acts on the center.
+ +
executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the dictionary.
+ +
executeFunction(self, evaluators, evaluatorIndex, nextEvaluator)
Execute the function.
+ +
executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel)
Execute operator which acts from the left.
+ +
executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the key index.
+ +
executePairOperation(self, evaluators, evaluatorIndex, operationLevel)
Operate on two evaluators.
+ +
executeRightOperation(self, evaluators, evaluatorIndex)
Execute operator which acts from the right.
+ +
executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator)
Execute the string.
+ +
getIsInRange(self, keyIndex)
Determine if the keyIndex is in range.
+ +

+ + + + + + + +
 
+class Function(BaseFunction)
   Class to get equation results.
 
 Methods defined here:
+
__init__(self, elementNode)
Initialize.
+ +
getReturnValueWithoutDeletion(self)
Get return value without deleting last function.
+ +
+Methods inherited from BaseFunction:
+
__repr__(self)
Get the string representation of this Class.
+ +
getReturnValue(self)
Get return value.
+ +
processChildNodes(self, elementNode)
Process childNodes if shouldReturn is false.
+ +

+ + + + + + + +
 
+class FunctionVariable
   Class to hold class functions and variable set.
 
 Methods defined here:
+
__init__(self, elementNode)
Initialize.
+ +
addToVariableSet(self, elementNode)
Add to variables.
+ +
processClass(self, elementNode)
Add class to FunctionVariable.
+ +
processFunction(self, elementNode)
Add function to function dictionary.
+ +
processStatement(self, elementNode)
Add self statement to variables.
+ +

+ + + + + + + +
 
+class KeyValue
   Class to hold a key value.
 
 Methods defined here:
+
__init__(self, key=None, value=None)
Get key value.
+ +
__repr__(self)
Get the string representation of this KeyValue.
+ +
getByCharacter(self, character, line)
Get by character.
+ +
getByDot(self, line)
Get by dot.
+ +
getByEqual(self, line)
Get by dot.
+ +

+ + + + + + + +
 
+class ModuleElementNode
   Class to get the in attribute, the index name and the value name.
 
 Methods defined here:
+
__init__(self, elementNode)
Initialize.
+ +
processElse(self, elementNode)
Process the else statement.
+ +

+ + + + + +
 
+Functions
       
addPrefixDictionary(dictionary, keys, value)
Add prefixed key values to dictionary.
+
addQuoteWord(evaluatorWords, word)
Add quote word and remainder if the word starts with a quote character or dollar sign, otherwise add the word.
+
addToPathsRecursively(paths, vector3Lists)
Add to vector3 paths recursively.
+
addValueToEvaluatedDictionary(elementNode, evaluatedDictionary, key)
Get the evaluated dictionary.
+
addVector3ToElementNode(elementNode, key, vector3)
Add vector3 to xml element.
+
compareExecutionOrderAscending(module, otherModule)
Get comparison in order to sort modules in ascending execution order.
+
convertToPaths(dictionary)
Recursively convert any ElementNodes to paths.
+
convertToTransformedPaths(dictionary)
Recursively convert any ElementNodes to paths.
+
executeLeftOperations(evaluators, operationLevel)
Evaluate the expression value from the numeric and operation evaluators.
+
executeNextEvaluatorArguments(evaluator, evaluators, evaluatorIndex, nextEvaluator)
Execute the nextEvaluator arguments.
+
executePairOperations(evaluators, operationLevel)
Evaluate the expression value from the numeric and operation evaluators.
+
getBracketEvaluators(bracketBeginIndex, bracketEndIndex, evaluators)
Get the bracket evaluators.
+
getBracketValuesDeleteEvaluator(bracketBeginIndex, bracketEndIndex, evaluators)
Get the bracket values and delete the evaluator.
+
getBracketsExist(evaluators)
Evaluate the expression value.
+
getCapitalizedSuffixKey(prefix, suffix)
Get key with capitalized suffix.
+
getDictionarySplitWords(dictionary, value)
Get split line for evaluators.
+
getElementNodeByKey(elementNode, key)
Get the xml element by key.
+
getElementNodeObject(evaluatedLinkValue)
Get ElementNodeObject.
+
getElementNodesByKey(elementNode, key)
Get the xml elements by key.
+
getEndIndexConvertEquationValue(bracketEndIndex, evaluatorIndex, evaluators)
Get the bracket end index and convert the equation value evaluators into a string.
+
getEvaluatedBoolean(defaultValue, elementNode, key)
Get the evaluated boolean.
+
getEvaluatedDictionaryByCopyKeys(copyKeys, elementNode)
Get the evaluated dictionary by copyKeys.
+
getEvaluatedDictionaryByEvaluationKeys(elementNode, evaluationKeys)
Get the evaluated dictionary.
+
getEvaluatedExpressionValue(elementNode, value)
Evaluate the expression value.
+
getEvaluatedExpressionValueBySplitLine(elementNode, words)
Evaluate the expression value.
+
getEvaluatedExpressionValueEvaluators(evaluators)
Evaluate the expression value from the numeric and operation evaluators.
+
getEvaluatedFloat(defaultValue, elementNode, key)
Get the evaluated float.
+
getEvaluatedInt(defaultValue, elementNode, key)
Get the evaluated int.
+
getEvaluatedIntByKeys(defaultValue, elementNode, keys)
Get the evaluated int by keys.
+
getEvaluatedLinkValue(elementNode, word)
Get the evaluated link value.
+
getEvaluatedString(defaultValue, elementNode, key)
Get the evaluated string.
+
getEvaluatedValue(defaultValue, elementNode, key)
Get the evaluated value.
+
getEvaluatedValueObliviously(elementNode, key)
Get the evaluated value.
+
getEvaluator(elementNode, evaluators, nextWord, word)
Get the evaluator.
+
getEvaluatorSplitWords(value)
Get split words for evaluators.
+
getFloatListFromBracketedString(bracketedString)
Get list from a bracketed string.
+
getFloatListListsByPaths(paths)
Get float lists by paths.
+
getIntFromFloatString(value)
Get the int from the string.
+
getIsBracketed(word)
Determine if the word is bracketed.
+
getIsQuoted(word)
Determine if the word is quoted.
+
getKeys(repository)
Get keys for repository.
+
getLocalAttributeValueString(key, valueString)
Get the local attribute value string with augmented assignment.
+
getMatchingPlugins(elementNode, namePathDictionary)
Get the plugins whose names are in the attribute dictionary.
+
getNextChildIndex(elementNode)
Get the next childNode index.
+
getPathByKey(defaultPath, elementNode, key)
Get path from prefix and xml element.
+
getPathByList(vertexList)
Get the paths by list.
+
getPathByPrefix(elementNode, path, prefix)
Get path from prefix and xml element.
+
getPathsByKey(defaultPaths, elementNode, key)
Get paths by key.
+
getPathsByLists(vertexLists)
Get paths by lists.
+
getRadiusArealizedBasedOnAreaRadius(elementNode, radius, sides)
Get the areal radius from the radius, number of sides and cascade radiusAreal.
+
getSidesBasedOnPrecision(elementNode, radius)
Get the number of polygon sides.
+
getSidesMinimumThreeBasedOnPrecision(elementNode, radius)
Get the number of polygon sides, with a minimum of three.
+
getSidesMinimumThreeBasedOnPrecisionSides(elementNode, radius)
Get the number of polygon sides, with a minimum of three.
+
getSplitDictionary()
Get split dictionary.
+
getStartsWithCurlyEqualRoundSquare(word)
Determine if the word starts with round or square brackets.
+
getTokenByNumber(number)
Get token by number.
+
getTransformedPathByKey(defaultTransformedPath, elementNode, key)
Get transformed path from prefix and xml element.
+
getTransformedPathByPrefix(elementNode, path, prefix)
Get path from prefix and xml element.
+
getTransformedPathsByKey(defaultTransformedPaths, elementNode, key)
Get transformed paths by key.
+
getUniqueQuoteIndex(uniqueQuoteIndex, word)
Get uniqueQuoteIndex.
+
getUniqueToken(word)
Get unique token.
+
getVector3ByDictionary(dictionary, vector3)
Get vector3 by dictionary.
+
getVector3ByDictionaryListValue(value, vector3)
Get vector3 by dictionary, list or value.
+
getVector3ByFloatList(floatList, vector3)
Get vector3 by float list.
+
getVector3ByMultiplierPrefix(elementNode, multiplier, prefix, vector3)
Get vector3 from multiplier, prefix and xml element.
+
getVector3ByMultiplierPrefixes(elementNode, multiplier, prefixes, vector3)
Get vector3 from multiplier, prefixes and xml element.
+
getVector3ByPrefix(defaultVector3, elementNode, prefix)
Get vector3 from prefix and xml element.
+
getVector3ByPrefixes(elementNode, prefixes, vector3)
Get vector3 from prefixes and xml element.
+
getVector3FromElementNode(elementNode)
Get vector3 from xml element.
+
getVector3IfNone(vector3)
Get new vector3 if the original vector3 is none.
+
getVector3ListsRecursively(floatLists)
Get vector3 lists recursively.
+
getVisibleObjects(archivableObjects)
Get the visible objects.
+
processArchivable(archivableClass, elementNode)
Get any new elements and process the archivable.
+
processCondition(elementNode)
Process the xml element condition.
+
removeIdentifiersFromDictionary(dictionary)
Remove the identifier elements from a dictionary.
+
setAttributesByArguments(argumentNames, arguments, elementNode)
Set the attribute dictionary to the arguments.
+
setFunctionLocalDictionary(arguments, function)
Evaluate the function statement and delete the evaluators.
+
setLocalAttribute(elementNode)
Set the local attribute if any.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalCreationDictionary = {'circle': '/home/enrique/Desktop/backup/babbleold/script/re...eus/fabmetheus_utilities/geometry/creation/circle', 'concatenate': '/home/enrique/Desktop/backup/babbleold/script/re...abmetheus_utilities/geometry/creation/concatenate', 'drill': '/home/enrique/Desktop/backup/babbleold/script/re...eus/fabmetheus_utilities/geometry/creation/_drill', 'extrude': '/home/enrique/Desktop/backup/babbleold/script/re...us/fabmetheus_utilities/geometry/creation/extrude', 'gear': '/home/enrique/Desktop/backup/babbleold/script/re...theus/fabmetheus_utilities/geometry/creation/gear', 'grid': '/home/enrique/Desktop/backup/babbleold/script/re...theus/fabmetheus_utilities/geometry/creation/grid', 'heightmap': '/home/enrique/Desktop/backup/babbleold/script/re.../fabmetheus_utilities/geometry/creation/heightmap', 'lathe': '/home/enrique/Desktop/backup/babbleold/script/re...heus/fabmetheus_utilities/geometry/creation/lathe', 'line': '/home/enrique/Desktop/backup/babbleold/script/re...theus/fabmetheus_utilities/geometry/creation/line', 'linearbearingcage': '/home/enrique/Desktop/backup/babbleold/script/re...s_utilities/geometry/creation/linear_bearing_cage', ...}
+globalDictionaryOperatorBegin = {'!=': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorNotEqual>, '**': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorPower>, '<=': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorLessEqual>, '==': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorEqual>, '>=': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorGreaterEqual>, '||': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorConcatenate>}
+globalElementNameSet = set(['creation', 'document', 'setting'])
+globalFundamentalNameSet = set(['_math', 'euclid', 'measure', 'print'])
+globalModuleEvaluatorDictionary = {'creation': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorElement>, 'document': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorElement>, 'euclid': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorFundamental>, 'math': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorFundamental>, 'measure': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorFundamental>, 'print': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorFundamental>, 'self': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorSelf>, 'setting': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorElement>}
+globalModuleFunctionsDictionary = {}
+globalSplitDictionary = {'!=': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorNotEqual>, '%': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorModulo>, '(': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorBracketRound>, ')': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.Evaluator>, '*': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorMultiplication>, '**': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorPower>, '+': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorAddition>, ',': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorComma>, '-': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorSubtraction>, '/': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorDivision>, ...}
+globalSplitDictionaryOperator = {'%': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorModulo>, '(': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorBracketRound>, ')': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.Evaluator>, '*': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorMultiplication>, '+': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorAddition>, ',': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorComma>, '-': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorSubtraction>, '/': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorDivision>, ':': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorDictionary>, '<': <class fabmetheus_utilities.geometry.geometry_utilities.evaluate.EvaluatorLess>, ...}

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.creation.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.creation.html new file mode 100644 index 0000000..2b122d5 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.creation.html @@ -0,0 +1,76 @@ + + +Python: module fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.creation + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.creation ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_utilities/evaluate_elements/creation.py
+

Boolean geometry utilities.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.archive
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+
fabmetheus_utilities.gcodec
+

+ + + + + +
 
+Classes
       
+
Creation +
+

+ + + + + + + +
 
+class Creation
   Class to handle a creation.
 
 Methods defined here:
+
__init__(self, elementNode, pluginModule)
Initialize.
+ +
__repr__(self)
Get the string representation of this creation.
+ +
getCreation(self, *arguments)
Get creation.
+ +

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.document.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.document.html new file mode 100644 index 0000000..dcdf5ca --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.document.html @@ -0,0 +1,95 @@ + + +Python: module fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.document + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.document ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_utilities/evaluate_elements/document.py
+

Boolean geometry utilities.

+

+ + + + + +
 
+Modules
       
__init__
+

+ + + + + +
 
+Classes
       
+
Document +
+

+ + + + + + + +
 
+class Document
   Class to handle elementNodes in a document.
 
 Methods defined here:
+
__init__(self, elementNode)
Initialize.
+ +
__repr__(self)
Get the string representation of this Document.
+ +
getCascadeBoolean(self, defaultBoolean, key)
Get cascade boolean.
+ +
getCascadeFloat(self, defaultFloat, key)
Get cascade float.
+ +
getDocumentElement(self)
Get document element element.
+ +
getElementByID(self, idKey)
Get element by id.
+ +
getElementsByName(self, nameKey)
Get element by name.
+ +
getElementsByTag(self, tagKey)
Get element by tag.
+ +
getParentNode(self)
Get parentNode element.
+ +
getPrevious(self)
Get previous element.
+ +
getPreviousElement(self)
Get previous element.
+ +
getPreviousVertex(self)
Get previous element.
+ +
getSelfElement(self)
Get self element.
+ +

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalAccessibleAttributeDictionary = ['getCascadeBoolean', 'getCascadeFloat', 'getDocumentElement', 'getElementByID', 'getElementsByName', 'getElementsByTag', 'getParentNode', 'getPrevious', 'getPreviousElement', 'getPreviousVertex', 'getSelfElement']
+globalGetAccessibleAttributeSet = set(['getCascadeBoolean', 'getCascadeFloat', 'getDocumentElement', 'getElementByID', 'getElementsByName', 'getElementsByTag', ...])

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.html new file mode 100644 index 0000000..187398b --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.html @@ -0,0 +1,33 @@ + + +Python: package fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_utilities/evaluate_elements/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
creation
+
document
+
setting
+

+ + + + + +
 
+Data
       level = 4
+numberOfLevelsDeepInPackageHierarchy = 4
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.setting.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.setting.html new file mode 100644 index 0000000..12b65fe --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.setting.html @@ -0,0 +1,122 @@ + + +Python: module fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.setting + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.setting ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_utilities/evaluate_elements/setting.py
+

Boolean geometry utilities.

+

+ + + + + +
 
+Modules
       
__init__
+
math
+
skeinforge_application.skeinforge_utilities.skeinforge_craft
+

+ + + + + +
 
+Classes
       
+
Setting +
+

+ + + + + + + +
 
+class Setting
   Class to get handle elementNodes in a setting.
 
 Methods defined here:
+
__init__(self, elementNode)
Initialize.
+ +
__repr__(self)
Get the string representation of this Setting.
+ +
getEdgeWidth(self)
Get the edge width.
+ +
getImportCoarseness(self)
Get the importCoarseness.
+ +
getImportRadius(self)
Get the importRadius.
+ +
getInteriorOverhangAngle(self)
Get the interior overhang support angle in degrees.
+ +
getInteriorOverhangRadians(self)
Get the interior overhang support angle in radians.
+ +
getLayerHeight(self)
Get the layer height.
+ +
getOverhangAngle(self)
Get the overhang support angle in degrees.
+ +
getOverhangRadians(self)
Get the overhang support angle in radians.
+ +
getOverhangSpan(self)
Get the overhang span.
+ +
getPrecision(self)
Get the cascade precision.
+ +
getSheetThickness(self)
Get the sheet thickness.
+ +
getTwistPrecision(self)
Get the twist precision in degrees.
+ +
getTwistPrecisionRadians(self)
Get the twist precision in radians.
+ +

+ + + + + +
 
+Functions
       
getCascadeFloatWithoutSelf(defaultFloat, elementNode, key)
Get the cascade float.
+
getEdgeWidth(elementNode)
Get the edge width.
+
getImportCoarseness(elementNode, preferences=None)
Get the importCoarseness.
+
getImportRadius(elementNode)
Get the importRadius.
+
getInteriorOverhangAngle(elementNode)
Get the interior overhang support angle in degrees.
+
getInteriorOverhangRadians(elementNode)
Get the interior overhang support angle in radians.
+
getLayerHeight(elementNode)
Get the layer height.
+
getOverhangAngle(elementNode)
Get the overhang support angle in degrees.
+
getOverhangRadians(elementNode)
Get the overhang support angle in radians.
+
getOverhangSpan(elementNode)
Get the overhang span.
+
getPrecision(elementNode)
Get the cascade precision.
+
getSheetThickness(elementNode)
Get the sheet thickness.
+
getTwistPrecision(elementNode)
Get the twist precision in degrees.
+
getTwistPrecisionRadians(elementNode)
Get the twist precision in radians.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalAccessibleAttributeDictionary = ['getEdgeWidth', 'getImportCoarseness', 'getImportRadius', 'getInteriorOverhangAngle', 'getInteriorOverhangRadians', 'getLayerHeight', 'getOverhangSpan', 'getOverhangAngle', 'getOverhangRadians', 'getPrecision', 'getSheetThickness', 'getTwistPrecision', 'getTwistPrecisionRadians']
+globalGetAccessibleAttributeSet = set(['getEdgeWidth', 'getImportCoarseness', 'getImportRadius', 'getInteriorOverhangAngle', 'getInteriorOverhangRadians', 'getLayerHeight', ...])

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables.dictionary_attribute.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables.dictionary_attribute.html new file mode 100644 index 0000000..4453474 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables.dictionary_attribute.html @@ -0,0 +1,96 @@ + + +Python: module fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables.dictionary_attribute + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables.dictionary_attribute ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_utilities/evaluate_enumerables/dictionary_attribute.py
+

Dictionary object attributes.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.euclidean
+

+ + + + + +
 
+Classes
       
+
DictionaryAttribute +
+

+ + + + + + + +
 
+class DictionaryAttribute
   Class to handle a dictionary.
 
 Methods defined here:
+
__init__(self, dictionaryObject)
Initialize.
+ +
__repr__(self)
Get the dictionary representation of this DictionaryAttribute.
+ +
count(self, value)
Get the count.
+ +
delete(self, arguments)
Get the delete dictionary.
+ +
getIsIn(self, value)
Determine if the value is in.
+ +
getIsNotIn(self, value)
Determine if the value is in.
+ +
getLength(self)
Get the length.
+ +
getMax(self)
Get the max.
+ +
getMin(self)
Get the min.
+ +
index(self, value)
Get the index element.
+ +
length(self)
Get the length.
+ +
set(self, itemIndex, value)
Set value.
+ +

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalAccessibleAttributeDictionary = ['count', 'delete', 'getIsIn', 'getIsNotIn', 'getLength', 'getMax', 'getMin', 'index', 'length', 'set']
+globalGetAccessibleAttributeSet = set(['count', 'delete', 'getIsIn', 'getIsNotIn', 'getLength', 'getMax', ...])
+globalNativeFunctionSet = set(['clear', 'copy', 'fromkeys', 'get', 'items', 'keys', ...])
+globalNativeFunctions = ['clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'remove', 'setdefault', 'update', 'values']

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables.html new file mode 100644 index 0000000..d24a0f4 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables.html @@ -0,0 +1,33 @@ + + +Python: package fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_utilities/evaluate_enumerables/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
dictionary_attribute
+
list_attribute
+
string_attribute
+

+ + + + + +
 
+Data
       level = 4
+numberOfLevelsDeepInPackageHierarchy = 4
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables.list_attribute.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables.list_attribute.html new file mode 100644 index 0000000..9b1f5ee --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables.list_attribute.html @@ -0,0 +1,108 @@ + + +Python: module fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables.list_attribute + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables.list_attribute ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_utilities/evaluate_enumerables/list_attribute.py
+

List object attributes.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.euclidean
+

+ + + + + +
 
+Classes
       
+
ListAttribute +
+

+ + + + + + + +
 
+class ListAttribute
   Class to handle a list.
 
 Methods defined here:
+
__init__(self, listObject)
Initialize.
+ +
__repr__(self)
Get the list representation of this ListAttribute.
+ +
add(self, value)
Get the concatenation, same as append.
+ +
copy(self)
Get the copy.
+ +
delete(self, arguments)
Get the delete list.
+ +
get(self, itemIndex)
Get value by index
+ +
getExpansion(self, items)
Get the concatenated copies.
+ +
getIsIn(self, value)
Determine if the value is in.
+ +
getIsNotIn(self, value)
Determine if the value is in.
+ +
getLength(self)
Get the length.
+ +
getMax(self)
Get the max.
+ +
getMin(self)
Get the min.
+ +
insert(self, insertIndex, value)
Get the insert list.
+ +
keys(self)
Get the keys.
+ +
length(self)
Get the length.
+ +
rindex(self, value)
Get the rindex element.
+ +
set(self, itemIndex, value)
Set value.
+ +
values(self, arguments=None)
Get the values.
+ +

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalAccessibleAttributeDictionary = ['add', 'copy', 'count', 'delete', 'get', 'getExpansion', 'getIsIn', 'getIsNotIn', 'getLength', 'getMax', 'getMin', 'insert', 'keys', 'length', 'rindex', 'set', 'values']
+globalGetAccessibleAttributeSet = set(['add', 'copy', 'count', 'delete', 'get', 'getExpansion', ...])
+globalNativeFunctionSet = set(['append', 'extend', 'index', 'pop', 'remove', 'reverse', ...])
+globalNativeFunctions = ['append', 'extend', 'index', 'pop', 'remove', 'reverse', 'sort']

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables.string_attribute.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables.string_attribute.html new file mode 100644 index 0000000..fd0e40b --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables.string_attribute.html @@ -0,0 +1,112 @@ + + +Python: module fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables.string_attribute + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables.string_attribute ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_utilities/evaluate_enumerables/string_attribute.py
+

String object attributes.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.euclidean
+

+ + + + + +
 
+Classes
       
+
StringAttribute +
+

+ + + + + + + +
 
+class StringAttribute
   Class to handle a string.
 
 Methods defined here:
+
__init__(self, stringObject)
Initialize.
+ +
__repr__(self)
Get the string representation of this StringAttribute.
+ +
add(self, nextString)
Get the add string, same as append.
+ +
append(self, nextString)
Get the append string.
+ +
copy(self)
Get the copy.
+ +
delete(self, arguments)
Get the delete string.
+ +
get(self, itemIndex)
Get value by characterIndex
+ +
getExpansion(self, items)
Get the concatenated copies.
+ +
getIsIn(self, value)
Determine if the value is in.
+ +
getIsNotIn(self, value)
Determine if the value is in.
+ +
getLength(self)
Get the length.
+ +
getMax(self)
Get the max.
+ +
getMin(self)
Get the min.
+ +
insert(self, insertIndex, value)
Get the insert string.
+ +
keys(self)
Get the keys.
+ +
length(self)
Get the length.
+ +
remove(self, value)
Get the remove string.
+ +
reverse(self)
Get the reverse string.
+ +
set(self, itemIndex, value)
Set value.
+ +
values(self)
Get the values.
+ +

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalAccessibleAttributeDictionary = ['add', 'append', 'copy', 'delete', 'get', 'getExpansion', 'getIsIn', 'getIsNotIn', 'getLength', 'getMax', 'getMin', 'insert', 'keys', 'length', 'remove', 'reverse', 'set', 'values']
+globalGetAccessibleAttributeSet = set(['add', 'append', 'copy', 'delete', 'get', 'getExpansion', ...])
+globalNativeFunctionSet = set(['capitalize', 'center', 'count', 'decode', 'encode', 'endswith', ...])
+globalNativeFunctions = ['capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'join', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'ljust', 'lower', ...]

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals._math.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals._math.html new file mode 100644 index 0000000..a488674 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals._math.html @@ -0,0 +1,71 @@ + + +Python: module fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals._math + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals._math ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_utilities/evaluate_fundamentals/_math.py
+

Boolean geometry utilities.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.euclidean
+
math
+

+ + + + + +
 
+Functions
       
getAbs(value)
Get the abs.
+
getBoolean(value)
Get the boolean.
+
getDivmod(x, y)
Get the divmod.
+
getFloat(value)
Get the float.
+
getHex(value)
Get the hex.
+
getInt(value)
Get the int.
+
getLong(value)
Get the long.
+
getMax(first, second)
Get the max.
+
getMin(first, second)
Get the min.
+
getRound(value)
Get the round.
+
getString(value)
Get the string.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalAccessibleAttributeDictionary = {'abs': <function getAbs>, 'boolean': <function getBoolean>, 'divmod': <function getDivmod>, 'float': <function getFloat>, 'hex': <function getHex>, 'int': <function getInt>, 'long': <function getLong>, 'max': <function getMax>, 'min': <function getMin>, 'round': <function getRound>, ...}
+globalMathConstantDictionary = {'euler': 0.5772156649015329, 'golden': 1.618033988749895, 'goldenAngle': 3.883222077450933, 'goldenRatio': 1.618033988749895, 'tau': 6.283185307179586}
+globalNativeFunctionSet = set(['acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', ...])
+globalNativeFunctions = ['acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'cosh', 'degrees', 'e', 'exp', 'fabs', 'floor', 'fmod', 'frexp', 'hypot', 'ldexp', 'log', 'log10', 'modf', 'pi', ...]

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals.euclid.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals.euclid.html new file mode 100644 index 0000000..2cb29f0 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals.euclid.html @@ -0,0 +1,89 @@ + + +Python: module fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals.euclid + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals.euclid ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_utilities/evaluate_fundamentals/euclid.py
+

Boolean geometry utilities.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.euclidean
+
math
+

+ + + + + +
 
+Classes
       
+
NestedVectorTestExample +
+

+ + + + + + + +
 
+class NestedVectorTestExample
   Class to test local attribute.
 
 Methods defined here:
+
__init__(self, vector3)
Get the accessible attribute.
+ +

+ + + + + +
 
+Functions
       
getComplex(x=0.0, y=0.0)
Get the complex.
+
getCylindrical(azimuthDegrees, radius=1.0, z=0.0)
Get the cylindrical vector3 by degrees.
+
getCylindricalByRadians(azimuthRadians, radius=1.0, z=0.0)
Get the cylindrical vector3 by radians.
+
getNestedVectorTestExample(x=0.0, y=0.0, z=0.0)
Get the NestedVectorTestExample.
+
getPolar(angleDegrees, radius=1.0)
Get the complex polar by degrees.
+
getPolarByRadians(angleRadians, radius=1.0)
Get the complex polar by radians.
+
getSpherical(azimuthDegrees, elevationDegrees, radius=1.0)
Get the spherical vector3 unit by degrees.
+
getSphericalByRadians(azimuthRadians, elevationRadians, radius=1.0)
Get the spherical vector3 unit by radians.
+
getVector3(x=0.0, y=0.0, z=0.0)
Get the vector3.
+
getVector3Index(index=0, x=0.0, y=0.0, z=0.0)
Get the vector3.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalAccessibleAttributeDictionary = {'NestedVectorTestExample': <function getNestedVectorTestExample>, 'Vector3': <function getVector3>, 'Vector3Index': <function getVector3Index>, 'complex': <function getComplex>, 'getCylindrical': <function getCylindrical>, 'getCylindricalByRadians': <function getCylindricalByRadians>, 'getPolar': <function getPolar>, 'getPolarByRadians': <function getPolarByRadians>, 'getSpherical': <function getSpherical>, 'getSphericalByRadians': <function getSphericalByRadians>}

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals.html new file mode 100644 index 0000000..c5e4cca --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals.html @@ -0,0 +1,34 @@ + + +Python: package fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_utilities/evaluate_fundamentals/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
_math
+
euclid
+
measure
+
print
+

+ + + + + +
 
+Data
       level = 4
+numberOfLevelsDeepInPackageHierarchy = 4
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals.measure.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals.measure.html new file mode 100644 index 0000000..418fe42 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals.measure.html @@ -0,0 +1,63 @@ + + +Python: module fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals.measure + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals.measure ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_utilities/evaluate_fundamentals/measure.py
+

Boolean geometry utilities.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.euclidean
+
math
+

+ + + + + +
 
+Functions
       
getBoundingBoxByPaths(elementNode)
Get bounding box of the transformed paths of the xmlObject of the elementNode.
+
getCenterByPaths(elementNode)
Get center of the transformed paths of the xmlObject of the elementNode.
+
getExtentByPaths(elementNode)
Get extent of the transformed paths of the xmlObject of the elementNode.
+
getInradiusByPaths(elementNode)
Get inradius of the transformed paths of the xmlObject of the elementNode.
+
getMaximumByPaths(elementNode)
Get maximum of the transformed paths of the xmlObject of the elementNode.
+
getMinimumByPaths(elementNode)
Get minimum of the transformed paths of the xmlObject of the elementNode.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalAccessibleAttributeDictionary = {'getBoundingBoxByPaths': <function getBoundingBoxByPaths>, 'getCenterByPaths': <function getCenterByPaths>, 'getExtentByPaths': <function getExtentByPaths>, 'getInradiusByPaths': <function getInradiusByPaths>, 'getMaximumByPaths': <function getMaximumByPaths>, 'getMinimumByPaths': <function getMinimumByPaths>}

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals.print.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals.print.html new file mode 100644 index 0000000..d9c5bc0 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals.print.html @@ -0,0 +1,58 @@ + + +Python: module fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals.print + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_utilities.evaluate_fundamentals.print ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_utilities/evaluate_fundamentals/print.py
+

Boolean geometry utilities.

+

+ + + + + +
 
+Modules
       
__init__
+
sys
+

+ + + + + +
 
+Functions
       
continuous(valueString)
Print continuous.
+
line(valueString)
Print line.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalAccessibleAttributeDictionary = {'continuous': <function continuous>, 'line': <function line>}

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.html new file mode 100644 index 0000000..8c66fa7 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.html @@ -0,0 +1,48 @@ + + +Python: package fabmetheus_utilities.geometry.geometry_utilities + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_utilities
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_utilities/__init__.py
+

+Previous / Next / Contents +

+


+This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Package Contents
       
boolean_geometry
+boolean_solid
+
evaluate
+evaluate_elements (package)
+
evaluate_enumerables (package)
+evaluate_fundamentals (package)
+
matrix
+

+ + + + + +
 
+Data
       level = 3
+numberOfLevelsDeepInPackageHierarchy = 3
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.matrix.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.matrix.html new file mode 100644 index 0000000..88f06d4 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.geometry_utilities.matrix.html @@ -0,0 +1,138 @@ + + +Python: module fabmetheus_utilities.geometry.geometry_utilities.matrix + + + + +
 
+ 
fabmetheus_utilities.geometry.geometry_utilities.matrix ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/geometry_utilities/matrix.py
+

Boolean geometry four by four matrix.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+
cStringIO
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+math
+
fabmetheus_utilities.xml_simple_writer
+

+ + + + + +
 
+Classes
       
+
Matrix +
+

+ + + + + + + +
 
+class Matrix
   A four by four matrix.
 
 Methods defined here:
+
__eq__(self, other)
Determine whether this matrix is identical to other one.
+ +
__init__(self, tetragrid=None)
Add empty lists.
+ +
__ne__(self, other)
Determine whether this vector is not identical to other one.
+ +
__repr__(self)
Get the string representation of this four by four matrix.
+ +
addXML(self, depth, output)
Add xml for this object.
+ +
getAttributes(self, prefix='')
Get the attributes from row column attribute strings, counting from one.
+ +
getFromElementNode(self, elementNode, prefix)
Get the values from row column attribute strings, counting from one.
+ +
getOtherTimesSelf(self, otherTetragrid)
Get this matrix reverse multiplied by the other matrix.
+ +
getSelfTimesOther(self, otherTetragrid)
Get this matrix multiplied by the other matrix.
+ +

+ + + + + +
 
+Functions
       
addVertexes(geometryOutput, vertexes)
Add the vertexes.
+
getBranchMatrix(elementNode)
Get matrix starting from the object if it exists, otherwise get a matrix starting from stratch.
+
getBranchMatrixSetElementNode(elementNode)
Get matrix starting from the object if it exists, otherwise get a matrix starting from stratch.
+
getCumulativeVector3Remove(defaultVector3, elementNode, prefix)
Get cumulative vector3 and delete the prefixed attributes.
+
getDiagonalSwitchedTetragrid(angleDegrees, diagonals)
Get the diagonals and switched matrix by degrees.
+
getDiagonalSwitchedTetragridByPolar(diagonals, unitPolar)
Get the diagonals and switched matrix by unitPolar.
+
getDiagonalSwitchedTetragridByRadians(angleRadians, diagonals)
Get the diagonals and switched matrix by radians.
+
getIdentityTetragrid(tetragrid=None)
Get four by four matrix with diagonal elements set to one.
+
getIsIdentityTetragrid(tetragrid)
Determine if the tetragrid is the identity tetragrid.
+
getIsIdentityTetragridOrNone(tetragrid)
Determine if the tetragrid is None or if it is the identity tetragrid.
+
getKeyA(row, column, prefix='')
Get the a format key string from row & column, counting from zero.
+
getKeyM(row, column, prefix='')
Get the m format key string from row & column, counting from one.
+
getKeysA(prefix='')
Get the matrix keys, counting from zero.
+
getKeysM(prefix='')
Get the matrix keys, counting from one.
+
getRemovedFloat(defaultFloat, elementNode, key, prefix)
Get the float by the key and the prefix.
+
getRemovedFloatByKeys(defaultFloat, elementNode, keys, prefix)
Get the float by the keys and the prefix.
+
getRotateAroundAxisTetragrid(elementNode, prefix)
Get rotate around axis tetragrid and delete the axis and angle attributes.
+
getRotateTetragrid(elementNode, prefix)
Get rotate tetragrid and delete the rotate attributes.
+
getScaleTetragrid(elementNode, prefix)
Get scale matrix and delete the scale attributes.
+
getTetragridA(elementNode, prefix, tetragrid)
Get the tetragrid from the elementNode letter a values.
+
getTetragridC(elementNode, prefix, tetragrid)
Get the matrix Tetragrid from the elementNode letter c values.
+
getTetragridCopy(tetragrid)
Get tetragrid copy.
+
getTetragridM(elementNode, prefix, tetragrid)
Get the tetragrid from the elementNode letter m values.
+
getTetragridMatrix(elementNode, prefix, tetragrid)
Get the tetragrid from the elementNode matrix value.
+
getTetragridR(elementNode, prefix, tetragrid)
Get the tetragrid from the elementNode letter r values.
+
getTetragridTimesOther(firstTetragrid, otherTetragrid)
Get this matrix multiplied by the other matrix.
+
getTransformTetragrid(elementNode, prefix)
Get the tetragrid from the elementNode.
+
getTransformedByList(floatList, point)
Get the point transformed by the array.
+
getTransformedVector3(tetragrid, vector3)
Get the vector3 multiplied by a matrix.
+
getTransformedVector3Blindly(tetragrid, vector3)
Get the vector3 multiplied by a tetragrid without checking if the tetragrid exists.
+
getTransformedVector3s(tetragrid, vector3s)
Get the vector3s multiplied by a matrix.
+
getTranslateTetragrid(elementNode, prefix)
Get translate matrix and delete the translate attributes.
+
getTranslateTetragridByTranslation(translation)
Get translate tetragrid by translation.
+
getVertexes(geometryOutput)
Get the vertexes.
+
setAttributesToMultipliedTetragrid(elementNode, tetragrid)
Set the element attribute dictionary and element matrix to the matrix times the tetragrid.
+
setElementNodeDictionaryMatrix(elementNode, matrix4X4)
Set the element attribute dictionary or element matrix to the matrix.
+
transformVector3Blindly(tetragrid, vector3)
Transform the vector3 by a tetragrid without checking to see if it exists.
+
transformVector3ByMatrix(tetragrid, vector3)
Transform the vector3 by a matrix.
+
transformVector3sByMatrix(tetragrid, vector3s)
Transform the vector3s by a matrix.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalExecutionOrder = 300

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.html new file mode 100644 index 0000000..db1e1d7 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.html @@ -0,0 +1,50 @@ + + +Python: package fabmetheus_utilities.geometry + + + + +
 
+ 
fabmetheus_utilities.geometry
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/__init__.py
+

+Previous / Next / Contents +

+


+This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Package Contents
       
creation (package)
+geometry_tools (package)
+geometry_utilities (package)
+
manipulation_matrix (package)
+manipulation_meta (package)
+manipulation_paths (package)
+
manipulation_shapes (package)
+solids (package)
+statements (package)
+

+ + + + + +
 
+Data
       level = 2
+numberOfLevelsDeepInPackageHierarchy = 2
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_matrix._scale.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_matrix._scale.html new file mode 100644 index 0000000..d93bbd8 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_matrix._scale.html @@ -0,0 +1,86 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_matrix._scale + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_matrix._scale ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_matrix/_scale.py
+

Boolean geometry scale.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.matrix
+
fabmetheus_utilities.geometry.creation.solid
+

+ + + + + +
 
+Classes
       
+
ScaleDerivation +
+

+ + + + + + + +
 
+class ScaleDerivation
   Class to hold scale variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getManipulatedGeometryOutput(elementNode, geometryOutput, prefix)
Get equated geometryOutput.
+
getManipulatedPaths(close, elementNode, loop, prefix, sideLength)
Get equated paths.
+
getNewDerivation(elementNode, prefix, sideLength)
Get new derivation.
+
manipulateElementNode(elementNode, target)
Manipulate the xml element.
+
processElementNode(elementNode)
Process the xml element.
+
scalePoints(elementNode, points, prefix)
Scale the points.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalExecutionOrder = 340

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_matrix.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_matrix.html new file mode 100644 index 0000000..b6ba92a --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_matrix.html @@ -0,0 +1,34 @@ + + +Python: package fabmetheus_utilities.geometry.manipulation_matrix + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_matrix
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_matrix/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
_scale
+
rotate
+
transform
+
translate
+

+ + + + + +
 
+Data
       level = 3
+numberOfLevelsDeepInPackageHierarchy = 3
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_matrix.rotate.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_matrix.rotate.html new file mode 100644 index 0000000..22637e3 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_matrix.rotate.html @@ -0,0 +1,87 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_matrix.rotate + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_matrix.rotate ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_matrix/rotate.py
+

Boolean geometry rotate.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.geometry_utilities.matrix
+
fabmetheus_utilities.geometry.creation.solid
+

+ + + + + +
 
+Classes
       
+
RotateDerivation +
+

+ + + + + + + +
 
+class RotateDerivation
   Class to hold rotate variables.
 
 Methods defined here:
+
__init__(self, elementNode, prefix)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getManipulatedGeometryOutput(elementNode, geometryOutput, prefix)
Get equated geometryOutput.
+
getManipulatedPaths(close, elementNode, loop, prefix, sideLength)
Get equated paths.
+
getNewDerivation(elementNode, prefix, sideLength)
Get new derivation.
+
manipulateElementNode(elementNode, target)
Manipulate the xml element.
+
processElementNode(elementNode)
Process the xml element.
+
rotatePoints(elementNode, points, prefix)
Rotate the points.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalExecutionOrder = 360

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_matrix.transform.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_matrix.transform.html new file mode 100644 index 0000000..c8b11c0 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_matrix.transform.html @@ -0,0 +1,87 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_matrix.transform + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_matrix.transform ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_matrix/transform.py
+

Boolean geometry transform.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.geometry_utilities.matrix
+
fabmetheus_utilities.geometry.creation.solid
+

+ + + + + +
 
+Classes
       
+
TransformDerivation +
+

+ + + + + + + +
 
+class TransformDerivation
   Class to hold transform variables.
 
 Methods defined here:
+
__init__(self, elementNode, prefix)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getManipulatedGeometryOutput(elementNode, geometryOutput, prefix)
Get equated geometryOutput.
+
getManipulatedPaths(close, elementNode, loop, prefix, sideLength)
Get equated paths.
+
getNewDerivation(elementNode, prefix, sideLength)
Get new derivation.
+
manipulateElementNode(elementNode, target)
Manipulate the xml element.
+
processElementNode(elementNode)
Process the xml element.
+
transformPoints(elementNode, points, prefix)
Transform the points.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalExecutionOrder = 320

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_matrix.translate.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_matrix.translate.html new file mode 100644 index 0000000..ec80566 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_matrix.translate.html @@ -0,0 +1,87 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_matrix.translate + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_matrix.translate ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_matrix/translate.py
+

Boolean geometry translation.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.matrix
+
fabmetheus_utilities.geometry.creation.solid
+

+ + + + + +
 
+Classes
       
+
TranslateDerivation +
+

+ + + + + + + +
 
+class TranslateDerivation
   Class to hold translate variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getManipulatedGeometryOutput(elementNode, geometryOutput, prefix)
Get equated geometryOutput.
+
getManipulatedPaths(close, elementNode, loop, prefix, sideLength)
Get equated paths.
+
getNewDerivation(elementNode, prefix, sideLength)
Get new derivation.
+
manipulateElementNode(elementNode, target)
Manipulate the xml element.
+
processElementNode(elementNode)
Process the xml element.
+
translateNegativesPositives(negatives, positives, translation)
Translate the negatives and postives.
+
translatePoints(elementNode, points, prefix)
Translate the points.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalExecutionOrder = 380

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_meta._array.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_meta._array.html new file mode 100644 index 0000000..f4c53a6 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_meta._array.html @@ -0,0 +1,87 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_meta._array + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_meta._array ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_meta/_array.py
+

Boolean geometry array.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+math
+
fabmetheus_utilities.geometry.geometry_utilities.matrix
+fabmetheus_utilities.geometry.geometry_tools.vertex
+

+ + + + + +
 
+Classes
       
+
ArrayDerivation +
+

+ + + + + + + +
 
+class ArrayDerivation
   Class to hold array variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
addPathToGroup(derivation, groupDictionaryCopy, path, targetMatrix, totalIndex)
Add path to the array group.
+
getNewDerivation(elementNode)
Get new derivation.
+
getRotationMatrix(arrayDictionary, derivation, path, point, pointIndex)
Get rotationMatrix.
+
getRotationMatrixByPolar(arrayDictionary, polar, polarLength)
Get rotationMatrix by polar and polarLength.
+
processElementNode(elementNode)
Process the xml element.
+
processElementNodeByDerivation(derivation, elementNode)
Process the xml element by derivation.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_meta._carve.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_meta._carve.html new file mode 100644 index 0000000..3de1be2 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_meta._carve.html @@ -0,0 +1,88 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_meta._carve + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_meta._carve ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_meta/_carve.py
+

Boolean geometry carve.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.geometry.geometry_utilities.boolean_geometry
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.geometry_utilities.matrix
+fabmetheus_utilities.geometry.geometry_tools.path
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.setting
+fabmetheus_utilities.geometry.solids.triangle_mesh
+fabmetheus_utilities.xml_simple_reader
+

+ + + + + +
 
+Classes
       
+
CarveDerivation +
+

+ + + + + + + +
 
+class CarveDerivation
   Class to hold carve variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getLinkedElementNode(idSuffix, parentNode, target)
Get elementNode with identifiers and parentNode.
+
getNewDerivation(elementNode)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+
processElementNodeByDerivation(derivation, elementNode)
Process the xml element by derivation.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_meta._copy.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_meta._copy.html new file mode 100644 index 0000000..2a9f2e6 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_meta._copy.html @@ -0,0 +1,84 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_meta._copy + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_meta._copy ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_meta/_copy.py
+

Boolean geometry copy.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.creation.lineation
+
fabmetheus_utilities.geometry.geometry_utilities.matrix
+fabmetheus_utilities.geometry.creation.solid
+

+ + + + + +
 
+Classes
       
+
CopyDerivation +
+

+ + + + + + + +
 
+class CopyDerivation
   Class to hold copy variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getNewDerivation(elementNode)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+
processElementNodeByDerivation(derivation, elementNode)
Process the xml element by derivation.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_meta.disjoin.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_meta.disjoin.html new file mode 100644 index 0000000..ccc9d50 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_meta.disjoin.html @@ -0,0 +1,89 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_meta.disjoin + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_meta.disjoin ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_meta/disjoin.py
+

Boolean geometry disjoin.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.geometry.geometry_utilities.boolean_geometry
+fabmetheus_utilities.geometry.solids.difference
+
fabmetheus_utilities.euclidean
+fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.geometry_utilities.matrix
+
fabmetheus_utilities.geometry.geometry_tools.path
+fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.setting
+fabmetheus_utilities.geometry.solids.triangle_mesh
+
fabmetheus_utilities.xml_simple_reader
+

+ + + + + +
 
+Classes
       
+
DisjoinDerivation +
+

+ + + + + + + +
 
+class DisjoinDerivation
   Class to hold disjoin variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getLinkedElementNode(idSuffix, parentNode, target)
Get elementNode with identifiers and parentNode.
+
getNewDerivation(elementNode)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+
processElementNodeByDerivation(derivation, elementNode)
Process the xml element by derivation.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_meta.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_meta.html new file mode 100644 index 0000000..38ea6e3 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_meta.html @@ -0,0 +1,36 @@ + + +Python: package fabmetheus_utilities.geometry.manipulation_meta + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_meta
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_meta/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
_array
+_carve
+
_copy
+disjoin
+
import
+write
+

+ + + + + +
 
+Data
       level = 3
+numberOfLevelsDeepInPackageHierarchy = 3
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_meta.import.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_meta.import.html new file mode 100644 index 0000000..09e51b0 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_meta.import.html @@ -0,0 +1,92 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_meta.import + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_meta.import ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_meta/import.py
+

Boolean geometry group of solids.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+cStringIO
+
fabmetheus_utilities.euclidean
+fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+
fabmetheus_utilities.gcodec
+fabmetheus_utilities.geometry.solids.group
+os
+
fabmetheus_utilities.settings
+fabmetheus_utilities.xml_simple_reader
+fabmetheus_utilities.xml_simple_writer
+

+ + + + + +
 
+Classes
       
+
ImportDerivation +
+

+ + + + + + + +
 
+class ImportDerivation
   Class to hold import variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
appendAttributes(fromElementNode, toElementNode)
Append the attributes from the child nodes of fromElementNode to the attributes of toElementNode.
+
getNewDerivation(elementNode)
Get new derivation.
+
getXMLFromCarvingFileName(fileName)
Get xml text from xml text.
+
processElementNode(elementNode)
Process the xml element.
+
processElementNodeByDerivation(derivation, elementNode)
Process the xml element by derivation.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_meta.write.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_meta.write.html new file mode 100644 index 0000000..6e4845e --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_meta.write.html @@ -0,0 +1,87 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_meta.write + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_meta.write ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_meta/write.py
+

Boolean geometry write.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+
fabmetheus_utilities.euclidean
+fabmetheus_utilities.geometry.geometry_utilities.evaluate
+
fabmetheus_utilities.gcodec
+fabmetheus_utilities.geometry.geometry_utilities.matrix
+
os
+

+ + + + + +
 
+Classes
       
+
WriteDerivation +
+

+ + + + + + + +
 
+class WriteDerivation
   Class to hold write variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getNewDerivation(elementNode)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+
processElementNodeByDerivation(derivation, elementNode)
Process the xml element by derivation.
+
writeElementNode(derivation, fileNames, target)
Write a quantity of the target.
+
writeXMLObject(absoluteFolderDirectory, derivation, fileNames, target, xmlObject)
Write one instance of the xmlObject.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.bevel.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.bevel.html new file mode 100644 index 0000000..f90d921 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.bevel.html @@ -0,0 +1,84 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_paths.bevel + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_paths.bevel ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_paths/bevel.py
+

Add material to support overhang or remove material at the overhang angle.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+
fabmetheus_utilities.geometry.creation.lineation
+

+ + + + + +
 
+Classes
       
+
BevelDerivation +
+

+ + + + + + + +
 
+class BevelDerivation
   Class to hold bevel variables.
 
 Methods defined here:
+
__init__(self, elementNode, prefix, sideLength)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getBevelPath(begin, center, close, end, radius)
Get bevel path.
+
getManipulatedPaths(close, elementNode, loop, prefix, sideLength)
Get bevel loop.
+
getNewDerivation(elementNode, prefix, sideLength)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalExecutionOrder = 20

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.convex.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.convex.html new file mode 100644 index 0000000..3e3e98b --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.convex.html @@ -0,0 +1,61 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_paths.convex + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_paths.convex ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_paths/convex.py
+

Create outline.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+
fabmetheus_utilities.geometry.creation.lineation
+

+ + + + + +
 
+Functions
       
getManipulatedPaths(close, elementNode, loop, prefix, sideLength)
Get path with overhangs removed or filled in.
+
getNewDerivation(elementNode, prefix, sideLength)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalExecutionOrder = 80

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.html new file mode 100644 index 0000000..d9048e2 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.html @@ -0,0 +1,37 @@ + + +Python: package fabmetheus_utilities.geometry.manipulation_paths + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_paths
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_paths/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
bevel
+convex
+
outline
+overhang
+
round
+segment
+
wedge
+

+ + + + + +
 
+Data
       level = 3
+numberOfLevelsDeepInPackageHierarchy = 3
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.outline.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.outline.html new file mode 100644 index 0000000..984be61 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.outline.html @@ -0,0 +1,85 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_paths.outline + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_paths.outline ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_paths/outline.py
+

Create outline.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.intercircle
+
fabmetheus_utilities.geometry.creation.lineation
+fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.setting
+

+ + + + + +
 
+Classes
       
+
OutlineDerivation +
+

+ + + + + + + +
 
+class OutlineDerivation
   Class to hold outline variables.
 
 Methods defined here:
+
__init__(self, elementNode, prefix, sideLength)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getManipulatedPaths(close, elementNode, loop, prefix, sideLength)
Get path with outline.
+
getNewDerivation(elementNode, prefix, sideLength)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalExecutionOrder = 80

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.overhang.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.overhang.html new file mode 100644 index 0000000..9d1e26b --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.overhang.html @@ -0,0 +1,195 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_paths.overhang + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_paths.overhang ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_paths/overhang.py
+

Add material to support overhang or remove material at the overhang angle.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.creation.lineation
+
math
+fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.setting
+

+ + + + + +
 
+Classes
       
+
AlongAway +
OverhangClockwise +
OverhangDerivation +
OverhangWiddershinsLeft +
+
+
OverhangWiddershinsRight +
+
+
+

+ + + + + + + +
 
+class AlongAway
   Class to derive the path along the point and away from the point.
 
 Methods defined here:
+
__init__(self, loop, overhangPlaneAngle)
Initialize.
+ +
__repr__(self)
Get the string representation of AlongAway.
+ +
addToBottomPoints(self, point)
Add point to bottom points and set y to minimumY.
+ +
getIsClockwisePointSupported(self, point)
Determine if the point on the clockwise loop is supported.
+ +
getIsPointSupportedBySegment(self, endIndex)
Determine if the point on the widdershins loop is supported.
+ +
getIsWiddershinsPointSupported(self, point)
Determine if the point on the widdershins loop is supported.
+ +

+ + + + + + + +
 
+class OverhangClockwise
   Class to get the intersection up from the point.
 
 Methods defined here:
+
__init__(self, alongAway)
Initialize.
+ +
__repr__(self)
Get the string representation of OverhangClockwise.
+ +
alterLoop(self, unsupportedPointIndexes)
Alter alongAway loop.
+ +

+ + + + + + + +
 
+class OverhangDerivation
   Class to hold overhang variables.
 
 Methods defined here:
+
__init__(self, elementNode, prefix)
Set defaults.
+ +

+ + + + + + + +
 
+class OverhangWiddershinsLeft
   Class to get the intersection from the point down to the left.
 
 Methods defined here:
+
__init__(self, alongAway)
Initialize.
+ +
__repr__(self)
Get the string representation of OverhangWiddershins.
+ +
alterLoop(self)
Alter alongAway loop.
+ +
getBottomLoop(self, closestBottomIndex, insertedPoint)
Get loop around bottom.
+ +
getDistance(self)
Get distance between point and closest intersection or bottom point along line.
+ +
getDistanceToBottom(self)
Get distance between point and closest bottom point along line.
+ +
getIntersectLoop(self)
Get intersection loop.
+ +
getIsOnside(self, x)
Determine if x is on the side along the direction of the intersection line.
+ +
setRatios(self)
Set ratios.
+ +

+ + + + + + + +
 
+class OverhangWiddershinsRight(OverhangWiddershinsLeft)
   Class to get the intersection from the point down to the right.
 
 Methods defined here:
+
__init__(self, alongAway)
Initialize.
+ +
getBottomLoop(self, closestBottomIndex, insertedPoint)
Get loop around bottom.
+ +
getIntersectLoop(self)
Get intersection loop.
+ +
getIsOnside(self, x)
Determine if x is on the side along the direction of the intersection line.
+ +
+Methods inherited from OverhangWiddershinsLeft:
+
__repr__(self)
Get the string representation of OverhangWiddershins.
+ +
alterLoop(self)
Alter alongAway loop.
+ +
getDistance(self)
Get distance between point and closest intersection or bottom point along line.
+ +
getDistanceToBottom(self)
Get distance between point and closest bottom point along line.
+ +
setRatios(self)
Set ratios.
+ +

+ + + + + +
 
+Functions
       
addUnsupportedPointIndexes(alongAway)
Add the indexes of the unsupported points.
+
alterClockwiseSupportedPath(alongAway, elementNode)
Get clockwise path with overhangs carved out.
+
alterWiddershinsSupportedPath(alongAway, close)
Get widdershins path with overhangs filled in.
+
alterWiddershinsSupportedPathByPoint(alongAway, overhangWiddershinsLeft, overhangWiddershinsRight, point)
Get widdershins path with overhangs filled in for point.
+
compareYAscending(point, pointOther)
Get comparison in order to sort points in ascending y.
+
getManipulatedPaths(close, elementNode, loop, prefix, sideLength)
Get path with overhangs removed or filled in.
+
getMinimumYByPath(path)
Get path with overhangs removed or filled in.
+
getNewDerivation(elementNode, prefix, sideLength)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalExecutionOrder = 100

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.round.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.round.html new file mode 100644 index 0000000..9e565c9 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.round.html @@ -0,0 +1,85 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_paths.round + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_paths.round ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_paths/round.py
+

Add material to support overhang or remove material at the overhang angle.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.creation.lineation
+
math
+

+ + + + + +
 
+Classes
       
+
RoundDerivation +
+

+ + + + + + + +
 
+class RoundDerivation
   Class to hold round variables.
 
 Methods defined here:
+
__init__(self, elementNode, prefix, sideLength)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getManipulatedPaths(close, elementNode, loop, prefix, sideLength)
Get round loop.
+
getNewDerivation(elementNode, prefix, sideLength)
Get new derivation.
+
getRoundPath(begin, center, close, end, radius, sidesPerRadian)
Get round path.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalExecutionOrder = 40

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.segment.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.segment.html new file mode 100644 index 0000000..de23fe2 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.segment.html @@ -0,0 +1,104 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_paths.segment + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_paths.segment ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_paths/segment.py
+

Add material to support overhang or remove material at the overhang angle.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+
fabmetheus_utilities.geometry.creation.lineation
+

+ + + + + +
 
+Classes
       
+
SegmentDerivation +
StartEnd +
+

+ + + + + + + +
 
+class SegmentDerivation
   Class to hold segment variables.
 
 Methods defined here:
+
__init__(self, elementNode, prefix)
Set defaults.
+ +

+ + + + + + + +
 
+class StartEnd
   Class to get a start through end range.
 
 Methods defined here:
+
__init__(self, elementNode, modulo, prefix)
Initialize.
+ +
__repr__(self)
Get the string representation of this StartEnd.
+ +

+ + + + + +
 
+Functions
       
getManipulatedPaths(close, elementNode, loop, prefix, sideLength)
Get segment loop.
+
getNewDerivation(elementNode, prefix, sideLength)
Get new derivation.
+
getRadialPath(begin, center, end, path)
Get radial path.
+
getSegmentPath(center, loop, path, pointIndex)
Get segment path.
+
getSegmentPathDefault()
Get segment path default.
+
getWedgePath(begin, centerBegin, centerEnd, centerEndMinusBegin, end, path)
Get segment path.
+
getWiddershinsAverageByVector3(centerMinusBeginComplex, endMinusCenterComplex)
Get the normalized average of the widdershins vectors.
+
getXNormalizedVector3Path(path)
Get path where the x ranges from 0 to 1.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalExecutionOrder = 60

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.wedge.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.wedge.html new file mode 100644 index 0000000..57d428c --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_paths.wedge.html @@ -0,0 +1,82 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_paths.wedge + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_paths.wedge ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_paths/wedge.py
+

Add material to support overhang or remove material at the overhang angle.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+
fabmetheus_utilities.geometry.creation.lineation
+

+ + + + + +
 
+Classes
       
+
WedgeDerivation +
+

+ + + + + + + +
 
+class WedgeDerivation
   Class to hold wedge variables.
 
 Methods defined here:
+
__init__(self, elementNode, prefix)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getManipulatedPaths(close, elementNode, loop, prefix, sideLength)
Get wedge loop.
+
getNewDerivation(elementNode, prefix, sideLength)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalExecutionOrder = -200

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_shapes._bottom.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_shapes._bottom.html new file mode 100644 index 0000000..aa191dd --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_shapes._bottom.html @@ -0,0 +1,91 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_shapes._bottom + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_shapes._bottom ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_shapes/_bottom.py
+

Boolean geometry bottom.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.geometry.geometry_utilities.boolean_geometry
+
fabmetheus_utilities.euclidean
+fabmetheus_utilities.geometry.geometry_utilities.evaluate
+
fabmetheus_utilities.geometry.geometry_utilities.matrix
+fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.setting
+
fabmetheus_utilities.geometry.creation.solid
+

+ + + + + +
 
+Classes
       
+
BottomDerivation +
+

+ + + + + + + +
 
+class BottomDerivation
   Class to hold bottom variables.
 
 Methods defined here:
+
__init__(self, elementNode, prefix)
Set defaults.
+ +
getAdditionalPathLift(self)
Get path lift.
+ +

+ + + + + +
 
+Functions
       
bottomElementNode(derivation, target)
Bottom target.
+
getManipulatedGeometryOutput(elementNode, geometryOutput, prefix)
Get bottomed geometryOutput.
+
getManipulatedPaths(close, elementNode, loop, prefix, sideLength)
Get flipped paths.
+
getNewDerivation(elementNode, prefix, sideLength)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+
processElementNodeByDerivation(derivation, elementNode)
Process the xml element by derivation.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalExecutionOrder = 400

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_shapes._inset.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_shapes._inset.html new file mode 100644 index 0000000..9bea8a3 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_shapes._inset.html @@ -0,0 +1,91 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_shapes._inset + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_shapes._inset ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_shapes/_inset.py
+

Create inset.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.geometry.geometry_utilities.boolean_solid
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.intercircle
+fabmetheus_utilities.geometry.creation.lineation
+
math
+fabmetheus_utilities.geometry.geometry_utilities.matrix
+fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.setting
+
fabmetheus_utilities.geometry.creation.solid
+fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
InsetDerivation +
+

+ + + + + + + +
 
+class InsetDerivation
   Class to hold inset variables.
 
 Methods defined here:
+
__init__(self, elementNode, prefix)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getManipulatedGeometryOutput(elementNode, geometryOutput, prefix)
Get inset geometryOutput.
+
getManipulatedPaths(close, elementNode, loop, prefix, sideLength)
Get inset path.
+
getNewDerivation(elementNode, prefix, sideLength)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalExecutionOrder = 80

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_shapes._outset.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_shapes._outset.html new file mode 100644 index 0000000..88f028e --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_shapes._outset.html @@ -0,0 +1,91 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_shapes._outset + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_shapes._outset ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_shapes/_outset.py
+

Create inset.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.geometry.geometry_utilities.boolean_solid
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.intercircle
+fabmetheus_utilities.geometry.creation.lineation
+
math
+fabmetheus_utilities.geometry.geometry_utilities.matrix
+fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements.setting
+
fabmetheus_utilities.geometry.creation.solid
+fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
OutsetDerivation +
+

+ + + + + + + +
 
+class OutsetDerivation
   Class to hold outset variables.
 
 Methods defined here:
+
__init__(self, elementNode, prefix)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
getManipulatedGeometryOutput(elementNode, geometryOutput, prefix)
Get outset geometryOutput.
+
getManipulatedPaths(close, elementNode, loop, prefix, sideLength)
Get outset path.
+
getNewDerivation(elementNode, prefix, sideLength)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalExecutionOrder = 80

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_shapes.equation.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_shapes.equation.html new file mode 100644 index 0000000..7243d3f --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_shapes.equation.html @@ -0,0 +1,106 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_shapes.equation + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_shapes.equation ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_shapes/equation.py
+

Equation for vertexes.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+math
+
fabmetheus_utilities.geometry.geometry_utilities.matrix
+

+ + + + + +
 
+Classes
       
+
EquationDerivation +
EquationResult +
+

+ + + + + + + +
 
+class EquationDerivation
   Class to hold equation variables.
 
 Methods defined here:
+
__init__(self, elementNode, prefix)
Set defaults.
+ +
addEquationResult(self, elementNode, equationFunction, prefix)
Add equation result to equationResults.
+ +

+ + + + + + + +
 
+class EquationResult
   Class to get equation results.
 
 Methods defined here:
+
__init__(self, elementNode, equationFunction, key)
Initialize.
+ +
getReturnValue(self, point, revolutions)
Get return value.
+ +

+ + + + + +
 
+Functions
       
equate(point, returnValue)
Get equation for rectangular.
+
equatePoints(elementNode, points, prefix, revolutions)
Equate the points.
+
equateX(point, returnValue)
Get equation for rectangular x.
+
equateY(point, returnValue)
Get equation for rectangular y.
+
equateZ(point, returnValue)
Get equation for rectangular z.
+
getManipulatedGeometryOutput(elementNode, geometryOutput, prefix)
Get equated geometryOutput.
+
getManipulatedPaths(close, elementNode, loop, prefix, sideLength)
Get equated paths.
+
getNewDerivation(elementNode, prefix, sideLength)
Get new derivation.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalExecutionOrder = -100

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_shapes.flip.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_shapes.flip.html new file mode 100644 index 0000000..7ba0dc0 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_shapes.flip.html @@ -0,0 +1,87 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_shapes.flip + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_shapes.flip ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_shapes/flip.py
+

Add material to support overhang or remove material at the overhang angle.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+
fabmetheus_utilities.geometry.geometry_utilities.matrix
+
fabmetheus_utilities.geometry.creation.solid
+

+ + + + + +
 
+Classes
       
+
FlipDerivation +
+

+ + + + + + + +
 
+class FlipDerivation
   Class to hold flip variables.
 
 Methods defined here:
+
__init__(self, elementNode, prefix)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
flipPoints(elementNode, points, prefix)
Flip the points.
+
getFlippedLoop(elementNode, loop, prefix)
Get flipped loop.
+
getManipulatedGeometryOutput(elementNode, geometryOutput, prefix)
Get equated geometryOutput.
+
getManipulatedPaths(close, elementNode, loop, prefix, sideLength)
Get flipped paths.
+
getNewDerivation(elementNode, prefix, sideLength)
Get new derivation.
+
getShouldReverse(elementNode, prefix)
Determine if the loop should be reversed.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalExecutionOrder = 200

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_shapes.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_shapes.html new file mode 100644 index 0000000..6e5bfb0 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_shapes.html @@ -0,0 +1,36 @@ + + +Python: package fabmetheus_utilities.geometry.manipulation_shapes + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_shapes
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_shapes/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
_bottom
+_inset
+
_outset
+equation
+
flip
+mirror
+

+ + + + + +
 
+Data
       level = 3
+numberOfLevelsDeepInPackageHierarchy = 3
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_shapes.mirror.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_shapes.mirror.html new file mode 100644 index 0000000..cd8d2aa --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.manipulation_shapes.mirror.html @@ -0,0 +1,66 @@ + + +Python: module fabmetheus_utilities.geometry.manipulation_shapes.mirror + + + + +
 
+ 
fabmetheus_utilities.geometry.manipulation_shapes.mirror ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/manipulation_shapes/mirror.py
+

Add material to support overhang or remove material at the overhang angle.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.geometry_tools.face
+
fabmetheus_utilities.geometry.manipulation_shapes.flip
+fabmetheus_utilities.geometry.geometry_utilities.matrix
+
fabmetheus_utilities.geometry.creation.solid
+fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Functions
       
getManipulatedGeometryOutput(elementNode, geometryOutput, prefix)
Get equated geometryOutput.
+
getManipulatedPaths(close, elementNode, loop, prefix, sideLength)
Get flipped paths.
+
getNewDerivation(elementNode, prefix, sideLength)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalExecutionOrder = 200

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.cube.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.cube.html new file mode 100644 index 0000000..35ab198 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.cube.html @@ -0,0 +1,187 @@ + + +Python: module fabmetheus_utilities.geometry.solids.cube + + + + +
 
+ 
fabmetheus_utilities.geometry.solids.cube ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/solids/cube.py
+

Boolean geometry cube.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+
fabmetheus_utilities.geometry.creation.solid
+
fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
CubeDerivation +
fabmetheus_utilities.geometry.solids.triangle_mesh.TriangleMesh(fabmetheus_utilities.geometry.solids.group.Group) +
+
+
Cube +
+
+
+

+ + + + + + + +
 
+class Cube(fabmetheus_utilities.geometry.solids.triangle_mesh.TriangleMesh)
   A cube object.
 
 
Method resolution order:
+
Cube
+
fabmetheus_utilities.geometry.solids.triangle_mesh.TriangleMesh
+
fabmetheus_utilities.geometry.solids.group.Group
+
fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary
+
+
+Methods defined here:
+
addXMLSection(self, depth, output)
Add the xml section for this object.
+ +
createShape(self)
Create the shape.
+ +
setToElementNode(self, elementNode)
Set to elementNode.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.triangle_mesh.TriangleMesh:
+
__init__(self)
Add empty lists.
+ +
getCarveBoundaryLayers(self)
Get the boundary layers.
+ +
getCarveCornerMaximum(self)
Get the corner maximum of the vertexes.
+ +
getCarveCornerMinimum(self)
Get the corner minimum of the vertexes.
+ +
getCarveLayerHeight(self)
Get the layer height.
+ +
getFabmetheusXML(self)
Return the fabmetheus XML.
+ +
getGeometryOutput(self)
Get geometry output dictionary.
+ +
getInterpretationSuffix(self)
Return the suffix for a triangle mesh.
+ +
getLoops(self, importRadius, z)
Get loops sliced through shape.
+ +
getLoopsFromMesh(self, z)
Get loops from a carve of a mesh.
+ +
getMinimumZ(self)
Get the minimum z.
+ +
getTransformedVertexes(self)
Get all transformed vertexes.
+ +
getTriangleMeshes(self)
Get all triangleMeshes.
+ +
getVertexes(self)
Get all vertexes.
+ +
setCarveImportRadius(self, importRadius)
Set the import radius.
+ +
setCarveIsCorrectMesh(self, isCorrectMesh)
Set the is correct mesh flag.
+ +
setCarveLayerHeight(self, layerHeight)
Set the layer height.
+ +
setEdgesForAllFaces(self)
Set the face edges of all the faces.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.group.Group:
+
addXMLInnerSection(self, depth, output)
Add xml inner section for this object.
+ +
getMatrix4X4(self)
Get the matrix4X4.
+ +
getMatrixChainTetragrid(self)
Get the matrix chain tetragrid.
+ +
getVisible(self)
Get visible.
+ +
+Methods inherited from fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary:
+
__repr__(self)
Get the string representation of this object info.
+ +
addXML(self, depth, output)
Add xml for this object.
+ +
addXMLArchivableObjects(self, depth, output)
Add xml for this object.
+ +
getAttributes(self)
Get attribute table.
+ +
getComplexTransformedPathLists(self)
Get complex transformed path lists.
+ +
getFabricationExtension(self)
Get fabrication extension.
+ +
getFabricationText(self, addLayerTemplate)
Get fabrication text.
+ +
getPaths(self)
Get all paths.
+ +
getTransformedPaths(self)
Get all transformed paths.
+ +
getType(self)
Get type.
+ +
getXMLLocalName(self)
Get xml local name.
+ +
transformGeometryOutput(self, geometryOutput)
Transform the geometry output by the local matrix4x4.
+ +

+ + + + + + + +
 
+class CubeDerivation
   Class to hold cube variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
addCube(elementNode, faces, inradius, vertexes)
Add cube by inradius.
+
getGeometryOutput(elementNode, inradius)
Get cube triangle mesh by inradius.
+
getNewDerivation(elementNode)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://hydraraptor.blogspot.com/>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.cylinder.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.cylinder.html new file mode 100644 index 0000000..cd2e084 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.cylinder.html @@ -0,0 +1,197 @@ + + +Python: module fabmetheus_utilities.geometry.solids.cylinder + + + + +
 
+ 
fabmetheus_utilities.geometry.solids.cylinder ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/solids/cylinder.py
+

Boolean geometry cylinder.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.geometry.solids.cube
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.creation.lineation
+math
+
fabmetheus_utilities.geometry.geometry_utilities.matrix
+fabmetheus_utilities.geometry.creation.solid
+fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
fabmetheus_utilities.geometry.solids.cube.Cube(fabmetheus_utilities.geometry.solids.triangle_mesh.TriangleMesh) +
+
+
Cylinder +
+
+
CylinderDerivation +
+

+ + + + + + + +
 
+class Cylinder(fabmetheus_utilities.geometry.solids.cube.Cube)
   A cylinder object.
 
 
Method resolution order:
+
Cylinder
+
fabmetheus_utilities.geometry.solids.cube.Cube
+
fabmetheus_utilities.geometry.solids.triangle_mesh.TriangleMesh
+
fabmetheus_utilities.geometry.solids.group.Group
+
fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary
+
+
+Methods defined here:
+
__init__(self)
Add empty lists.
+ +
createShape(self)
Create the shape.
+ +
setToElementNode(self, elementNode)
Set to elementNode.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.cube.Cube:
+
addXMLSection(self, depth, output)
Add the xml section for this object.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.triangle_mesh.TriangleMesh:
+
getCarveBoundaryLayers(self)
Get the boundary layers.
+ +
getCarveCornerMaximum(self)
Get the corner maximum of the vertexes.
+ +
getCarveCornerMinimum(self)
Get the corner minimum of the vertexes.
+ +
getCarveLayerHeight(self)
Get the layer height.
+ +
getFabmetheusXML(self)
Return the fabmetheus XML.
+ +
getGeometryOutput(self)
Get geometry output dictionary.
+ +
getInterpretationSuffix(self)
Return the suffix for a triangle mesh.
+ +
getLoops(self, importRadius, z)
Get loops sliced through shape.
+ +
getLoopsFromMesh(self, z)
Get loops from a carve of a mesh.
+ +
getMinimumZ(self)
Get the minimum z.
+ +
getTransformedVertexes(self)
Get all transformed vertexes.
+ +
getTriangleMeshes(self)
Get all triangleMeshes.
+ +
getVertexes(self)
Get all vertexes.
+ +
setCarveImportRadius(self, importRadius)
Set the import radius.
+ +
setCarveIsCorrectMesh(self, isCorrectMesh)
Set the is correct mesh flag.
+ +
setCarveLayerHeight(self, layerHeight)
Set the layer height.
+ +
setEdgesForAllFaces(self)
Set the face edges of all the faces.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.group.Group:
+
addXMLInnerSection(self, depth, output)
Add xml inner section for this object.
+ +
getMatrix4X4(self)
Get the matrix4X4.
+ +
getMatrixChainTetragrid(self)
Get the matrix chain tetragrid.
+ +
getVisible(self)
Get visible.
+ +
+Methods inherited from fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary:
+
__repr__(self)
Get the string representation of this object info.
+ +
addXML(self, depth, output)
Add xml for this object.
+ +
addXMLArchivableObjects(self, depth, output)
Add xml for this object.
+ +
getAttributes(self)
Get attribute table.
+ +
getComplexTransformedPathLists(self)
Get complex transformed path lists.
+ +
getFabricationExtension(self)
Get fabrication extension.
+ +
getFabricationText(self, addLayerTemplate)
Get fabrication text.
+ +
getPaths(self)
Get all paths.
+ +
getTransformedPaths(self)
Get all transformed paths.
+ +
getType(self)
Get type.
+ +
getXMLLocalName(self)
Get xml local name.
+ +
transformGeometryOutput(self, geometryOutput)
Transform the geometry output by the local matrix4x4.
+ +

+ + + + + + + +
 
+class CylinderDerivation
   Class to hold cylinder variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
addCylinder(faces, inradius, sides, topOverBottom, vertexes)
Add cylinder by inradius.
+
addCylinderOutputByEndStart(endZ, inradiusComplex, outputs, sides, start, topOverBottom=1.0)
Add cylinder triangle mesh by endZ, inradius and start.
+
getGeometryOutput(inradius, sides, topOverBottom)
Get cylinder triangle mesh by inradius.
+
getNewDerivation(elementNode)
Get new derivation.
+
getTopOverBottom(angle, endZ, inradiusComplex, startZ)
Get topOverBottom by angle in radians, endZ, inradius and start.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://hydraraptor.blogspot.com/>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.difference.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.difference.html new file mode 100644 index 0000000..88cb746 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.difference.html @@ -0,0 +1,159 @@ + + +Python: module fabmetheus_utilities.geometry.solids.difference + + + + +
 
+ 
fabmetheus_utilities.geometry.solids.difference ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/solids/difference.py
+

Boolean geometry difference of solids.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.geometry.geometry_utilities.boolean_solid
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+
fabmetheus_utilities.geometry.solids.group
+

+ + + + + +
 
+Classes
       
+
fabmetheus_utilities.geometry.geometry_utilities.boolean_solid.BooleanSolid(fabmetheus_utilities.geometry.solids.group.Group) +
+
+
Difference +
+
+
+

+ + + + + + + +
 
+class Difference(fabmetheus_utilities.geometry.geometry_utilities.boolean_solid.BooleanSolid)
   A difference object.
 
 
Method resolution order:
+
Difference
+
fabmetheus_utilities.geometry.geometry_utilities.boolean_solid.BooleanSolid
+
fabmetheus_utilities.geometry.solids.group.Group
+
fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary
+
+
+Methods defined here:
+
getLoopsFromObjectLoopsList(self, importRadius, visibleObjectLoopsList)
Get loops from visible object loops list.
+ +
getXMLLocalName(self)
Get xml class name.
+ +
+Methods inherited from fabmetheus_utilities.geometry.geometry_utilities.boolean_solid.BooleanSolid:
+
getDifference(self, importRadius, visibleObjectLoopsList)
Get subtracted loops sliced through shape.
+ +
getIntersection(self, importRadius, visibleObjectLoopsList)
Get intersected loops sliced through shape.
+ +
getLoops(self, importRadius, z)
Get loops sliced through shape.
+ +
getTransformedPaths(self)
Get all transformed paths.
+ +
getUnion(self, importRadius, visibleObjectLoopsList)
Get joined loops sliced through shape.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.group.Group:
+
__init__(self)
Add empty lists.
+ +
addXMLInnerSection(self, depth, output)
Add xml inner section for this object.
+ +
addXMLSection(self, depth, output)
Add the xml section for this object.
+ +
getMatrix4X4(self)
Get the matrix4X4.
+ +
getMatrixChainTetragrid(self)
Get the matrix chain tetragrid.
+ +
getVisible(self)
Get visible.
+ +
setToElementNode(self, elementNode)
Set to elementNode.
+ +
+Methods inherited from fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary:
+
__repr__(self)
Get the string representation of this object info.
+ +
addXML(self, depth, output)
Add xml for this object.
+ +
addXMLArchivableObjects(self, depth, output)
Add xml for this object.
+ +
createShape(self)
Create the shape.
+ +
getAttributes(self)
Get attribute table.
+ +
getComplexTransformedPathLists(self)
Get complex transformed path lists.
+ +
getFabricationExtension(self)
Get fabrication extension.
+ +
getFabricationText(self, addLayerTemplate)
Get fabrication text.
+ +
getGeometryOutput(self)
Get geometry output dictionary.
+ +
getMinimumZ(self)
Get the minimum z.
+ +
getPaths(self)
Get all paths.
+ +
getTransformedVertexes(self)
Get all transformed vertexes.
+ +
getTriangleMeshes(self)
Get all triangleMeshes.
+ +
getType(self)
Get type.
+ +
getVertexes(self)
Get all vertexes.
+ +
transformGeometryOutput(self, geometryOutput)
Transform the geometry output by the local matrix4x4.
+ +

+ + + + + +
 
+Functions
       
convertElementNode(elementNode, geometryOutput)
Convert the xml element to a difference xml element.
+
getNewDerivation(elementNode)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://hydraraptor.blogspot.com/>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.group.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.group.html new file mode 100644 index 0000000..553b2c7 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.group.html @@ -0,0 +1,141 @@ + + +Python: module fabmetheus_utilities.geometry.solids.group + + + + +
 
+ 
fabmetheus_utilities.geometry.solids.group ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/solids/group.py
+

Boolean geometry group of solids.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.geometry.geometry_tools.dictionary
+
fabmetheus_utilities.euclidean
+fabmetheus_utilities.geometry.geometry_utilities.evaluate
+
fabmetheus_utilities.geometry.geometry_utilities.matrix
+

+ + + + + +
 
+Classes
       
+
fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary +
+
+
Group +
+
+
+

+ + + + + + + +
 
+class Group(fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary)
   A group.
 
 Methods defined here:
+
__init__(self)
Add empty lists.
+ +
addXMLInnerSection(self, depth, output)
Add xml inner section for this object.
+ +
addXMLSection(self, depth, output)
Add the xml section for this object.
+ +
getLoops(self, importRadius, z)
Get loops sliced through shape.
+ +
getMatrix4X4(self)
Get the matrix4X4.
+ +
getMatrixChainTetragrid(self)
Get the matrix chain tetragrid.
+ +
getVisible(self)
Get visible.
+ +
setToElementNode(self, elementNode)
Set to elementNode.
+ +
+Methods inherited from fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary:
+
__repr__(self)
Get the string representation of this object info.
+ +
addXML(self, depth, output)
Add xml for this object.
+ +
addXMLArchivableObjects(self, depth, output)
Add xml for this object.
+ +
createShape(self)
Create the shape.
+ +
getAttributes(self)
Get attribute table.
+ +
getComplexTransformedPathLists(self)
Get complex transformed path lists.
+ +
getFabricationExtension(self)
Get fabrication extension.
+ +
getFabricationText(self, addLayerTemplate)
Get fabrication text.
+ +
getGeometryOutput(self)
Get geometry output dictionary.
+ +
getMinimumZ(self)
Get the minimum z.
+ +
getPaths(self)
Get all paths.
+ +
getTransformedPaths(self)
Get all transformed paths.
+ +
getTransformedVertexes(self)
Get all transformed vertexes.
+ +
getTriangleMeshes(self)
Get all triangleMeshes.
+ +
getType(self)
Get type.
+ +
getVertexes(self)
Get all vertexes.
+ +
getXMLLocalName(self)
Get xml local name.
+ +
transformGeometryOutput(self, geometryOutput)
Transform the geometry output by the local matrix4x4.
+ +

+ + + + + +
 
+Functions
       
convertContainerElementNode(elementNode, geometryOutput, xmlObject)
Convert the xml element to a group xml element.
+
convertElementNode(elementNode, geometryOutput)
Convert the xml element to a group xml element.
+
getNewDerivation(elementNode)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.html new file mode 100644 index 0000000..69b4856 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.html @@ -0,0 +1,49 @@ + + +Python: package fabmetheus_utilities.geometry.solids + + + + +
 
+ 
fabmetheus_utilities.geometry.solids
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/solids/__init__.py
+

+Previous / Next / Contents +

+


+This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Package Contents
       
cube
+cylinder
+
difference
+group
+
intersection
+sphere
+
triangle_mesh
+union
+

+ + + + + +
 
+Data
       level = 3
+numberOfLevelsDeepInPackageHierarchy = 3
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.intersection.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.intersection.html new file mode 100644 index 0000000..d251a38 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.intersection.html @@ -0,0 +1,162 @@ + + +Python: module fabmetheus_utilities.geometry.solids.intersection + + + + +
 
+ 
fabmetheus_utilities.geometry.solids.intersection ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/solids/intersection.py
+

Boolean geometry intersection of solids.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.geometry.solids.difference
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+
fabmetheus_utilities.geometry.solids.group
+

+ + + + + +
 
+Classes
       
+
fabmetheus_utilities.geometry.solids.difference.Difference(fabmetheus_utilities.geometry.geometry_utilities.boolean_solid.BooleanSolid) +
+
+
Intersection +
+
+
+

+ + + + + + + +
 
+class Intersection(fabmetheus_utilities.geometry.solids.difference.Difference)
   An intersection object.
 
 
Method resolution order:
+
Intersection
+
fabmetheus_utilities.geometry.solids.difference.Difference
+
fabmetheus_utilities.geometry.geometry_utilities.boolean_solid.BooleanSolid
+
fabmetheus_utilities.geometry.solids.group.Group
+
fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary
+
+
+Methods defined here:
+
getLoopsFromObjectLoopsList(self, importRadius, visibleObjectLoopsList)
Get loops from visible object loops list.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.difference.Difference:
+
getXMLLocalName(self)
Get xml class name.
+ +
+Methods inherited from fabmetheus_utilities.geometry.geometry_utilities.boolean_solid.BooleanSolid:
+
getDifference(self, importRadius, visibleObjectLoopsList)
Get subtracted loops sliced through shape.
+ +
getIntersection(self, importRadius, visibleObjectLoopsList)
Get intersected loops sliced through shape.
+ +
getLoops(self, importRadius, z)
Get loops sliced through shape.
+ +
getTransformedPaths(self)
Get all transformed paths.
+ +
getUnion(self, importRadius, visibleObjectLoopsList)
Get joined loops sliced through shape.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.group.Group:
+
__init__(self)
Add empty lists.
+ +
addXMLInnerSection(self, depth, output)
Add xml inner section for this object.
+ +
addXMLSection(self, depth, output)
Add the xml section for this object.
+ +
getMatrix4X4(self)
Get the matrix4X4.
+ +
getMatrixChainTetragrid(self)
Get the matrix chain tetragrid.
+ +
getVisible(self)
Get visible.
+ +
setToElementNode(self, elementNode)
Set to elementNode.
+ +
+Methods inherited from fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary:
+
__repr__(self)
Get the string representation of this object info.
+ +
addXML(self, depth, output)
Add xml for this object.
+ +
addXMLArchivableObjects(self, depth, output)
Add xml for this object.
+ +
createShape(self)
Create the shape.
+ +
getAttributes(self)
Get attribute table.
+ +
getComplexTransformedPathLists(self)
Get complex transformed path lists.
+ +
getFabricationExtension(self)
Get fabrication extension.
+ +
getFabricationText(self, addLayerTemplate)
Get fabrication text.
+ +
getGeometryOutput(self)
Get geometry output dictionary.
+ +
getMinimumZ(self)
Get the minimum z.
+ +
getPaths(self)
Get all paths.
+ +
getTransformedVertexes(self)
Get all transformed vertexes.
+ +
getTriangleMeshes(self)
Get all triangleMeshes.
+ +
getType(self)
Get type.
+ +
getVertexes(self)
Get all vertexes.
+ +
transformGeometryOutput(self, geometryOutput)
Transform the geometry output by the local matrix4x4.
+ +

+ + + + + +
 
+Functions
       
convertElementNode(elementNode, geometryOutput)
Convert the xml element to an intersection xml element.
+
getNewDerivation(elementNode)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://hydraraptor.blogspot.com/>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.sphere.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.sphere.html new file mode 100644 index 0000000..662a3e0 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.sphere.html @@ -0,0 +1,193 @@ + + +Python: module fabmetheus_utilities.geometry.solids.sphere + + + + +
 
+ 
fabmetheus_utilities.geometry.solids.sphere ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/solids/sphere.py
+

Boolean geometry sphere.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.geometry.solids.cube
+
fabmetheus_utilities.euclidean
+fabmetheus_utilities.geometry.geometry_utilities.evaluate
+
math
+fabmetheus_utilities.geometry.creation.solid
+
fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
fabmetheus_utilities.geometry.solids.cube.Cube(fabmetheus_utilities.geometry.solids.triangle_mesh.TriangleMesh) +
+
+
Sphere +
+
+
SphereDerivation +
+

+ + + + + + + +
 
+class Sphere(fabmetheus_utilities.geometry.solids.cube.Cube)
   A sphere object.
 
 
Method resolution order:
+
Sphere
+
fabmetheus_utilities.geometry.solids.cube.Cube
+
fabmetheus_utilities.geometry.solids.triangle_mesh.TriangleMesh
+
fabmetheus_utilities.geometry.solids.group.Group
+
fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary
+
+
+Methods defined here:
+
createShape(self)
Create the shape.
+ +
setToElementNode(self, elementNode)
Set to elementNode.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.cube.Cube:
+
addXMLSection(self, depth, output)
Add the xml section for this object.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.triangle_mesh.TriangleMesh:
+
__init__(self)
Add empty lists.
+ +
getCarveBoundaryLayers(self)
Get the boundary layers.
+ +
getCarveCornerMaximum(self)
Get the corner maximum of the vertexes.
+ +
getCarveCornerMinimum(self)
Get the corner minimum of the vertexes.
+ +
getCarveLayerHeight(self)
Get the layer height.
+ +
getFabmetheusXML(self)
Return the fabmetheus XML.
+ +
getGeometryOutput(self)
Get geometry output dictionary.
+ +
getInterpretationSuffix(self)
Return the suffix for a triangle mesh.
+ +
getLoops(self, importRadius, z)
Get loops sliced through shape.
+ +
getLoopsFromMesh(self, z)
Get loops from a carve of a mesh.
+ +
getMinimumZ(self)
Get the minimum z.
+ +
getTransformedVertexes(self)
Get all transformed vertexes.
+ +
getTriangleMeshes(self)
Get all triangleMeshes.
+ +
getVertexes(self)
Get all vertexes.
+ +
setCarveImportRadius(self, importRadius)
Set the import radius.
+ +
setCarveIsCorrectMesh(self, isCorrectMesh)
Set the is correct mesh flag.
+ +
setCarveLayerHeight(self, layerHeight)
Set the layer height.
+ +
setEdgesForAllFaces(self)
Set the face edges of all the faces.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.group.Group:
+
addXMLInnerSection(self, depth, output)
Add xml inner section for this object.
+ +
getMatrix4X4(self)
Get the matrix4X4.
+ +
getMatrixChainTetragrid(self)
Get the matrix chain tetragrid.
+ +
getVisible(self)
Get visible.
+ +
+Methods inherited from fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary:
+
__repr__(self)
Get the string representation of this object info.
+ +
addXML(self, depth, output)
Add xml for this object.
+ +
addXMLArchivableObjects(self, depth, output)
Add xml for this object.
+ +
getAttributes(self)
Get attribute table.
+ +
getComplexTransformedPathLists(self)
Get complex transformed path lists.
+ +
getFabricationExtension(self)
Get fabrication extension.
+ +
getFabricationText(self, addLayerTemplate)
Get fabrication text.
+ +
getPaths(self)
Get all paths.
+ +
getTransformedPaths(self)
Get all transformed paths.
+ +
getType(self)
Get type.
+ +
getXMLLocalName(self)
Get xml local name.
+ +
transformGeometryOutput(self, geometryOutput)
Transform the geometry output by the local matrix4x4.
+ +

+ + + + + + + +
 
+class SphereDerivation
   Class to hold sphere variables.
 
 Methods defined here:
+
__init__(self, elementNode)
Set defaults.
+ +

+ + + + + +
 
+Functions
       
addSphere(elementNode, faces, radius, vertexes)
Add sphere by radius.
+
getGeometryOutput(elementNode, radius)
Get triangle mesh from attribute dictionary.
+
getNewDerivation(elementNode)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://hydraraptor.blogspot.com/>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.triangle_mesh.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.triangle_mesh.html new file mode 100644 index 0000000..c8f0695 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.triangle_mesh.html @@ -0,0 +1,285 @@ + + +Python: module fabmetheus_utilities.geometry.solids.triangle_mesh + + + + +
 
+ 
fabmetheus_utilities.geometry.solids.triangle_mesh ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/solids/triangle_mesh.py
+

Triangle Mesh holds the faces and edges of a triangular mesh.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.geometry.geometry_tools.dictionary
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.geometry_tools.face
+fabmetheus_utilities.geometry.solids.group
+
fabmetheus_utilities.intercircle
+math
+fabmetheus_utilities.geometry.geometry_utilities.matrix
+
fabmetheus_utilities.settings
+fabmetheus_utilities.geometry.geometry_tools.vertex
+fabmetheus_utilities.xml_simple_writer
+

+ + + + + +
 
+Classes
       
+
fabmetheus_utilities.geometry.solids.group.Group(fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary) +
+
+
TriangleMesh +
+
+
EdgePair +
FaceGenerator +
ZoneArrangement +
+

+ + + + + +
 
+class EdgePair
    Methods defined here:
+
__init__(self)
Pair of edges on a face.
+ +
__repr__(self)
Get the string representation of this EdgePair.
+ +
getFromIndexesEdges(self, edgeIndexes, edges)
Initialize from edge indices.
+ +

+ + + + + + + +
 
+class FaceGenerator
   A face generator.
 
 Methods defined here:
+
__init__(self, faces, indexedLoopBottom, indexedLoopTop)
Initialize.
+ +
addFacesByBottomIndex(self, bottomIndex, faces)
Add faces from the  bottom index to the next index.
+ +
getBetweenIndex(self, bottomPoint, bottomPointNext, topIndexPlusOne)
Get the index of the last point along the loop which is closer to the bottomPoint.
+ +

+ + + + + + + +
 
+class TriangleMesh(fabmetheus_utilities.geometry.solids.group.Group)
   A triangle mesh.
 
 
Method resolution order:
+
TriangleMesh
+
fabmetheus_utilities.geometry.solids.group.Group
+
fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary
+
+
+Methods defined here:
+
__init__(self)
Add empty lists.
+ +
addXMLSection(self, depth, output)
Add the xml section for this object.
+ +
getCarveBoundaryLayers(self)
Get the boundary layers.
+ +
getCarveCornerMaximum(self)
Get the corner maximum of the vertexes.
+ +
getCarveCornerMinimum(self)
Get the corner minimum of the vertexes.
+ +
getCarveLayerHeight(self)
Get the layer height.
+ +
getFabmetheusXML(self)
Return the fabmetheus XML.
+ +
getGeometryOutput(self)
Get geometry output dictionary.
+ +
getInterpretationSuffix(self)
Return the suffix for a triangle mesh.
+ +
getLoops(self, importRadius, z)
Get loops sliced through shape.
+ +
getLoopsFromMesh(self, z)
Get loops from a carve of a mesh.
+ +
getMinimumZ(self)
Get the minimum z.
+ +
getTransformedVertexes(self)
Get all transformed vertexes.
+ +
getTriangleMeshes(self)
Get all triangleMeshes.
+ +
getVertexes(self)
Get all vertexes.
+ +
setCarveImportRadius(self, importRadius)
Set the import radius.
+ +
setCarveIsCorrectMesh(self, isCorrectMesh)
Set the is correct mesh flag.
+ +
setCarveLayerHeight(self, layerHeight)
Set the layer height.
+ +
setEdgesForAllFaces(self)
Set the face edges of all the faces.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.group.Group:
+
addXMLInnerSection(self, depth, output)
Add xml inner section for this object.
+ +
getMatrix4X4(self)
Get the matrix4X4.
+ +
getMatrixChainTetragrid(self)
Get the matrix chain tetragrid.
+ +
getVisible(self)
Get visible.
+ +
setToElementNode(self, elementNode)
Set to elementNode.
+ +
+Methods inherited from fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary:
+
__repr__(self)
Get the string representation of this object info.
+ +
addXML(self, depth, output)
Add xml for this object.
+ +
addXMLArchivableObjects(self, depth, output)
Add xml for this object.
+ +
createShape(self)
Create the shape.
+ +
getAttributes(self)
Get attribute table.
+ +
getComplexTransformedPathLists(self)
Get complex transformed path lists.
+ +
getFabricationExtension(self)
Get fabrication extension.
+ +
getFabricationText(self, addLayerTemplate)
Get fabrication text.
+ +
getPaths(self)
Get all paths.
+ +
getTransformedPaths(self)
Get all transformed paths.
+ +
getType(self)
Get type.
+ +
getXMLLocalName(self)
Get xml local name.
+ +
transformGeometryOutput(self, geometryOutput)
Transform the geometry output by the local matrix4x4.
+ +

+ + + + + + + +
 
+class ZoneArrangement
   A zone arrangement.
 
 Methods defined here:
+
__init__(self, layerHeight, vertexes)
Initialize the zone interval and the zZone table.
+ +
getEmptyZ(self, z)
Get the first z which is not in the zone table.
+ +

+ + + + + +
 
+Functions
       
addEdgePair(edgePairTable, edges, faceEdgeIndex, remainingEdgeIndex, remainingEdgeTable)
Add edge pair to the edge pair table.
+
addFacesByConcaveLoop(faces, indexedLoop)
Add faces from a polygon which is concave.
+
addFacesByConvex(faces, indexedLoop)
Add faces from a convex polygon.
+
addFacesByConvexBottomTopLoop(faces, indexedLoopBottom, indexedLoopTop)
Add faces from loops.
+
addFacesByConvexLoops(faces, indexedLoops)
Add faces from loops.
+
addFacesByConvexReversed(faces, indexedLoop)
Add faces from a reversed convex polygon.
+
addFacesByGrid(faces, grid)
Add faces from grid.
+
addFacesByLoop(faces, indexedLoop)
Add faces from a polygon which may be concave.
+
addFacesByLoopReversed(faces, indexedLoop)
Add faces from a reversed convex polygon.
+
addFacesByMeldedConvexLoops(faces, indexedLoops)
Add faces from melded loops.
+
addLoopToPointTable(loop, pointTable)
Add the points in the loop to the point table.
+
addMeldedPillarByLoops(faces, indexedLoops)
Add melded pillar by loops which may be concave.
+
addPillarByLoops(faces, indexedLoops)
Add pillar by loops which may be concave.
+
addPillarFromConvexLoopsGridTop(faces, indexedGridTop, indexedLoops)
Add pillar from convex loops and grid top.
+
addPillarFromConvexLoopsGrids(faces, indexedGrids, indexedLoops)
Add pillar from convex loops and grids.
+
addPointsAtZ(edgePair, points, radius, vertexes, z)
Add point complexes on the segment between the edge intersections with z.
+
addSymmetricXPath(outputs, path, x)
Add x path output to outputs.
+
addSymmetricXPaths(outputs, paths, x)
Add x paths outputs to outputs.
+
addSymmetricYPath(outputs, path, y)
Add y path output to outputs.
+
addSymmetricYPaths(outputs, paths, y)
Add y paths outputs to outputs.
+
addVector3Loop(loop, loops, vertexes, z)
Add vector3Loop to loops if there is something in it, for inset and outset.
+
addWithLeastLength(importRadius, loops, point)
Insert a point into a loop, at the index at which the loop would be shortest.
+
convertElementNode(elementNode, geometryOutput)
Convert the xml element to a TriangleMesh xml element.
+
getAddIndexedGrid(grid, vertexes, z)
Get and add an indexed grid.
+
getAddIndexedLoop(loop, vertexes, z)
Get and add an indexed loop.
+
getAddIndexedLoops(loop, vertexes, zList)
Get and add indexed loops.
+
getAdditionalLoopLength(loop, point, pointIndex)
Get the additional length added by inserting a point into a loop.
+
getCarveIntersectionFromEdge(edge, vertexes, z)
Get the complex where the carve intersects the edge.
+
getClosestDistanceIndexToPoint(point, loop)
Get the distance squared to the closest point of the loop and index of that point.
+
getDescendingAreaLoops(allPoints, corners, importRadius)
Get descending area loops which include most of the points.
+
getDescendingAreaOrientedLoops(allPoints, corners, importRadius)
Get descending area oriented loops which include most of the points.
+
getGeometryOutputByFacesVertexes(faces, vertexes)
Get geometry output dictionary by faces and vertexes.
+
getGeometryOutputCopy(object)
Get the geometry output copy.
+
getIndexedCellLoopsFromIndexedGrid(grid)
Get indexed cell loops from an indexed grid.
+
getIndexedLoopFromIndexedGrid(indexedGrid)
Get indexed loop from around the indexed grid.
+
getInfillDictionary(arounds, aroundWidth, infillInset, infillWidth, pixelTable, rotatedLoops, testLoops=None)
Get combined fill loops which include most of the points.
+
getInsetPoint(loop, tinyRadius)
Get the inset vertex.
+
getIsPathEntirelyOutsideTriangle(begin, center, end, vector3Path)
Determine if a path is entirely outside another loop.
+
getIsPointCloseInline(close, loop, point, pointIndex)
Insert a point into a loop, at the index at which the loop would be shortest.
+
getLoopLayerAppend(loopLayers, z)
Get next z and add extruder loops.
+
getLoopsFromCorrectMesh(edges, faces, vertexes, z)
Get loops from a carve of a correct mesh.
+
getLoopsFromUnprovenMesh(edges, faces, importRadius, vertexes, z)
Get loops from a carve of an unproven mesh.
+
getLoopsWithCorners(corners, importRadius, loops, pointTable)
Add corners to the loops.
+
getMeldedPillarOutput(loops)
Get melded pillar output.
+
getNewDerivation(elementNode)
Get new derivation.
+
getNextEdgeIndexAroundZ(edge, faces, remainingEdgeTable)
Get the next edge index in the mesh carve.
+
getOrientedLoops(loops)
Orient the loops which must be in descending order.
+
getOverlapRatio(loop, pointTable)
Get the overlap ratio between the loop and the point table.
+
getPath(edges, pathIndexes, loop, z)
Get the path from the edge intersections.
+
getPillarOutput(loops)
Get pillar output.
+
getPillarsOutput(loopLists)
Get pillars output.
+
getRemainingEdgeTable(edges, vertexes, z)
Get the remaining edge hashtable.
+
getRemainingLoopAddFace(faces, remainingLoop)
Get the remaining loop and add face.
+
getSharedFace(firstEdge, faces, secondEdge)
Get the face which is shared by two edges.
+
getSymmetricXLoop(path, vertexes, x)
Get symmetrix x loop.
+
getSymmetricYLoop(path, vertexes, y)
Get symmetrix y loop.
+
getUnifiedOutput(outputs)
Get unified output.
+
getUniqueVertexes(loops)
Get unique vertexes.
+
getWideAnglePointIndex(loop)
Get a point index which has a wide enough angle, most point indexes have a wide enough angle, this is just to make sure.
+
isInline(beginComplex, centerComplex, endComplex)
Determine if the three complex points form a line.
+
isPathAdded(edges, faces, loops, remainingEdgeTable, vertexes, z)
Get the path indexes around a triangle mesh carve and add the path to the flat loops.
+
processElementNode(elementNode)
Process the xml element.
+
setEdgeMaximumMinimum(edge, vertexes)
Set the edge maximum and minimum.
+
sortLoopsInOrderOfArea(isDescending, loops)
Sort the loops in the order of area according isDescending.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.union.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.union.html new file mode 100644 index 0000000..9d08e2e --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.solids.union.html @@ -0,0 +1,162 @@ + + +Python: module fabmetheus_utilities.geometry.solids.union + + + + +
 
+ 
fabmetheus_utilities.geometry.solids.union ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/solids/union.py
+

Boolean geometry union of solids.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.geometry.solids.difference
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+
fabmetheus_utilities.geometry.solids.group
+

+ + + + + +
 
+Classes
       
+
fabmetheus_utilities.geometry.solids.difference.Difference(fabmetheus_utilities.geometry.geometry_utilities.boolean_solid.BooleanSolid) +
+
+
Union +
+
+
+

+ + + + + + + +
 
+class Union(fabmetheus_utilities.geometry.solids.difference.Difference)
   A difference object.
 
 
Method resolution order:
+
Union
+
fabmetheus_utilities.geometry.solids.difference.Difference
+
fabmetheus_utilities.geometry.geometry_utilities.boolean_solid.BooleanSolid
+
fabmetheus_utilities.geometry.solids.group.Group
+
fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary
+
+
+Methods defined here:
+
getLoopsFromObjectLoopsList(self, importRadius, visibleObjectLoopsList)
Get loops from visible object loops list.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.difference.Difference:
+
getXMLLocalName(self)
Get xml class name.
+ +
+Methods inherited from fabmetheus_utilities.geometry.geometry_utilities.boolean_solid.BooleanSolid:
+
getDifference(self, importRadius, visibleObjectLoopsList)
Get subtracted loops sliced through shape.
+ +
getIntersection(self, importRadius, visibleObjectLoopsList)
Get intersected loops sliced through shape.
+ +
getLoops(self, importRadius, z)
Get loops sliced through shape.
+ +
getTransformedPaths(self)
Get all transformed paths.
+ +
getUnion(self, importRadius, visibleObjectLoopsList)
Get joined loops sliced through shape.
+ +
+Methods inherited from fabmetheus_utilities.geometry.solids.group.Group:
+
__init__(self)
Add empty lists.
+ +
addXMLInnerSection(self, depth, output)
Add xml inner section for this object.
+ +
addXMLSection(self, depth, output)
Add the xml section for this object.
+ +
getMatrix4X4(self)
Get the matrix4X4.
+ +
getMatrixChainTetragrid(self)
Get the matrix chain tetragrid.
+ +
getVisible(self)
Get visible.
+ +
setToElementNode(self, elementNode)
Set to elementNode.
+ +
+Methods inherited from fabmetheus_utilities.geometry.geometry_tools.dictionary.Dictionary:
+
__repr__(self)
Get the string representation of this object info.
+ +
addXML(self, depth, output)
Add xml for this object.
+ +
addXMLArchivableObjects(self, depth, output)
Add xml for this object.
+ +
createShape(self)
Create the shape.
+ +
getAttributes(self)
Get attribute table.
+ +
getComplexTransformedPathLists(self)
Get complex transformed path lists.
+ +
getFabricationExtension(self)
Get fabrication extension.
+ +
getFabricationText(self, addLayerTemplate)
Get fabrication text.
+ +
getGeometryOutput(self)
Get geometry output dictionary.
+ +
getMinimumZ(self)
Get the minimum z.
+ +
getPaths(self)
Get all paths.
+ +
getTransformedVertexes(self)
Get all transformed vertexes.
+ +
getTriangleMeshes(self)
Get all triangleMeshes.
+ +
getType(self)
Get type.
+ +
getVertexes(self)
Get all vertexes.
+ +
transformGeometryOutput(self, geometryOutput)
Transform the geometry output by the local matrix4x4.
+ +

+ + + + + +
 
+Functions
       
convertElementNode(elementNode, geometryOutput)
Convert the xml element to a union xml element.
+
getNewDerivation(elementNode)
Get new derivation.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://hydraraptor.blogspot.com/>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements._print.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements._print.html new file mode 100644 index 0000000..43c68b1 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements._print.html @@ -0,0 +1,62 @@ + + +Python: module fabmetheus_utilities.geometry.statements._print + + + + +
 
+ 
fabmetheus_utilities.geometry.statements._print ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/statements/_print.py
+

Print statement.
+
+There is also the print attribute in geometry_utilities/evaluate_fundamentals/print.py
+
+The model is xml_models/geometry_utilities/evaluate_fundamentals/print.xml

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+

+ + + + + +
 
+Functions
       
getLocalDictionary(attributesKey, elementNode)
Get the local dictionary.
+
printAttributesKey(attributesKey, elementNode)
Print the attributesKey.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.class.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.class.html new file mode 100644 index 0000000..0702337 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.class.html @@ -0,0 +1,55 @@ + + +Python: module fabmetheus_utilities.geometry.statements.class + + + + +
 
+ 
fabmetheus_utilities.geometry.statements.class ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/statements/class.py
+

Class.

+

+ + + + + +
 
+Modules
       
__init__
+

+ + + + + +
 
+Functions
       
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.elif.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.elif.html new file mode 100644 index 0000000..2ca1c39 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.elif.html @@ -0,0 +1,57 @@ + + +Python: module fabmetheus_utilities.geometry.statements.elif + + + + +
 
+ 
fabmetheus_utilities.geometry.statements.elif ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/statements/elif.py
+

Polygon path.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+

+ + + + + +
 
+Functions
       
processElementNode(elementNode)
Process the xml element.
+
processElse(elementNode)
Process the else statement.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.else.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.else.html new file mode 100644 index 0000000..b773cec --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.else.html @@ -0,0 +1,57 @@ + + +Python: module fabmetheus_utilities.geometry.statements.else + + + + +
 
+ 
fabmetheus_utilities.geometry.statements.else ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/statements/else.py
+

Polygon path.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+

+ + + + + +
 
+Functions
       
processElementNode(elementNode)
Process the xml element.
+
processElse(elementNode)
Process the else statement.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.for.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.for.html new file mode 100644 index 0000000..5fe03d1 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.for.html @@ -0,0 +1,79 @@ + + +Python: module fabmetheus_utilities.geometry.statements.for + + + + +
 
+ 
fabmetheus_utilities.geometry.statements.for ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/statements/for.py
+

Polygon path.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+

+ + + + + +
 
+Classes
       
+
IndexValue +
+

+ + + + + + + +
 
+class IndexValue
   Class to get the in attribute, the index name and the value name.
 
 Methods defined here:
+
__init__(self, elementNode)
Initialize.
+ +

+ + + + + +
 
+Functions
       
processChildNodesByIndexValue(elementNode, function, index, indexValue, value)
Process childNodes by index value.
+
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.function.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.function.html new file mode 100644 index 0000000..07378dd --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.function.html @@ -0,0 +1,55 @@ + + +Python: module fabmetheus_utilities.geometry.statements.function + + + + +
 
+ 
fabmetheus_utilities.geometry.statements.function ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/statements/function.py
+

Polygon path.

+

+ + + + + +
 
+Modules
       
__init__
+

+ + + + + +
 
+Functions
       
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.html new file mode 100644 index 0000000..0cd0b33 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.html @@ -0,0 +1,40 @@ + + +Python: package fabmetheus_utilities.geometry.statements + + + + +
 
+ 
fabmetheus_utilities.geometry.statements
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/statements/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
_print
+class
+elif
+
else
+for
+function
+
if
+return
+statement
+
while
+

+ + + + + +
 
+Data
       level = 3
+numberOfLevelsDeepInPackageHierarchy = 3
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.if.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.if.html new file mode 100644 index 0000000..924beb6 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.if.html @@ -0,0 +1,56 @@ + + +Python: module fabmetheus_utilities.geometry.statements.if + + + + +
 
+ 
fabmetheus_utilities.geometry.statements.if ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/statements/if.py
+

Polygon path.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+

+ + + + + +
 
+Functions
       
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.return.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.return.html new file mode 100644 index 0000000..aee419a --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.return.html @@ -0,0 +1,56 @@ + + +Python: module fabmetheus_utilities.geometry.statements.return + + + + +
 
+ 
fabmetheus_utilities.geometry.statements.return ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/statements/return.py
+

Polygon path.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+

+ + + + + +
 
+Functions
       
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.statement.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.statement.html new file mode 100644 index 0000000..c5be584 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.statement.html @@ -0,0 +1,56 @@ + + +Python: module fabmetheus_utilities.geometry.statements.statement + + + + +
 
+ 
fabmetheus_utilities.geometry.statements.statement ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/statements/statement.py
+

Polygon path.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+

+ + + + + +
 
+Functions
       
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.while.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.while.html new file mode 100644 index 0000000..6a27b40 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry.statements.while.html @@ -0,0 +1,56 @@ + + +Python: module fabmetheus_utilities.geometry.statements.while + + + + +
 
+ 
fabmetheus_utilities.geometry.statements.while ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry/statements/while.py
+

Polygon path.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+

+ + + + + +
 
+Functions
       
processElementNode(elementNode)
Process the xml element.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry_plugins.creation.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry_plugins.creation.html new file mode 100644 index 0000000..d65da69 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry_plugins.creation.html @@ -0,0 +1,30 @@ + + +Python: package fabmetheus_utilities.geometry_plugins.creation + + + + +
 
+ 
fabmetheus_utilities.geometry_plugins.creation
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry_plugins/creation/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       

+ + + + + +
 
+Data
       level = 3
+numberOfLevelsDeepInPackageHierarchy = 3
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry_plugins.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry_plugins.html new file mode 100644 index 0000000..27a2c48 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry_plugins.html @@ -0,0 +1,35 @@ + + +Python: package fabmetheus_utilities.geometry_plugins + + + + +
 
+ 
fabmetheus_utilities.geometry_plugins
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry_plugins/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
creation (package)
+manipulation_matrix (package)
+
manipulation_meta (package)
+manipulation_paths (package)
+
manipulation_shapes (package)
+

+ + + + + +
 
+Data
       level = 2
+numberOfLevelsDeepInPackageHierarchy = 2
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry_plugins.manipulation_matrix.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry_plugins.manipulation_matrix.html new file mode 100644 index 0000000..adfbe60 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry_plugins.manipulation_matrix.html @@ -0,0 +1,30 @@ + + +Python: package fabmetheus_utilities.geometry_plugins.manipulation_matrix + + + + +
 
+ 
fabmetheus_utilities.geometry_plugins.manipulation_matrix
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry_plugins/manipulation_matrix/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       

+ + + + + +
 
+Data
       level = 3
+numberOfLevelsDeepInPackageHierarchy = 3
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry_plugins.manipulation_meta.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry_plugins.manipulation_meta.html new file mode 100644 index 0000000..b723865 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry_plugins.manipulation_meta.html @@ -0,0 +1,30 @@ + + +Python: package fabmetheus_utilities.geometry_plugins.manipulation_meta + + + + +
 
+ 
fabmetheus_utilities.geometry_plugins.manipulation_meta
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry_plugins/manipulation_meta/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       

+ + + + + +
 
+Data
       level = 3
+numberOfLevelsDeepInPackageHierarchy = 3
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry_plugins.manipulation_paths.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry_plugins.manipulation_paths.html new file mode 100644 index 0000000..1d4f16a --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry_plugins.manipulation_paths.html @@ -0,0 +1,30 @@ + + +Python: package fabmetheus_utilities.geometry_plugins.manipulation_paths + + + + +
 
+ 
fabmetheus_utilities.geometry_plugins.manipulation_paths
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry_plugins/manipulation_paths/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       

+ + + + + +
 
+Data
       level = 3
+numberOfLevelsDeepInPackageHierarchy = 3
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.geometry_plugins.manipulation_shapes.html b/SkeinPyPy/documentation/fabmetheus_utilities.geometry_plugins.manipulation_shapes.html new file mode 100644 index 0000000..16c5ef3 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.geometry_plugins.manipulation_shapes.html @@ -0,0 +1,30 @@ + + +Python: package fabmetheus_utilities.geometry_plugins.manipulation_shapes + + + + +
 
+ 
fabmetheus_utilities.geometry_plugins.manipulation_shapes
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/geometry_plugins/manipulation_shapes/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       

+ + + + + +
 
+Data
       level = 3
+numberOfLevelsDeepInPackageHierarchy = 3
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.hidden_scrollbar.html b/SkeinPyPy/documentation/fabmetheus_utilities.hidden_scrollbar.html new file mode 100644 index 0000000..e202aee --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.hidden_scrollbar.html @@ -0,0 +1,39 @@ + + +Python: module fabmetheus_utilities.hidden_scrollbar + + + + +
 
+ 
fabmetheus_utilities.hidden_scrollbar ($Date: 2008/23/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/hidden_scrollbar.py
+

Hidden scrollbar is in its own file so that even if Tkinter is not installed, settings can still be imported.

+

+ + + + + +
 
+Modules
       
__init__
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__date__ = '$Date: 2008/23/04 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.html b/SkeinPyPy/documentation/fabmetheus_utilities.html new file mode 100644 index 0000000..95bd98b --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.html @@ -0,0 +1,56 @@ + + +Python: package fabmetheus_utilities + + + + +
 
+ 
fabmetheus_utilities
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/__init__.py
+

+Previous / Next / Contents +

+


+This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Package Contents
       
archive
+euclidean
+fabmetheus_tools (package)
+gcodec
+
geometry (package)
+geometry_plugins (package)
+hidden_scrollbar
+intercircle
+
settings
+svg_reader
+svg_writer
+vector3
+
vector3index
+xml_simple_reader
+xml_simple_writer
+

+ + + + + +
 
+Data
       level = 1
+numberOfLevelsDeepInPackageHierarchy = 1
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.intercircle.html b/SkeinPyPy/documentation/fabmetheus_utilities.intercircle.html new file mode 100644 index 0000000..d141f56 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.intercircle.html @@ -0,0 +1,188 @@ + + +Python: module fabmetheus_utilities.intercircle + + + + +
 
+ 
fabmetheus_utilities.intercircle ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/intercircle.py
+

Intercircle is a collection of utilities for intersecting circles, used to get smooth loops around a collection of points and inset & outset loops.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.euclidean
+
math
+

+ + + + + +
 
+Classes
       
+
BoundingLoop +
CenterOutset +
CircleIntersection +
CircleNode +
+

+ + + + + + + +
 
+class BoundingLoop
   A class to hold a bounding loop composed of a minimum complex, a maximum complex and an outset loop.
 
 Methods defined here:
+
__eq__(self, other)
Determine whether this bounding loop is identical to other one.
+ +
__repr__(self)
Get the string representation of this bounding loop.
+ +
getFromLoop(self, loop)
Get the bounding loop from a path.
+ +
getOutsetBoundingLoop(self, outsetDistance)
Outset the bounding rectangle and loop by a distance.
+ +
isEntirelyInsideAnother(self, anotherBoundingLoop)
Determine if this bounding loop is entirely inside another bounding loop.
+ +
isOverlappingAnother(self, anotherBoundingLoop)
Determine if this bounding loop is intersecting another bounding loop.
+ +
isOverlappingAnotherInList(self, boundingLoops)
Determine if this bounding loop is intersecting another bounding loop in a list.
+ +
isRectangleMissingAnother(self, anotherBoundingLoop)
Determine if the rectangle of this bounding loop is missing the rectangle of another bounding loop.
+ +

+ + + + + + + +
 
+class CenterOutset
   A class to hold a center and an outset.
 
 Methods defined here:
+
__init__(self, center, outset)
Set the center and outset.
+ +
__repr__(self)
Get the string representation of this CenterOutset.
+ +

+ + + + + + + +
 
+class CircleIntersection
   An intersection of two complex circles.
 
 Methods defined here:
+
__init__(self, circleNodeAhead, index, circleNodeBehind)
+ +
__repr__(self)
Get the string representation of this CircleIntersection.
+ +
addToList(self, circleIntersectionPath)
Add this to the circle intersection path, setting stepped on to be true.
+ +
getAbsolutePosition(self)
Get the absolute position.
+ +
getCircleIntersectionAhead(self)
Get the first circle intersection on the circle node ahead.
+ +
isWithinCircles(self, pixelTable)
Determine if this circle intersection is within the circle node circles.
+ +

+ + + + + + + +
 
+class CircleNode
   A complex node of complex circle intersections.
 
 Methods defined here:
+
__init__(self, oneOverRadius, point)
+ +
__repr__(self)
Get the string representation of this CircleNode.
+ +
getWithinNodes(self, pixelTable)
Get the nodes this circle node is within.
+ +

+ + + + + +
 
+Functions
       
addCircleIntersectionLoop(circleIntersectionLoop, circleIntersections)
Add a circle intersection loop.
+
addEndCap(begin, end, points, radius)
Get circular end cap.
+
addHalfPath(path, points, radius, thresholdRatio=0.9)
Add the points from every point on a half path and between points.
+
addInsetPointFromClockwiseTriple(begin, center, end, loop, radius)
Get inset point with possible intersection from clockwise triple, out from widdershins loop.
+
addOrbits(distanceFeedRate, loop, orbitalFeedRatePerSecond, temperatureChangeTime, z)
Add orbits with the extruder off.
+
addOrbitsIfLarge(distanceFeedRate, loop, orbitalFeedRatePerSecond, temperatureChangeTime, z)
Add orbits with the extruder off if the orbits are large enough.
+
addPointsFromSegment(pointBegin, pointEnd, points, radius, thresholdRatio=0.9)
Add point complexes between the endpoints of a segment.
+
directLoop(isWiddershins, loop)
Direct the loop.
+
directLoopLists(isWiddershins, loopLists)
Direct the loop lists.
+
directLoops(isWiddershins, loops)
Direct the loops.
+
getAroundsFromLoop(loop, radius, thresholdRatio=0.9)
Get the arounds from the loop.
+
getAroundsFromLoops(loops, radius, thresholdRatio=0.9)
Get the arounds from the loops.
+
getAroundsFromPath(path, radius, thresholdRatio=0.9)
Get the arounds from the path.
+
getAroundsFromPathPoints(points, radius, thresholdRatio=0.9)
Get the arounds from the path.
+
getAroundsFromPaths(paths, radius, thresholdRatio=0.9)
Get the arounds from the path.
+
getAroundsFromPoints(points, radius)
Get the arounds from the points.
+
getCentersFromCircleNodes(circleNodes, radius)
Get the complex centers of the circle intersection loops from circle nodes.
+
getCentersFromIntersectionLoop(circleIntersectionLoop, radius)
Get the centers from the intersection loop.
+
getCentersFromIntersectionLoops(circleIntersectionLoops, radius)
Get the centers from the intersection loops.
+
getCentersFromLoop(loop, radius)
Get the centers of the loop.
+
getCentersFromLoopDirection(isWiddershins, loop, radius)
Get the centers of the loop which go around in the given direction.
+
getCentersFromPoints(points, radius)
Get the centers from the points.
+
getCircleIntersectionLoops(circleIntersections)
Get all the loops going through the circle intersections.
+
getCircleIntersectionsFromCircleNodes(circleNodes)
Get all the circle intersections which exist between all the circle nodes.
+
getCircleNodesFromLoop(loop, radius, thresholdRatio=0.9)
Get the circle nodes from every point on a loop and between points.
+
getCircleNodesFromPoints(points, radius)
Get the circle nodes from a path.
+
getInsetLoopsFromLoop(loop, radius, thresholdRatio=0.9)
Get the inset loops, which might overlap.
+
getInsetLoopsFromLoops(loops, radius)
Get the inset loops, which might overlap.
+
getInsetLoopsFromVector3Loop(loop, radius, thresholdRatio=0.9)
Get the inset loops from vector3 loop, which might overlap.
+
getInsetSeparateLoopsFromAroundLoops(loops, radius, radiusAround, thresholdRatio=0.9)
Get the separate inset loops.
+
getInsetSeparateLoopsFromLoops(loops, radius, thresholdRatio=0.9)
Get the separate inset loops.
+
getIsLarge(loop, radius)
Determine if the loop is large enough.
+
getLargestCenterOutsetLoopFromLoop(loop, radius, thresholdRatio=0.9)
Get the largest circle outset loop from the loop.
+
getLargestCenterOutsetLoopFromLoopRegardless(loop, radius)
Get the largest circle outset loop from the loop, even if the radius has to be shrunk and even if there is still no outset loop.
+
getLargestInsetLoopFromLoop(loop, radius)
Get the largest inset loop from the loop.
+
getLargestInsetLoopFromLoopRegardless(loop, radius)
Get the largest inset loop from the loop, even if the radius has to be shrunk and even if there is still no inset loop.
+
getLoopsFromLoopsDirection(isWiddershins, loops)
Get the loops going round in a given direction.
+
getPointsFromLoop(loop, radius, thresholdRatio=0.9)
Get the points from every point on a loop and between points.
+
getPointsFromLoops(loops, radius, thresholdRatio=0.9)
Get the points from every point on a loop and between points.
+
getPointsFromPath(path, radius, thresholdRatio=0.9)
Get the points from every point on a path and between points.
+
getSimplifiedInsetFromClockwiseLoop(loop, radius)
Get loop inset from clockwise loop, out from widdershins loop.
+
getWiddershinsByLength(begin, end, length)
Get the widdershins by length.
+
getWithoutIntersections(loop)
Get loop without intersections.
+
isLargeSameDirection(inset, loop, radius)
Determine if the inset is in the same direction as the loop and it is large enough.
+
isLoopIntersectingLoop(anotherLoop, loop)
Determine if the a loop is intersecting another loop.
+
orbitsAreLarge(loop, temperatureChangeTime)
Determine if the orbits are large enough.
+
removeIntersection(loop)
Get loop without the first intersection.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalDecreasingRadiusMultipliers = [1.0, 0.55, 0.35, 0.2]
+globalIntercircleMultiplier = 1.04

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.settings.html b/SkeinPyPy/documentation/fabmetheus_utilities.settings.html new file mode 100644 index 0000000..e23ab41 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.settings.html @@ -0,0 +1,1766 @@ + + +Python: module fabmetheus_utilities.settings + + + + +
 
+ 
fabmetheus_utilities.settings ($Date: 2008/23/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/settings.py
+

Settings is a collection of utilities to display, read & write the settings and position widgets.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+cStringIO
+
fabmetheus_utilities.euclidean
+fabmetheus_utilities.gcodec
+math
+
os
+shutil
+sys
+
traceback
+webbrowser
+

+ + + + + +
 
+Classes
       
+
CloseListener +
DisplayToolButton +
FileHelpMenuBar +
FrameList +
GridHorizontal +
GridVertical +
HelpPage +
HelpPageRepository +
LabelDisplay +
LabelHelp +
LabelSeparator +
LatentStringVar +
LayerCount +
MenuButtonDisplay +
PluginFrame +
+
+
PluginGroupFrame +
+
+
RepositoryDialog +
StringSetting +
+
+
BooleanSetting +
+
+
MenuRadio +
Radio +
+
+
RadioCapitalized +
+
+
RadioPlugin +
+
+
RadioCapitalizedButton +
+
+
+
+
FileNameInput +
FloatSetting +
+
+
FloatSpin +
+
+
FloatSpinNotOnMenu +
FloatSpinUpdate +
IntSpin +
+
+
IntSpinNotOnMenu +
IntSpinUpdate +
+
+
+
+
IntSetting +
+
+
TextSetting +
WindowPosition +
+
+
TokenConversion +
ToolDialog +
+

+ + + + + + + +
 
+class BooleanSetting(StringSetting)
   A class to display, read & write a boolean.
 
 Methods defined here:
+
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
addToMenu(self, repositoryMenu)
Add this to the repository menu.
+ +
addToMenuFrameable(self, repositoryMenu)
Add this to the frameable repository menu.
+ +
setStateToValue(self)
Set the checkbutton to the boolean.
+ +
setToDisplay(self)
Do nothing because toggleCheckbutton is handling the value.
+ +
setValueToString(self, valueString)
Set the boolean to the string.
+ +
toggleCheckbutton(self)
Workaround for Tkinter bug, toggle the value.
+ +
toggleMenuCheckbutton(self)
Workaround for Tkinter bug, toggle the value.
+ +
+Methods inherited from StringSetting:
+
__init__(self)
Set the update function to none.
+ +
__repr__(self)
Get the string representation of this StringSetting.
+ +
addToWindow(self)
Add this to the repository frame list.
+ +
bindEntry(self)
Bind the entry to the update function.
+ +
createEntry(self, root)
Create the entry.
+ +
getFromValue(self, name, repository, value)
Initialize.
+ +
getFromValueOnly(self, name, repository, value)
Initialize.
+ +
getFromValueOnlyAddToRepository(self, name, repository, value)
Initialize.
+ +
removeFromWindow(self)
Remove this from the repository frame list.
+ +
setUpdateFunction(self, updateFunction)
Set the update function.
+ +
setValueToSplitLine(self, lineIndex, lines, splitLine)
Set the value to the second word of a split line.
+ +
updateSaveListeners(self)
Update save listeners if any.
+ +
writeToRepositoryWriter(self, repositoryWriter)
Write tab separated name and value to the repository writer.
+ +

+ + + + + + + +
 
+class CloseListener
   A class to listen to link a window to the global repository dialog list table.
 
 Methods defined here:
+
__init__(self, window, closeFunction=None)
Add the window to the global repository dialog list table.
+ +
listenToWidget(self, widget)
Listen to the destroy message of the widget.
+ +
wasClosed(self, event)
The dialog was closed.
+ +

+ + + + + + + +
 
+class DisplayToolButton
   A class to display the tool dialog button, in a two column wide table.
 
 Methods defined here:
+
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
displayDialog(self)
Display function.
+ +
getFromPath(self, important, name, path, repository)
Initialize.
+ +

+ + + + + +
 
+class FileHelpMenuBar
    Methods defined here:
+
__init__(self, root)
Create a menu bar with a file and help menu.
+ +
addMenuToMenuBar(self, labelText, menu)
Add a menu to the menu bar.
+ +
addPluginToMenuBar(self, modulePath, repository, window)
Add a menu to the menu bar from a tool.
+ +
completeMenu(self, closeFunction, repository, saveFunction, window)
Complete the menu.
+ +
saveClose(self)
Call the save function then the close function.
+ +

+ + + + + + + +
 
+class FileNameInput(StringSetting)
   A class to display, read & write a fileName.
 
 Methods defined here:
+
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
execute(self)
Open the file picker.
+ +
getFileNameFirstTypes(self)
Get the file types with the file type of the fileName moved to the front of the list.
+ +
getFromFileName(self, fileTypes, name, repository, value)
Initialize.
+ +
setCancelledValue(self, fileName)
Set the value to the file name and wasCancelled true if a file was not picked.
+ +
setToDisplay(self)
Do nothing because the file dialog is handling the value.
+ +
+Methods inherited from StringSetting:
+
__init__(self)
Set the update function to none.
+ +
__repr__(self)
Get the string representation of this StringSetting.
+ +
addToMenu(self, repositoryMenu)
Do nothing because this should only be added to a frameable repository menu.
+ +
addToMenuFrameable(self, repositoryMenu)
Add this to the frameable repository menu.
+ +
addToWindow(self)
Add this to the repository frame list.
+ +
bindEntry(self)
Bind the entry to the update function.
+ +
createEntry(self, root)
Create the entry.
+ +
getFromValue(self, name, repository, value)
Initialize.
+ +
getFromValueOnly(self, name, repository, value)
Initialize.
+ +
getFromValueOnlyAddToRepository(self, name, repository, value)
Initialize.
+ +
removeFromWindow(self)
Remove this from the repository frame list.
+ +
setStateToValue(self)
Set the entry to the value.
+ +
setUpdateFunction(self, updateFunction)
Set the update function.
+ +
setValueToSplitLine(self, lineIndex, lines, splitLine)
Set the value to the second word of a split line.
+ +
setValueToString(self, valueString)
Set the value to the value string.
+ +
updateSaveListeners(self)
Update save listeners if any.
+ +
writeToRepositoryWriter(self, repositoryWriter)
Write tab separated name and value to the repository writer.
+ +

+ + + + + + + +
 
+class FloatSetting(StringSetting)
   A class to display, read & write a float.
 
 Methods defined here:
+
setValueToString(self, valueString)
Set the float to the string.
+ +
+Methods inherited from StringSetting:
+
__init__(self)
Set the update function to none.
+ +
__repr__(self)
Get the string representation of this StringSetting.
+ +
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
addToMenu(self, repositoryMenu)
Do nothing because this should only be added to a frameable repository menu.
+ +
addToMenuFrameable(self, repositoryMenu)
Add this to the frameable repository menu.
+ +
addToWindow(self)
Add this to the repository frame list.
+ +
bindEntry(self)
Bind the entry to the update function.
+ +
createEntry(self, root)
Create the entry.
+ +
getFromValue(self, name, repository, value)
Initialize.
+ +
getFromValueOnly(self, name, repository, value)
Initialize.
+ +
getFromValueOnlyAddToRepository(self, name, repository, value)
Initialize.
+ +
removeFromWindow(self)
Remove this from the repository frame list.
+ +
setStateToValue(self)
Set the entry to the value.
+ +
setToDisplay(self)
Set the string to the entry field.
+ +
setUpdateFunction(self, updateFunction)
Set the update function.
+ +
setValueToSplitLine(self, lineIndex, lines, splitLine)
Set the value to the second word of a split line.
+ +
updateSaveListeners(self)
Update save listeners if any.
+ +
writeToRepositoryWriter(self, repositoryWriter)
Write tab separated name and value to the repository writer.
+ +

+ + + + + + + +
 
+class FloatSpin(FloatSetting)
   A class to display, read & write an float in a spin box.
 
 
Method resolution order:
+
FloatSpin
+
FloatSetting
+
StringSetting
+
+
+Methods defined here:
+
addToMenuFrameable(self, repositoryMenu)
Add this to the frameable repository menu.
+ +
bindEntry(self)
Bind the entry to the update function.
+ +
createEntry(self, root)
Create the entry.
+ +
decrease(self)
Decrease the value then set the state and color to the value.
+ +
entryUpdated(self, event=None)
Create the entry.
+ +
getFromValue(self, from_, name, repository, to, value)
Initialize.
+ +
increase(self)
Increase the value then set the state and color to the value.
+ +
setColor(self, event=None)
Set the color to the value, yellow if it is lower than the default and blue if it is higher.
+ +
setColorToDisplay(self, event=None)
Set the color to the value, yellow if it is lower than the default and blue if it is higher.
+ +
setStateToValue(self)
Set the entry to the value.
+ +
setStateUpdateColor(self)
Set the state to the value, call the update function, then set the color.
+ +
+Methods inherited from FloatSetting:
+
setValueToString(self, valueString)
Set the float to the string.
+ +
+Methods inherited from StringSetting:
+
__init__(self)
Set the update function to none.
+ +
__repr__(self)
Get the string representation of this StringSetting.
+ +
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
addToMenu(self, repositoryMenu)
Do nothing because this should only be added to a frameable repository menu.
+ +
addToWindow(self)
Add this to the repository frame list.
+ +
getFromValueOnly(self, name, repository, value)
Initialize.
+ +
getFromValueOnlyAddToRepository(self, name, repository, value)
Initialize.
+ +
removeFromWindow(self)
Remove this from the repository frame list.
+ +
setToDisplay(self)
Set the string to the entry field.
+ +
setUpdateFunction(self, updateFunction)
Set the update function.
+ +
setValueToSplitLine(self, lineIndex, lines, splitLine)
Set the value to the second word of a split line.
+ +
updateSaveListeners(self)
Update save listeners if any.
+ +
writeToRepositoryWriter(self, repositoryWriter)
Write tab separated name and value to the repository writer.
+ +

+ + + + + + + +
 
+class FloatSpinNotOnMenu(FloatSpin)
   A class to display, read & write an float in a spin box, which is not to be added to a menu.
 
 
Method resolution order:
+
FloatSpinNotOnMenu
+
FloatSpin
+
FloatSetting
+
StringSetting
+
+
+Methods defined here:
+
getFromValueOnlyAddToRepository(self, name, repository, value)
Initialize.
+ +
+Methods inherited from FloatSpin:
+
addToMenuFrameable(self, repositoryMenu)
Add this to the frameable repository menu.
+ +
bindEntry(self)
Bind the entry to the update function.
+ +
createEntry(self, root)
Create the entry.
+ +
decrease(self)
Decrease the value then set the state and color to the value.
+ +
entryUpdated(self, event=None)
Create the entry.
+ +
getFromValue(self, from_, name, repository, to, value)
Initialize.
+ +
increase(self)
Increase the value then set the state and color to the value.
+ +
setColor(self, event=None)
Set the color to the value, yellow if it is lower than the default and blue if it is higher.
+ +
setColorToDisplay(self, event=None)
Set the color to the value, yellow if it is lower than the default and blue if it is higher.
+ +
setStateToValue(self)
Set the entry to the value.
+ +
setStateUpdateColor(self)
Set the state to the value, call the update function, then set the color.
+ +
+Methods inherited from FloatSetting:
+
setValueToString(self, valueString)
Set the float to the string.
+ +
+Methods inherited from StringSetting:
+
__init__(self)
Set the update function to none.
+ +
__repr__(self)
Get the string representation of this StringSetting.
+ +
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
addToMenu(self, repositoryMenu)
Do nothing because this should only be added to a frameable repository menu.
+ +
addToWindow(self)
Add this to the repository frame list.
+ +
getFromValueOnly(self, name, repository, value)
Initialize.
+ +
removeFromWindow(self)
Remove this from the repository frame list.
+ +
setToDisplay(self)
Set the string to the entry field.
+ +
setUpdateFunction(self, updateFunction)
Set the update function.
+ +
setValueToSplitLine(self, lineIndex, lines, splitLine)
Set the value to the second word of a split line.
+ +
updateSaveListeners(self)
Update save listeners if any.
+ +
writeToRepositoryWriter(self, repositoryWriter)
Write tab separated name and value to the repository writer.
+ +

+ + + + + + + +
 
+class FloatSpinUpdate(FloatSpin)
   A class to display, read, update & write an float in a spin box.
 
 
Method resolution order:
+
FloatSpinUpdate
+
FloatSpin
+
FloatSetting
+
StringSetting
+
+
+Methods defined here:
+
createEntry(self, root)
Create the entry.
+ +
+Methods inherited from FloatSpin:
+
addToMenuFrameable(self, repositoryMenu)
Add this to the frameable repository menu.
+ +
bindEntry(self)
Bind the entry to the update function.
+ +
decrease(self)
Decrease the value then set the state and color to the value.
+ +
entryUpdated(self, event=None)
Create the entry.
+ +
getFromValue(self, from_, name, repository, to, value)
Initialize.
+ +
increase(self)
Increase the value then set the state and color to the value.
+ +
setColor(self, event=None)
Set the color to the value, yellow if it is lower than the default and blue if it is higher.
+ +
setColorToDisplay(self, event=None)
Set the color to the value, yellow if it is lower than the default and blue if it is higher.
+ +
setStateToValue(self)
Set the entry to the value.
+ +
setStateUpdateColor(self)
Set the state to the value, call the update function, then set the color.
+ +
+Methods inherited from FloatSetting:
+
setValueToString(self, valueString)
Set the float to the string.
+ +
+Methods inherited from StringSetting:
+
__init__(self)
Set the update function to none.
+ +
__repr__(self)
Get the string representation of this StringSetting.
+ +
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
addToMenu(self, repositoryMenu)
Do nothing because this should only be added to a frameable repository menu.
+ +
addToWindow(self)
Add this to the repository frame list.
+ +
getFromValueOnly(self, name, repository, value)
Initialize.
+ +
getFromValueOnlyAddToRepository(self, name, repository, value)
Initialize.
+ +
removeFromWindow(self)
Remove this from the repository frame list.
+ +
setToDisplay(self)
Set the string to the entry field.
+ +
setUpdateFunction(self, updateFunction)
Set the update function.
+ +
setValueToSplitLine(self, lineIndex, lines, splitLine)
Set the value to the second word of a split line.
+ +
updateSaveListeners(self)
Update save listeners if any.
+ +
writeToRepositoryWriter(self, repositoryWriter)
Write tab separated name and value to the repository writer.
+ +

+ + + + + + + +
 
+class FrameList
   A class to list the frames.
 
 Methods defined here:
+
addToList(self, word)
Add the word to the sorted list.
+ +
getFromValue(self, name, repository, value)
Initialize.
+ +
removeFromList(self, word)
Remove the word from the sorted list.
+ +
setToDisplay(self)
Do nothing because frame list does not have a display.
+ +
setValueToSplitLine(self, lineIndex, lines, splitLine)
Set the value to the second and later words of a split line.
+ +
updateSaveListeners(self)
Update save listeners if any.
+ +
writeToRepositoryWriter(self, repositoryWriter)
Write tab separated name and list to the repository writer.
+ +

+ + + + + + + +
 
+class GridHorizontal
   A class to place elements horizontally on a grid.
 
 Methods defined here:
+
__init__(self, column, row)
Initialize the column and row.
+ +
getCopy(self)
Get a copy.
+ +
increment(self)
Increment the position horizontally.
+ +

+ + + + + + + +
 
+class GridVertical
   A class to place elements vertically on a grid.
 
 Methods defined here:
+
__init__(self, column, row)
Initialize the column and row.
+ +
execute(self)
The execute button was clicked.
+ +
getCopy(self)
Get a copy.
+ +
increment(self)
Increment the position vertically.
+ +
incrementGivenNumberOfColumns(self, numberOfColumns)
Increment the position vertically and offset it horizontally by the given number of columns.
+ +
setExecutablesRepository(self, repository)
Set the executables to an empty list and set the repository.
+ +

+ + + + + + + +
 
+class HelpPage
   A class to open a help page.
 
 Methods defined here:
+
__init__(self)
Initialize column.
+ +
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
addToMenu(self, repositoryMenu)
Add this to the repository menu.
+ +
addToMenuFrameable(self, repositoryMenu)
Add this to the frameable repository menu.
+ +
getFromNameAfterHTTP(self, afterHTTP, name, repository)
Initialize.
+ +
getFromNameAfterWWW(self, afterWWW, name, repository)
Initialize.
+ +
getFromNameSubName(self, name, repository, subName='')
Initialize.
+ +
getOpenFromAbsolute(self, hypertextAddress)
Get the open help page function from the hypertext address.
+ +
getOpenFromAfterHTTP(self, afterHTTP)
Get the open help page function from the part of the address after the HTTP.
+ +
getOpenFromAfterWWW(self, afterWWW)
Get the open help page function from the afterWWW of the address after the www.
+ +
getOpenFromDocumentationSubName(self, subName='')
Get the open help page function from the afterWWW of the address after the www.
+ +
openPage(self, event=None)
Open the browser to the hypertext address.
+ +
setToNameRepository(self, name, repository)
Set to the name and repository.
+ +

+ + + + + + + +
 
+class HelpPageRepository
   A class to open a repository help page.
 
 Methods defined here:
+
__init__(self, repository)
Add this to the dialog.
+ +
openPage(self, event=None)
Open the browser to the repository help page.
+ +

+ + + + + + + +
 
+class IntSetting(FloatSetting)
   A class to display, read & write an int.
 
 
Method resolution order:
+
IntSetting
+
FloatSetting
+
StringSetting
+
+
+Methods defined here:
+
setValueToString(self, valueString)
Set the integer to the string.
+ +
+Methods inherited from StringSetting:
+
__init__(self)
Set the update function to none.
+ +
__repr__(self)
Get the string representation of this StringSetting.
+ +
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
addToMenu(self, repositoryMenu)
Do nothing because this should only be added to a frameable repository menu.
+ +
addToMenuFrameable(self, repositoryMenu)
Add this to the frameable repository menu.
+ +
addToWindow(self)
Add this to the repository frame list.
+ +
bindEntry(self)
Bind the entry to the update function.
+ +
createEntry(self, root)
Create the entry.
+ +
getFromValue(self, name, repository, value)
Initialize.
+ +
getFromValueOnly(self, name, repository, value)
Initialize.
+ +
getFromValueOnlyAddToRepository(self, name, repository, value)
Initialize.
+ +
removeFromWindow(self)
Remove this from the repository frame list.
+ +
setStateToValue(self)
Set the entry to the value.
+ +
setToDisplay(self)
Set the string to the entry field.
+ +
setUpdateFunction(self, updateFunction)
Set the update function.
+ +
setValueToSplitLine(self, lineIndex, lines, splitLine)
Set the value to the second word of a split line.
+ +
updateSaveListeners(self)
Update save listeners if any.
+ +
writeToRepositoryWriter(self, repositoryWriter)
Write tab separated name and value to the repository writer.
+ +

+ + + + + + + +
 
+class IntSpin(FloatSpin)
   A class to display, read & write an int in a spin box.
 
 
Method resolution order:
+
IntSpin
+
FloatSpin
+
FloatSetting
+
StringSetting
+
+
+Methods defined here:
+
getFromValue(self, from_, name, repository, to, value)
Initialize.
+ +
getSingleIncrementFromValue(self, from_, name, repository, to, value)
Initialize.
+ +
setValueToString(self, valueString)
Set the integer to the string.
+ +
+Methods inherited from FloatSpin:
+
addToMenuFrameable(self, repositoryMenu)
Add this to the frameable repository menu.
+ +
bindEntry(self)
Bind the entry to the update function.
+ +
createEntry(self, root)
Create the entry.
+ +
decrease(self)
Decrease the value then set the state and color to the value.
+ +
entryUpdated(self, event=None)
Create the entry.
+ +
increase(self)
Increase the value then set the state and color to the value.
+ +
setColor(self, event=None)
Set the color to the value, yellow if it is lower than the default and blue if it is higher.
+ +
setColorToDisplay(self, event=None)
Set the color to the value, yellow if it is lower than the default and blue if it is higher.
+ +
setStateToValue(self)
Set the entry to the value.
+ +
setStateUpdateColor(self)
Set the state to the value, call the update function, then set the color.
+ +
+Methods inherited from StringSetting:
+
__init__(self)
Set the update function to none.
+ +
__repr__(self)
Get the string representation of this StringSetting.
+ +
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
addToMenu(self, repositoryMenu)
Do nothing because this should only be added to a frameable repository menu.
+ +
addToWindow(self)
Add this to the repository frame list.
+ +
getFromValueOnly(self, name, repository, value)
Initialize.
+ +
getFromValueOnlyAddToRepository(self, name, repository, value)
Initialize.
+ +
removeFromWindow(self)
Remove this from the repository frame list.
+ +
setToDisplay(self)
Set the string to the entry field.
+ +
setUpdateFunction(self, updateFunction)
Set the update function.
+ +
setValueToSplitLine(self, lineIndex, lines, splitLine)
Set the value to the second word of a split line.
+ +
updateSaveListeners(self)
Update save listeners if any.
+ +
writeToRepositoryWriter(self, repositoryWriter)
Write tab separated name and value to the repository writer.
+ +

+ + + + + + + +
 
+class IntSpinNotOnMenu(IntSpin)
   A class to display, read & write an integer in a spin box, which is not to be added to a menu.
 
 
Method resolution order:
+
IntSpinNotOnMenu
+
IntSpin
+
FloatSpin
+
FloatSetting
+
StringSetting
+
+
+Methods defined here:
+
getFromValueOnlyAddToRepository(self, name, repository, value)
Initialize.
+ +
+Methods inherited from IntSpin:
+
getFromValue(self, from_, name, repository, to, value)
Initialize.
+ +
getSingleIncrementFromValue(self, from_, name, repository, to, value)
Initialize.
+ +
setValueToString(self, valueString)
Set the integer to the string.
+ +
+Methods inherited from FloatSpin:
+
addToMenuFrameable(self, repositoryMenu)
Add this to the frameable repository menu.
+ +
bindEntry(self)
Bind the entry to the update function.
+ +
createEntry(self, root)
Create the entry.
+ +
decrease(self)
Decrease the value then set the state and color to the value.
+ +
entryUpdated(self, event=None)
Create the entry.
+ +
increase(self)
Increase the value then set the state and color to the value.
+ +
setColor(self, event=None)
Set the color to the value, yellow if it is lower than the default and blue if it is higher.
+ +
setColorToDisplay(self, event=None)
Set the color to the value, yellow if it is lower than the default and blue if it is higher.
+ +
setStateToValue(self)
Set the entry to the value.
+ +
setStateUpdateColor(self)
Set the state to the value, call the update function, then set the color.
+ +
+Methods inherited from StringSetting:
+
__init__(self)
Set the update function to none.
+ +
__repr__(self)
Get the string representation of this StringSetting.
+ +
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
addToMenu(self, repositoryMenu)
Do nothing because this should only be added to a frameable repository menu.
+ +
addToWindow(self)
Add this to the repository frame list.
+ +
getFromValueOnly(self, name, repository, value)
Initialize.
+ +
removeFromWindow(self)
Remove this from the repository frame list.
+ +
setToDisplay(self)
Set the string to the entry field.
+ +
setUpdateFunction(self, updateFunction)
Set the update function.
+ +
setValueToSplitLine(self, lineIndex, lines, splitLine)
Set the value to the second word of a split line.
+ +
updateSaveListeners(self)
Update save listeners if any.
+ +
writeToRepositoryWriter(self, repositoryWriter)
Write tab separated name and value to the repository writer.
+ +

+ + + + + + + +
 
+class IntSpinUpdate(IntSpin)
   A class to display, read, update & write an int in a spin box.
 
 
Method resolution order:
+
IntSpinUpdate
+
IntSpin
+
FloatSpin
+
FloatSetting
+
StringSetting
+
+
+Methods defined here:
+
createEntry(self, root)
Create the entry.
+ +
+Methods inherited from IntSpin:
+
getFromValue(self, from_, name, repository, to, value)
Initialize.
+ +
getSingleIncrementFromValue(self, from_, name, repository, to, value)
Initialize.
+ +
setValueToString(self, valueString)
Set the integer to the string.
+ +
+Methods inherited from FloatSpin:
+
addToMenuFrameable(self, repositoryMenu)
Add this to the frameable repository menu.
+ +
bindEntry(self)
Bind the entry to the update function.
+ +
decrease(self)
Decrease the value then set the state and color to the value.
+ +
entryUpdated(self, event=None)
Create the entry.
+ +
increase(self)
Increase the value then set the state and color to the value.
+ +
setColor(self, event=None)
Set the color to the value, yellow if it is lower than the default and blue if it is higher.
+ +
setColorToDisplay(self, event=None)
Set the color to the value, yellow if it is lower than the default and blue if it is higher.
+ +
setStateToValue(self)
Set the entry to the value.
+ +
setStateUpdateColor(self)
Set the state to the value, call the update function, then set the color.
+ +
+Methods inherited from StringSetting:
+
__init__(self)
Set the update function to none.
+ +
__repr__(self)
Get the string representation of this StringSetting.
+ +
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
addToMenu(self, repositoryMenu)
Do nothing because this should only be added to a frameable repository menu.
+ +
addToWindow(self)
Add this to the repository frame list.
+ +
getFromValueOnly(self, name, repository, value)
Initialize.
+ +
getFromValueOnlyAddToRepository(self, name, repository, value)
Initialize.
+ +
removeFromWindow(self)
Remove this from the repository frame list.
+ +
setToDisplay(self)
Set the string to the entry field.
+ +
setUpdateFunction(self, updateFunction)
Set the update function.
+ +
setValueToSplitLine(self, lineIndex, lines, splitLine)
Set the value to the second word of a split line.
+ +
updateSaveListeners(self)
Update save listeners if any.
+ +
writeToRepositoryWriter(self, repositoryWriter)
Write tab separated name and value to the repository writer.
+ +

+ + + + + + + +
 
+class LabelDisplay
   A class to add a label.
 
 Methods defined here:
+
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
getFromName(self, name, repository)
Initialize.
+ +

+ + + + + + + +
 
+class LabelHelp
   A class to add help to a widget.
 
 Methods defined here:
+
__init__(self, fileNameHelp, master, name, widget)
Add menu to the widget.
+ +
displayPopupMenu(self, event=None)
Display the popup menu when the button is right clicked.
+ +
unpostPopupMenu(self, event=None)
Unpost the popup menu.
+ +

+ + + + + + + +
 
+class LabelSeparator
   A class to add a label and menu separator.
 
 Methods defined here:
+
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
addToMenu(self, repositoryMenu)
Add this to the repository menu.
+ +
addToMenuFrameable(self, repositoryMenu)
Add this to the frameable repository menu.
+ +
getFromRepository(self, repository)
Initialize.
+ +

+ + + + + + + +
 
+class LatentStringVar
   A class to provide a StringVar when needed.
 
 Methods defined here:
+
__init__(self)
Set the string var.
+ +
getString(self)
Get the string.
+ +
getVar(self)
Get the string var.
+ +
setString(self, word)
Set the string.
+ +

+ + + + + + + +
 
+class LayerCount
   A class to handle the layerIndex.
 
 Methods defined here:
+
__init__(self)
Initialize.
+ +
__repr__(self)
Get the string representation of this LayerCount.
+ +
printProgressIncrement(self, procedureName)
Print progress then increment layerIndex.
+ +

+ + + + + + + +
 
+class MenuButtonDisplay
   A class to add a menu button.
 
 Methods defined here:
+
addRadiosToDialog(self, gridPosition)
Add the menu radios to the dialog.
+ +
addToMenu(self, repositoryMenu)
Add this to the repository menu.
+ +
addToMenuFrameable(self, repositoryMenu)
Add this to the frameable repository menu.
+ +
getFromName(self, name, repository)
Initialize.
+ +
removeMenus(self)
Remove all menus.
+ +
setRadioVarToName(self, name)
Get the menu button.
+ +
setToNameAddToDialog(self, name, gridPosition)
Get the menu button.
+ +

+ + + + + + + +
 
+class MenuRadio(BooleanSetting)
   A class to display, read & write a boolean with associated menu radio button.
 
 
Method resolution order:
+
MenuRadio
+
BooleanSetting
+
StringSetting
+
+
+Methods defined here:
+
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
addToMenu(self, repositoryMenu)
Add this to the submenu set by MenuButtonDisplay, the repository menu is ignored
+ +
addToMenuFrameable(self, repositoryMenu)
Add this to the frameable repository menu.
+ +
addToSubmenu(self)
Add this to the submenu.
+ +
clickRadio(self)
Workaround for Tkinter bug, invoke and set the value when clicked.
+ +
getFromMenuButtonDisplay(self, menuButtonDisplay, name, repository, value)
Initialize.
+ +
invoke(self)
Workaround for Tkinter bug, invoke to set the value when changed.
+ +
setStateToValue(self)
Set the checkbutton to the boolean.
+ +
setToDisplay(self)
Set the boolean to the checkbutton.
+ +
+Methods inherited from BooleanSetting:
+
setValueToString(self, valueString)
Set the boolean to the string.
+ +
toggleCheckbutton(self)
Workaround for Tkinter bug, toggle the value.
+ +
toggleMenuCheckbutton(self)
Workaround for Tkinter bug, toggle the value.
+ +
+Methods inherited from StringSetting:
+
__init__(self)
Set the update function to none.
+ +
__repr__(self)
Get the string representation of this StringSetting.
+ +
addToWindow(self)
Add this to the repository frame list.
+ +
bindEntry(self)
Bind the entry to the update function.
+ +
createEntry(self, root)
Create the entry.
+ +
getFromValue(self, name, repository, value)
Initialize.
+ +
getFromValueOnly(self, name, repository, value)
Initialize.
+ +
getFromValueOnlyAddToRepository(self, name, repository, value)
Initialize.
+ +
removeFromWindow(self)
Remove this from the repository frame list.
+ +
setUpdateFunction(self, updateFunction)
Set the update function.
+ +
setValueToSplitLine(self, lineIndex, lines, splitLine)
Set the value to the second word of a split line.
+ +
updateSaveListeners(self)
Update save listeners if any.
+ +
writeToRepositoryWriter(self, repositoryWriter)
Write tab separated name and value to the repository writer.
+ +

+ + + + + + + +
 
+class PluginFrame
   A class to display the plugins in a frame.
 
 Methods defined here:
+
__init__(self)
Initialize.
+ +
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
createFrame(self, gridPosition)
Create the frame.
+ +
focusSetMaster(self, gridPosition)
Set the focus to the plugin master.
+ +
getFromPath(self, defaultRadioButton, directoryPath, repository)
Initialize.
+ +
setStateToValue(self)
Set the state of all the plugins to the value.
+ +
setToDisplay(self)
Set the plugins to the display.
+ +
update(self)
Update the frame.
+ +
updateSaveListeners(self)
Update save listeners if any.
+ +
writeToRepositoryWriter(self, repositoryWriter)
Write tab separated name and value to the repository writer.
+ +

+ + + + + + + +
 
+class PluginGroupFrame(PluginFrame)
   A class to display the plugin groups in a frame.
 
 Methods defined here:
+
createFrame(self, gridPosition)
Create the frame.
+ +
focusSetMaster(self, gridPosition)
Set the focus to the plugin master.
+ +
+Methods inherited from PluginFrame:
+
__init__(self)
Initialize.
+ +
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
getFromPath(self, defaultRadioButton, directoryPath, repository)
Initialize.
+ +
setStateToValue(self)
Set the state of all the plugins to the value.
+ +
setToDisplay(self)
Set the plugins to the display.
+ +
update(self)
Update the frame.
+ +
updateSaveListeners(self)
Update save listeners if any.
+ +
writeToRepositoryWriter(self, repositoryWriter)
Write tab separated name and value to the repository writer.
+ +

+ + + + + + + +
 
+class Radio(BooleanSetting)
   A class to display, read & write a boolean with associated radio button.
 
 
Method resolution order:
+
Radio
+
BooleanSetting
+
StringSetting
+
+
+Methods defined here:
+
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
clickRadio(self)
Workaround for Tkinter bug, set the value.
+ +
createRadioButton(self, gridPosition)
Create the radio button.
+ +
getFromRadio(self, latentStringVar, name, repository, value)
Initialize.
+ +
setSelect(self)
Set the int var and select the radio button.
+ +
setStateToValue(self)
Set the checkbutton to the boolean.
+ +
setToDisplay(self)
Set the boolean to the checkbutton.
+ +
+Methods inherited from BooleanSetting:
+
addToMenu(self, repositoryMenu)
Add this to the repository menu.
+ +
addToMenuFrameable(self, repositoryMenu)
Add this to the frameable repository menu.
+ +
setValueToString(self, valueString)
Set the boolean to the string.
+ +
toggleCheckbutton(self)
Workaround for Tkinter bug, toggle the value.
+ +
toggleMenuCheckbutton(self)
Workaround for Tkinter bug, toggle the value.
+ +
+Methods inherited from StringSetting:
+
__init__(self)
Set the update function to none.
+ +
__repr__(self)
Get the string representation of this StringSetting.
+ +
addToWindow(self)
Add this to the repository frame list.
+ +
bindEntry(self)
Bind the entry to the update function.
+ +
createEntry(self, root)
Create the entry.
+ +
getFromValue(self, name, repository, value)
Initialize.
+ +
getFromValueOnly(self, name, repository, value)
Initialize.
+ +
getFromValueOnlyAddToRepository(self, name, repository, value)
Initialize.
+ +
removeFromWindow(self)
Remove this from the repository frame list.
+ +
setUpdateFunction(self, updateFunction)
Set the update function.
+ +
setValueToSplitLine(self, lineIndex, lines, splitLine)
Set the value to the second word of a split line.
+ +
updateSaveListeners(self)
Update save listeners if any.
+ +
writeToRepositoryWriter(self, repositoryWriter)
Write tab separated name and value to the repository writer.
+ +

+ + + + + + + +
 
+class RadioCapitalized(Radio)
   A class to display, read & write a boolean with associated radio button.
 
 
Method resolution order:
+
RadioCapitalized
+
Radio
+
BooleanSetting
+
StringSetting
+
+
+Methods defined here:
+
createRadioButton(self, gridPosition)
Create the radio button.
+ +
+Methods inherited from Radio:
+
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
clickRadio(self)
Workaround for Tkinter bug, set the value.
+ +
getFromRadio(self, latentStringVar, name, repository, value)
Initialize.
+ +
setSelect(self)
Set the int var and select the radio button.
+ +
setStateToValue(self)
Set the checkbutton to the boolean.
+ +
setToDisplay(self)
Set the boolean to the checkbutton.
+ +
+Methods inherited from BooleanSetting:
+
addToMenu(self, repositoryMenu)
Add this to the repository menu.
+ +
addToMenuFrameable(self, repositoryMenu)
Add this to the frameable repository menu.
+ +
setValueToString(self, valueString)
Set the boolean to the string.
+ +
toggleCheckbutton(self)
Workaround for Tkinter bug, toggle the value.
+ +
toggleMenuCheckbutton(self)
Workaround for Tkinter bug, toggle the value.
+ +
+Methods inherited from StringSetting:
+
__init__(self)
Set the update function to none.
+ +
__repr__(self)
Get the string representation of this StringSetting.
+ +
addToWindow(self)
Add this to the repository frame list.
+ +
bindEntry(self)
Bind the entry to the update function.
+ +
createEntry(self, root)
Create the entry.
+ +
getFromValue(self, name, repository, value)
Initialize.
+ +
getFromValueOnly(self, name, repository, value)
Initialize.
+ +
getFromValueOnlyAddToRepository(self, name, repository, value)
Initialize.
+ +
removeFromWindow(self)
Remove this from the repository frame list.
+ +
setUpdateFunction(self, updateFunction)
Set the update function.
+ +
setValueToSplitLine(self, lineIndex, lines, splitLine)
Set the value to the second word of a split line.
+ +
updateSaveListeners(self)
Update save listeners if any.
+ +
writeToRepositoryWriter(self, repositoryWriter)
Write tab separated name and value to the repository writer.
+ +

+ + + + + + + +
 
+class RadioCapitalizedButton(Radio)
   A class to display, read & write a boolean with associated radio button.
 
 
Method resolution order:
+
RadioCapitalizedButton
+
Radio
+
BooleanSetting
+
StringSetting
+
+
+Methods defined here:
+
createRadioButton(self, gridPosition)
Create the radio button.
+ +
displayDialog(self)
Display function.
+ +
getFromPath(self, latentStringVar, name, path, repository, value)
Initialize.
+ +
+Methods inherited from Radio:
+
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
clickRadio(self)
Workaround for Tkinter bug, set the value.
+ +
getFromRadio(self, latentStringVar, name, repository, value)
Initialize.
+ +
setSelect(self)
Set the int var and select the radio button.
+ +
setStateToValue(self)
Set the checkbutton to the boolean.
+ +
setToDisplay(self)
Set the boolean to the checkbutton.
+ +
+Methods inherited from BooleanSetting:
+
addToMenu(self, repositoryMenu)
Add this to the repository menu.
+ +
addToMenuFrameable(self, repositoryMenu)
Add this to the frameable repository menu.
+ +
setValueToString(self, valueString)
Set the boolean to the string.
+ +
toggleCheckbutton(self)
Workaround for Tkinter bug, toggle the value.
+ +
toggleMenuCheckbutton(self)
Workaround for Tkinter bug, toggle the value.
+ +
+Methods inherited from StringSetting:
+
__init__(self)
Set the update function to none.
+ +
__repr__(self)
Get the string representation of this StringSetting.
+ +
addToWindow(self)
Add this to the repository frame list.
+ +
bindEntry(self)
Bind the entry to the update function.
+ +
createEntry(self, root)
Create the entry.
+ +
getFromValue(self, name, repository, value)
Initialize.
+ +
getFromValueOnly(self, name, repository, value)
Initialize.
+ +
getFromValueOnlyAddToRepository(self, name, repository, value)
Initialize.
+ +
removeFromWindow(self)
Remove this from the repository frame list.
+ +
setUpdateFunction(self, updateFunction)
Set the update function.
+ +
setValueToSplitLine(self, lineIndex, lines, splitLine)
Set the value to the second word of a split line.
+ +
updateSaveListeners(self)
Update save listeners if any.
+ +
writeToRepositoryWriter(self, repositoryWriter)
Write tab separated name and value to the repository writer.
+ +

+ + + + + + + +
 
+class RadioPlugin(RadioCapitalized)
   A class to display, read & write a boolean with associated radio button.
 
 
Method resolution order:
+
RadioPlugin
+
RadioCapitalized
+
Radio
+
BooleanSetting
+
StringSetting
+
+
+Methods defined here:
+
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
getFromRadio(self, important, latentStringVar, name, repository, value)
Initialize.
+ +
incrementGridPosition(self, gridPosition)
Increment the grid position.
+ +
+Methods inherited from RadioCapitalized:
+
createRadioButton(self, gridPosition)
Create the radio button.
+ +
+Methods inherited from Radio:
+
clickRadio(self)
Workaround for Tkinter bug, set the value.
+ +
setSelect(self)
Set the int var and select the radio button.
+ +
setStateToValue(self)
Set the checkbutton to the boolean.
+ +
setToDisplay(self)
Set the boolean to the checkbutton.
+ +
+Methods inherited from BooleanSetting:
+
addToMenu(self, repositoryMenu)
Add this to the repository menu.
+ +
addToMenuFrameable(self, repositoryMenu)
Add this to the frameable repository menu.
+ +
setValueToString(self, valueString)
Set the boolean to the string.
+ +
toggleCheckbutton(self)
Workaround for Tkinter bug, toggle the value.
+ +
toggleMenuCheckbutton(self)
Workaround for Tkinter bug, toggle the value.
+ +
+Methods inherited from StringSetting:
+
__init__(self)
Set the update function to none.
+ +
__repr__(self)
Get the string representation of this StringSetting.
+ +
addToWindow(self)
Add this to the repository frame list.
+ +
bindEntry(self)
Bind the entry to the update function.
+ +
createEntry(self, root)
Create the entry.
+ +
getFromValue(self, name, repository, value)
Initialize.
+ +
getFromValueOnly(self, name, repository, value)
Initialize.
+ +
getFromValueOnlyAddToRepository(self, name, repository, value)
Initialize.
+ +
removeFromWindow(self)
Remove this from the repository frame list.
+ +
setUpdateFunction(self, updateFunction)
Set the update function.
+ +
setValueToSplitLine(self, lineIndex, lines, splitLine)
Set the value to the second word of a split line.
+ +
updateSaveListeners(self)
Update save listeners if any.
+ +
writeToRepositoryWriter(self, repositoryWriter)
Write tab separated name and value to the repository writer.
+ +

+ + + + + +
 
+class RepositoryDialog
    Methods defined here:
+
__init__(self, repository, root)
Add entities to the dialog.
+ +
__repr__(self)
Get the string representation of this RepositoryDialog.
+ +
addButtons(self, repository, root)
Add buttons to the dialog.
+ +
cancel(self, event=None)
Set all entities to their saved state.
+ +
close(self, event=None)
The dialog was closed.
+ +
save(self, event=None)
Set the entities to the dialog then write them.
+ +
setWindowPositionDeiconify(self)
Set the window position if that setting exists.
+ +

+ + + + + + + +
 
+class StringSetting
   A class to display, read & write a string.
 
 Methods defined here:
+
__init__(self)
Set the update function to none.
+ +
__repr__(self)
Get the string representation of this StringSetting.
+ +
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
addToMenu(self, repositoryMenu)
Do nothing because this should only be added to a frameable repository menu.
+ +
addToMenuFrameable(self, repositoryMenu)
Add this to the frameable repository menu.
+ +
addToWindow(self)
Add this to the repository frame list.
+ +
bindEntry(self)
Bind the entry to the update function.
+ +
createEntry(self, root)
Create the entry.
+ +
getFromValue(self, name, repository, value)
Initialize.
+ +
getFromValueOnly(self, name, repository, value)
Initialize.
+ +
getFromValueOnlyAddToRepository(self, name, repository, value)
Initialize.
+ +
removeFromWindow(self)
Remove this from the repository frame list.
+ +
setStateToValue(self)
Set the entry to the value.
+ +
setToDisplay(self)
Set the string to the entry field.
+ +
setUpdateFunction(self, updateFunction)
Set the update function.
+ +
setValueToSplitLine(self, lineIndex, lines, splitLine)
Set the value to the second word of a split line.
+ +
setValueToString(self, valueString)
Set the value to the value string.
+ +
updateSaveListeners(self)
Update save listeners if any.
+ +
writeToRepositoryWriter(self, repositoryWriter)
Write tab separated name and value to the repository writer.
+ +

+ + + + + + + +
 
+class TextSetting(StringSetting)
   A class to display, read & write a text.
 
 Methods defined here:
+
__init__(self)
Set the update function to none.
+ +
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
getFromValue(self, name, repository, value)
Initialize.
+ +
setStateToValue(self)
Set the entry to the value.
+ +
setToDisplay(self)
Set the string to the entry field.
+ +
setValueToSplitLine(self, lineIndex, lines, splitLine)
Set the value to the second word of a split line.
+ +
writeToRepositoryWriter(self, repositoryWriter)
Write tab separated name and value to the repository writer.
+ +
+Methods inherited from StringSetting:
+
__repr__(self)
Get the string representation of this StringSetting.
+ +
addToMenu(self, repositoryMenu)
Do nothing because this should only be added to a frameable repository menu.
+ +
addToMenuFrameable(self, repositoryMenu)
Add this to the frameable repository menu.
+ +
addToWindow(self)
Add this to the repository frame list.
+ +
bindEntry(self)
Bind the entry to the update function.
+ +
createEntry(self, root)
Create the entry.
+ +
getFromValueOnly(self, name, repository, value)
Initialize.
+ +
getFromValueOnlyAddToRepository(self, name, repository, value)
Initialize.
+ +
removeFromWindow(self)
Remove this from the repository frame list.
+ +
setUpdateFunction(self, updateFunction)
Set the update function.
+ +
setValueToString(self, valueString)
Set the value to the value string.
+ +
updateSaveListeners(self)
Update save listeners if any.
+ +

+ + + + + + + +
 
+class TokenConversion
   A class to convert tokens in a string.
 
 Methods defined here:
+
__init__(self, name='replaceToken', token='___replaced___')
Set the name and token.
+ +
getNamedString(self, text)
Get a string with the tokens changed to names.
+ +
getTokenizedString(self, text)
Get a string with the names changed to tokens.
+ +

+ + + + + + + +
 
+class ToolDialog
   A class to display the tool repository dialog.
 
 Methods defined here:
+
addPluginToMenu(self, menu, path)
Add the display command to the menu.
+ +
display(self)
Display the tool repository dialog.
+ +
getFromPath(self, path)
Initialize and return display function.
+ +

+ + + + + + + +
 
+class WindowPosition(StringSetting)
   A class to display, read & write a window position.
 
 Methods defined here:
+
addToDialog(self, gridPosition)
Set the root to later get the geometry.
+ +
getFromValue(self, repository, value)
Initialize.
+ +
setToDisplay(self)
Set the string to the window position.
+ +
setWindowPosition(self)
Set the window position.
+ +
+Methods inherited from StringSetting:
+
__init__(self)
Set the update function to none.
+ +
__repr__(self)
Get the string representation of this StringSetting.
+ +
addToMenu(self, repositoryMenu)
Do nothing because this should only be added to a frameable repository menu.
+ +
addToMenuFrameable(self, repositoryMenu)
Add this to the frameable repository menu.
+ +
addToWindow(self)
Add this to the repository frame list.
+ +
bindEntry(self)
Bind the entry to the update function.
+ +
createEntry(self, root)
Create the entry.
+ +
getFromValueOnly(self, name, repository, value)
Initialize.
+ +
getFromValueOnlyAddToRepository(self, name, repository, value)
Initialize.
+ +
removeFromWindow(self)
Remove this from the repository frame list.
+ +
setStateToValue(self)
Set the entry to the value.
+ +
setUpdateFunction(self, updateFunction)
Set the update function.
+ +
setValueToSplitLine(self, lineIndex, lines, splitLine)
Set the value to the second word of a split line.
+ +
setValueToString(self, valueString)
Set the value to the value string.
+ +
updateSaveListeners(self)
Update save listeners if any.
+ +
writeToRepositoryWriter(self, repositoryWriter)
Write tab separated name and value to the repository writer.
+ +

+ + + + + +
 
+Functions
       
addAcceleratorCommand(acceleratorBinding, commandFunction, master, menu, text)
Add accelerator command.
+
addEmptyRow(gridPosition)
Add an empty row.
+
addListsToRepository(fileNameHelp, repository)
Add the value to the lists.
+
addListsToRepositoryByFunction(fileNameHelp, getProfileDirectory, repository)
Add the value to the lists.
+
addMenuEntitiesToMenu(menu, menuEntities)
Add the menu entities to the menu.
+
addMenuEntitiesToMenuFrameable(menu, menuEntities)
Add the menu entities to the menu.
+
addPluginsParentToMenu(directoryPath, menu, parentPath, pluginFileNames)
Add plugins and the parent to the menu.
+
addPluginsToMenu(directoryPath, menu, pluginFileNames)
Add plugins to the menu.
+
cancelRepository(repository)
Read the repository then set all the entities to the read repository values.
+
deleteDirectory(directory, subfolderName)
Delete the directory if it exists.
+
deleteMenuItems(menu)
Delete the menu items.
+
getAlongWayHexadecimalColor(beginBrightness, colorWidth, difference, endColorTuple, wayLength)
Get a color along the way from begin brightness to the end color.
+
getAlongWayHexadecimalPrimary(beginBrightness, beginRatio, colorWidth, endBrightness, endRatio)
Get a primary color along the way from grey to the end color.
+
getAlterationFile(fileName)
Get the file from the fileName or the lowercase fileName in the alterations directories.
+
getAlterationFileLine(fileName)
Get the alteration file line from the fileName.
+
getAlterationFileLineBlindly(fileName)
Get the alteration file line from the fileName.
+
getAlterationFileLines(fileName)
Get the alteration file line and the text lines from the fileName in the alterations directories.
+
getAlterationLines(fileName)
Get the text lines from the fileName in the alterations directories.
+
getDisplayToolButtonsRepository(directoryPath, importantFileNames, names, repository)
Get the display tool buttons.
+
getDisplayedDialogFromConstructor(repository)
Display the repository dialog.
+
getDisplayedDialogFromPath(path)
Display the repository dialog.
+
getEachWordCapitalized(name)
Get the capitalized name.
+
getFileInGivenDirectory(directory, fileName)
Get the file from the fileName or the lowercase fileName in the given directory.
+
getFileTextGivenDirectoryFileName(directory, fileName)
Get the entire text of a file with the given file name in the given directory.
+
getFolders(directory)
Get the folder list in a directory.
+
getGlobalRepositoryDialogValues()
Get the global repository dialog values.
+
getPathInFabmetheusFromFileNameHelp(fileNameHelp)
Get the directory path from file name help.
+
getProfileBaseName(repository)
Get the profile base file name.
+
getProfileName(name, repository)
Get the name, joined with the profile directory if there is one.
+
getProfilesDirectoryInAboveDirectory(subName='')
Get the profiles directory path in the above directory.
+
getRadioPluginsAddPluginFrame(directoryPath, importantFileNames, names, repository)
Get the radio plugins and add the plugin frame.
+
getReadRepository(repository)
Read and return settings from a file.
+
getRepositoryText(repository)
Get the text representation of the repository.
+
getRepositoryWriter(title)
Get the repository writer for the title.
+
getSelectedPluginModuleFromPath(filePath, plugins)
Get the selected plugin module.
+
getSelectedPluginName(plugins)
Get the selected plugin name.
+
getSelectedRadioPlugin(names, radioPlugins)
Get the selected radio button if it exists, None otherwise.
+
getShortestUniqueSettingName(settingName, settings)
Get the shortest unique name in the settings.
+
getSubfolderWithBasename(basename, directory)
Get the subfolder in the directory with the basename.
+
getTitleFromName(title)
Get the title of this setting.
+
getUntilFirstBracket(text)
Get the text until the first bracket, if any.
+
getWidthHex(number, width)
Get the first width hexadecimal digits.
+
liftRepositoryDialogs(repositoryDialogs)
Lift the repository dialogs.
+
openSVGPage(fileName, svgViewer)
Open svg page with an svg program.
+
openWebPage(webPagePath)
Open a web page in a browser.
+
printProgress(layerIndex, procedureName)
Print layerIndex followed by a carriage return.
+
printProgressByNumber(layerIndex, numberOfLayers, procedureName)
Print layerIndex and numberOfLayers followed by a carriage return.
+
printProgressByString(progressString)
Print progress string.
+
quitWindow(root)
Quit a window.
+
quitWindows(event=None)
Quit all windows.
+
readSettingsFromText(repository, text)
Read settings from a text.
+
saveAll()
Save all the dialogs.
+
saveRepository(repository)
Set the entities to the dialog then write them.
+
setButtonFontWeightString(button, isBold)
Set button font weight given isBold.
+
setEntryText(entry, value)
Set the entry text.
+
setIntegerValueToString(integerSetting, valueString)
Set the integer to the string.
+
setRepositoryToLine(lineIndex, lines, shortDictionary)
Set setting dictionary to a setting line.globalSettingReplacements
+
setSpinColor(setting)
Set the spin box color to the value, yellow if it is lower than the default and blue if it is higher.
+
startMainLoopFromConstructor(repository)
Display the repository dialog and start the main loop.
+
startMainLoopFromWindow(window)
Display the tableau window and start the main loop.
+
temporaryAddPreferenceOverride(module, name, value)
+
temporaryApplyOverrides(repository)
Apply any overrides that have been set at the command line.
+
writeSettings(repository)
Write the settings to a file.
+
writeSettingsPrintMessage(repository)
Set the settings to the dialog then write them.
+
writeValueListToRepositoryWriter(repositoryWriter, setting)
Write tab separated name and list to the repository writer.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__date__ = '$Date: 2008/23/04 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalCloseListTables = [{}, {}]
+globalProfileSaveListenerListTable = {}
+globalRepositoryDialogListTable = {}
+globalSettingReplacements = {'Layer Thickness (mm):': 'Layer Height (mm):', 'Location Arrival X (mm):': 'Arrival X (mm):', 'Location Arrival Y (mm):': 'Arrival Y (mm):', 'Location Arrival Z (mm):': 'Arrival Z (mm):', 'Location Departure X (mm):': 'Departure X (mm):', 'Location Departure Y (mm):': 'Departure Y (mm):', 'Location Departure Z (mm):': 'Departure Z (mm):', 'Location Wipe X (mm):': 'Wipe X (mm):', 'Location Wipe Y (mm):': 'Wipe Y (mm):', 'Location Wipe Z (mm):': 'Wipe Z (mm):', ...}
+globalSpreadsheetSeparator = '\t'
+globalTemporaryOverrides = {}

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.svg_reader.html b/SkeinPyPy/documentation/fabmetheus_utilities.svg_reader.html new file mode 100644 index 0000000..a5c1620 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.svg_reader.html @@ -0,0 +1,279 @@ + + +Python: module fabmetheus_utilities.svg_reader + + + + +
 
+ 
fabmetheus_utilities.svg_reader ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/svg_reader.py
+

Svg reader.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+math
+
os
+fabmetheus_utilities.settings
+fabmetheus_utilities.svg_writer
+
sys
+traceback
+fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
FontReader +
Glyph +
MatrixSVG +
PathReader +
SVGReader +
+

+ + + + + + + +
 
+class FontReader
   Class to read a font in the fonts folder.
 
 Methods defined here:
+
__init__(self, fontFamily)
Initialize.
+ +
getGlyph(self, character, yAxisPointingUpward)
Get the glyph for the character.
+ +

+ + + + + + + +
 
+class Glyph
   Class to handle a glyph.
 
 Methods defined here:
+
__init__(self, elementNode, unitsPerEM, yAxisPointingUpward)
Initialize.
+ +
getSizedAdvancedLoops(self, fontSize, horizontalAdvanceX, yAxisPointingUpward=True)
Get loops for font size, advanced horizontally.
+ +

+ + + + + + + +
 
+class MatrixSVG
   Two by three svg matrix.
 
 Methods defined here:
+
__init__(self, tricomplex=None)
Initialize.
+ +
__repr__(self)
Get the string representation of this two by three svg matrix.
+ +
getOtherTimesSelf(self, otherTricomplex)
Get the other matrix multiplied by this matrix.
+ +
getSelfTimesOther(self, otherTricomplex)
Get this matrix multiplied by the other matrix.
+ +
getTransformedPath(self, path)
Get transformed path.
+ +
getTransformedPaths(self, paths)
Get transformed paths.
+ +

+ + + + + + + +
 
+class PathReader
   Class to read svg path.
 
 Methods defined here:
+
__init__(self, elementNode, loops, yAxisPointingUpward)
Add to path string to loops.
+ +
addPathArc(self, end)
Add an arc to the path.
+ +
addPathCubic(self, controlPoints, end)
Add a cubic curve to the path.
+ +
addPathCubicReflected(self, controlPoint, end)
Add a cubic curve to the path from a reflected control point.
+ +
addPathLine(self, lineFunction, point)
Add a line to the path.
+ +
addPathLineAxis(self, point)
Add an axis line to the path.
+ +
addPathLineByFunction(self, lineFunction)
Add a line to the path by line function.
+ +
addPathMove(self, lineFunction, point)
Add an axis line to the path.
+ +
addPathQuadratic(self, controlPoint, end)
Add a quadratic curve to the path.
+ +
addPathQuadraticReflected(self, end)
Add a quadratic curve to the path from a reflected control point.
+ +
getComplexByExtraIndex(self, extraIndex=0)
Get complex from the extraIndex.
+ +
getComplexRelative(self)
Get relative complex.
+ +
getFloatByExtraIndex(self, extraIndex=0)
Get float from the extraIndex.
+ +
getOldPoint(self)
Get the old point.
+ +
processPathWordA(self)
Process path word A.
+ +
processPathWordC(self)
Process path word C.
+ +
processPathWordH(self)
Process path word H.
+ +
processPathWordL(self)
Process path word L.
+ +
processPathWordM(self)
Process path word M.
+ +
processPathWordQ(self)
Process path word Q.
+ +
processPathWordS(self)
Process path word S.
+ +
processPathWordT(self)
Process path word T.
+ +
processPathWordV(self)
Process path word V.
+ +
processPathWordZ(self)
Process path word Z.
+ +
processPathWorda(self)
Process path word a.
+ +
processPathWordc(self)
Process path word C.
+ +
processPathWordh(self)
Process path word h.
+ +
processPathWordl(self)
Process path word l.
+ +
processPathWordm(self)
Process path word m.
+ +
processPathWordq(self)
Process path word q.
+ +
processPathWords(self)
Process path word s.
+ +
processPathWordt(self)
Process path word t.
+ +
processPathWordv(self)
Process path word v.
+ +
processPathWordz(self)
Process path word z.
+ +

+ + + + + + + +
 
+class SVGReader
   An svg carving.
 
 Methods defined here:
+
__init__(self)
Add empty lists.
+ +
flipDirectLayer(self, loopLayer)
Flip the y coordinate of the layer and direct the loops.
+ +
getLoopLayer(self)
Return the rotated loop layer.
+ +
parseSVG(self, fileName, svgText)
Parse SVG text and store the layers.
+ +
parseSVGByElementNode(self, elementNode)
Parse SVG by elementNode.
+ +
processElementNode(self, elementNode)
Process the xml element.
+ +

+ + + + + +
 
+Functions
       
addFunctionsToDictionary(dictionary, functions, prefix)
Add functions to dictionary.
+
getArcComplexes(begin, end, largeArcFlag, radius, sweepFlag, xAxisRotation)
Get the arc complexes, procedure at http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
+
getChainMatrixSVG(elementNode, matrixSVG)
Get chain matrixSVG by svgElement.
+
getChainMatrixSVGIfNecessary(elementNode, yAxisPointingUpward)
Get chain matrixSVG by svgElement and yAxisPointingUpward.
+
getCubicPoint(along, begin, controlPoints, end)
Get the cubic point.
+
getCubicPoints(begin, controlPoints, end, numberOfBezierPoints=22)
Get the cubic points.
+
getFontReader(fontFamily)
Get the font reader for the fontFamily.
+
getFontsDirectoryPath()
Get the fonts directory path.
+
getLabelString(dictionary)
Get the label string for the dictionary.
+
getMatrixSVG(elementNode)
Get matrixSVG by svgElement.
+
getQuadraticPoint(along, begin, controlPoint, end)
Get the quadratic point.
+
getQuadraticPoints(begin, controlPoint, end, numberOfBezierPoints=22)
Get the quadratic points.
+
getRightStripAlphabetPercent(word)
Get word with alphabet characters and the percent sign stripped from the right.
+
getRightStripMinusSplit(lineString)
Get string with spaces after the minus sign stripped.
+
getStrokeRadius(elementNode)
Get the stroke radius.
+
getStyleValue(defaultValue, elementNode, key)
Get the stroke value string.
+
getTextComplexLoops(fontFamily, fontSize, text, yAxisPointingUpward=True)
Get text as complex loops.
+
getTransformedFillOutline(elementNode, loop, yAxisPointingUpward)
Get the loops if fill is on, otherwise get the outlines.
+
getTransformedOutlineByPath(elementNode, path, yAxisPointingUpward)
Get the outline from the path.
+
getTransformedOutlineByPaths(elementNode, paths, yAxisPointingUpward)
Get the outline from the paths.
+
getTricomplexTimesColumn(firstTricomplex, otherColumn)
Get this matrix multiplied by the otherColumn.
+
getTricomplexTimesOther(firstTricomplex, otherTricomplex)
Get the first tricomplex multiplied by the other tricomplex.
+
getTricomplexmatrix(transformWords)
Get matrixSVG by transformWords.
+
getTricomplexrotate(transformWords)
Get matrixSVG by transformWords.
+
getTricomplexscale(transformWords)
Get matrixSVG by transformWords.
+
getTricomplexskewX(transformWords)
Get matrixSVG by transformWords.
+
getTricomplexskewY(transformWords)
Get matrixSVG by transformWords.
+
getTricomplextranslate(transformWords)
Get matrixSVG by transformWords.
+
processSVGElementcircle(elementNode, svgReader)
Process elementNode by svgReader.
+
processSVGElementellipse(elementNode, svgReader)
Process elementNode by svgReader.
+
processSVGElementg(elementNode, svgReader)
Process elementNode by svgReader.
+
processSVGElementline(elementNode, svgReader)
Process elementNode by svgReader.
+
processSVGElementpath(elementNode, svgReader)
Process elementNode by svgReader.
+
processSVGElementpolygon(elementNode, svgReader)
Process elementNode by svgReader.
+
processSVGElementpolyline(elementNode, svgReader)
Process elementNode by svgReader.
+
processSVGElementrect(elementNode, svgReader)
Process elementNode by svgReader.
+
processSVGElementtext(elementNode, svgReader)
Process elementNode by svgReader.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalFontFileNames = None
+globalFontReaderDictionary = {}
+globalGetTricomplexDictionary = {'matrix': <function getTricomplexmatrix>, 'rotate': <function getTricomplexrotate>, 'scale': <function getTricomplexscale>, 'skewX': <function getTricomplexskewX>, 'skewY': <function getTricomplexskewY>, 'translate': <function getTricomplextranslate>}
+globalGetTricomplexFunctions = [<function getTricomplexmatrix>, <function getTricomplexrotate>, <function getTricomplexscale>, <function getTricomplexskewX>, <function getTricomplexskewY>, <function getTricomplextranslate>]
+globalNumberOfBezierPoints = 22
+globalNumberOfCirclePoints = 44
+globalNumberOfCornerPoints = 11
+globalProcessPathWordDictionary = {'A': <unbound method PathReader.processPathWordA>, 'C': <unbound method PathReader.processPathWordC>, 'H': <unbound method PathReader.processPathWordH>, 'L': <unbound method PathReader.processPathWordL>, 'M': <unbound method PathReader.processPathWordM>, 'Q': <unbound method PathReader.processPathWordQ>, 'S': <unbound method PathReader.processPathWordS>, 'T': <unbound method PathReader.processPathWordT>, 'V': <unbound method PathReader.processPathWordV>, 'Z': <unbound method PathReader.processPathWordZ>, ...}
+globalProcessPathWordFunctions = [<unbound method PathReader.processPathWordA>, <unbound method PathReader.processPathWorda>, <unbound method PathReader.processPathWordC>, <unbound method PathReader.processPathWordc>, <unbound method PathReader.processPathWordH>, <unbound method PathReader.processPathWordh>, <unbound method PathReader.processPathWordL>, <unbound method PathReader.processPathWordl>, <unbound method PathReader.processPathWordM>, <unbound method PathReader.processPathWordm>, <unbound method PathReader.processPathWordQ>, <unbound method PathReader.processPathWordq>, <unbound method PathReader.processPathWordS>, <unbound method PathReader.processPathWords>, <unbound method PathReader.processPathWordT>, <unbound method PathReader.processPathWordt>, <unbound method PathReader.processPathWordV>, <unbound method PathReader.processPathWordv>, <unbound method PathReader.processPathWordZ>, <unbound method PathReader.processPathWordz>]
+globalProcessSVGElementDictionary = {'circle': <function processSVGElementcircle>, 'ellipse': <function processSVGElementellipse>, 'g': <function processSVGElementg>, 'line': <function processSVGElementline>, 'path': <function processSVGElementpath>, 'polygon': <function processSVGElementpolygon>, 'polyline': <function processSVGElementpolyline>, 'rect': <function processSVGElementrect>, 'text': <function processSVGElementtext>}
+globalProcessSVGElementFunctions = [<function processSVGElementcircle>, <function processSVGElementellipse>, <function processSVGElementg>, <function processSVGElementline>, <function processSVGElementpath>, <function processSVGElementpolygon>, <function processSVGElementpolyline>, <function processSVGElementrect>, <function processSVGElementtext>]
+globalSideAngle = 0.14279966607226333

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://hydraraptor.blogspot.com/>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.svg_writer.html b/SkeinPyPy/documentation/fabmetheus_utilities.svg_writer.html new file mode 100644 index 0000000..82ae0a8 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.svg_writer.html @@ -0,0 +1,115 @@ + + +Python: module fabmetheus_utilities.svg_writer + + + + +
 
+ 
fabmetheus_utilities.svg_writer ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/svg_writer.py
+

Svg_writer is a class and collection of utilities to read from and write to an svg file.
+
+Svg_writer uses the layer_template.svg file in the templates folder in the same folder as svg_writer, to output an svg file.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+cStringIO
+
fabmetheus_utilities.euclidean
+fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+
math
+os
+fabmetheus_utilities.xml_simple_reader
+
fabmetheus_utilities.xml_simple_writer
+

+ + + + + +
 
+Classes
       
+
SVGWriter +
+

+ + + + + + + +
 
+class SVGWriter
   A base class to get an svg skein from a carving.
 
 Methods defined here:
+
__init__(self, addLayerTemplateToSVG, cornerMaximum, cornerMinimum, decimalPlacesCarried, layerHeight, edgeWidth=None)
Initialize.
+ +
addLayerBegin(self, layerIndex, loopLayer)
Add the start lines for the layer.
+ +
addLoopLayerToOutput(self, layerIndex, loopLayer)
Add rotated boundary layer to the output.
+ +
addLoopLayersToOutput(self, loopLayers)
Add rotated boundary layers to the output.
+ +
addOriginalAsComment(self, elementNode)
Add original elementNode as a comment.
+ +
getReplacedSVGTemplate(self, fileName, loopLayers, procedureName, elementNode=None)
Get the lines of text from the layer_template.svg file.
+ +
getRounded(self, number)
Get number rounded to the number of carried decimal places as a string.
+ +
getRoundedComplexString(self, point)
Get the rounded complex string.
+ +
getSVGStringForLoop(self, loop)
Get the svg loop string.
+ +
getSVGStringForLoops(self, loops)
Get the svg loops string.
+ +
getSVGStringForPath(self, path)
Get the svg path string.
+ +
getTransformString(self)
Get the svg transform string.
+ +
setDimensionTexts(self, key, valueString)
Set the texts to the valueString followed by mm.
+ +
setMetadataNoscriptElement(self, key, prefix, value)
Set the metadata value and the text.
+ +
setTexts(self, key, valueString)
Set the texts to the valueString.
+ +

+ + + + + +
 
+Functions
       
getCarving(fileName)
Get a carving for the file using an import plugin.
+
getCommentElement(elementNode)
Get a carving for the file using an import plugin.
+
getSVGByLoopLayers(addLayerTemplateToSVG, carving, loopLayers)
Get the svg text.
+
getSliceDictionary(elementNode)
Get the metadata slice attribute dictionary.
+
getSliceElementNodes(elementNode)
Get the slice elements.
+
getTruncatedRotatedBoundaryLayers(loopLayers, repository)
Get the truncated rotated boundary layers.
+
setSVGCarvingCorners(cornerMaximum, cornerMinimum, layerHeight, loopLayers)
Parse SVG text and store the layers.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__date__ = '$Date: 2008/02/05 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalOriginalTextString = '<!-- Original XML Text:\n'

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.vector3.html b/SkeinPyPy/documentation/fabmetheus_utilities.vector3.html new file mode 100644 index 0000000..6bbdee0 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.vector3.html @@ -0,0 +1,179 @@ + + +Python: module fabmetheus_utilities.vector3 + + + + +
 
+ 
fabmetheus_utilities.vector3 ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/vector3.py
+

Vector3 is a three dimensional vector class.
+
+Below are examples of Vector3 use.
+
+>>> from vector3 import Vector3
+>>> origin = Vector3()
+>>> origin
+0.0, 0.0, 0.0
+>>> pythagoras = Vector3( 3, 4, 0 )
+>>> pythagoras
+3.0, 4.0, 0.0
+>>> pythagoras.magnitude()
+5.0
+>>> pythagoras.magnitudeSquared()
+25
+>>> triplePythagoras = pythagoras * 3.0
+>>> triplePythagoras
+9.0, 12.0, 0.0
+>>> plane = pythagoras.dropAxis()
+>>> plane
+(3+4j)

+

+ + + + + +
 
+Modules
       
__init__
+
math
+
operator
+
fabmetheus_utilities.xml_simple_writer
+

+ + + + + +
 
+Classes
       
+
Vector3 +
+

+ + + + + + + +
 
+class Vector3
   A three dimensional vector class.
 
 Methods defined here:
+
__abs__(self)
Get the magnitude of the Vector3.
+ +
__add__(self, other)
Get the sum of this Vector3 and other one.
+ +
__copy__(self)
Get the copy of this Vector3.
+ +
__div__(self, other)
Get a new Vector3 by dividing each component of this one.
+ +
__eq__(self, other)
Determine whether this vector is identical to other one.
+ +
__floordiv__(self, other)
Get a new Vector3 by floor dividing each component of this one.
+ +
__hash__(self)
Determine whether this vector is identical to other one.
+ +
__iadd__(self, other)
Add other Vector3 to this one.
+ +
__idiv__(self, other)
Divide each component of this Vector3.
+ +
__ifloordiv__(self, other)
Floor divide each component of this Vector3.
+ +
__imul__(self, other)
Multiply each component of this Vector3.
+ +
__init__(self, x=0.0, y=0.0, z=0.0)
+ +
__isub__(self, other)
Subtract other Vector3 from this one.
+ +
__itruediv__(self, other)
True divide each component of this Vector3.
+ +
__mul__(self, other)
Get a new Vector3 by multiplying each component of this one.
+ +
__ne__(self, other)
Determine whether this vector is not identical to other one.
+ +
__neg__(self)
+ +
__nonzero__(self)
+ +
__pos__ = __copy__(self)
+ +
__rdiv__(self, other)
Get a new Vector3 by dividing each component of this one.
+ +
__repr__(self)
Get the string representation of this Vector3.
+ +
__rfloordiv__(self, other)
Get a new Vector3 by floor dividing each component of this one.
+ +
__rmul__(self, other)
Get a new Vector3 by multiplying each component of this one.
+ +
__rtruediv__(self, other)
Get a new Vector3 by true dividing each component of this one.
+ +
__sub__(self, other)
Get the difference between the Vector3 and other one.
+ +
__truediv__(self, other)
Get a new Vector3 by true dividing each component of this one.
+ +
copy = __copy__(self)
+ +
cross(self, other)
Calculate the cross product of this vector with other one.
+ +
distance(self, other)
Get the Euclidean distance between this vector and other one.
+ +
distanceSquared(self, other)
Get the square of the Euclidean distance between this vector and other one.
+ +
dot(self, other)
Calculate the dot product of this vector with other one.
+ +
dropAxis(self, which=2)
Get a complex by removing one axis of the vector3.
+ +
getFloatList(self)
Get the vector as a list of floats.
+ +
getIsDefault(self)
Determine if this is the zero vector.
+ +
getNormalized(self)
Get the normalized Vector3.
+ +
magnitude = __abs__(self)
+ +
magnitudeSquared(self)
Get the square of the magnitude of the Vector3.
+ +
maximize(self, other)
Maximize the Vector3.
+ +
minimize(self, other)
Minimize the Vector3.
+ +
normalize(self)
Scale each component of this Vector3 so that it has a magnitude of 1. If this Vector3 has a magnitude of 0, this method has no effect.
+ +
reflect(self, normal)
Reflect the Vector3 across the normal, which is assumed to be normalized.
+ +
setToVector3(self, other)
Set this Vector3 to be identical to other one.
+ +
setToXYZ(self, x, y, z)
Set the x, y, and z components of this Vector3.
+ +

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__credits__ = 'Nophead <http://forums.reprap.org/profile.php?12,28>\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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalGetAccessibleAttributeSet = ['x', 'y', 'z']
+globalSetAccessibleAttributeSet = ['x', 'y', 'z']

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://forums.reprap.org/profile.php?12,28>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.vector3index.html b/SkeinPyPy/documentation/fabmetheus_utilities.vector3index.html new file mode 100644 index 0000000..c8fef3b --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.vector3index.html @@ -0,0 +1,179 @@ + + +Python: module fabmetheus_utilities.vector3index + + + + +
 
+ 
fabmetheus_utilities.vector3index ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/vector3index.py
+

Vector3 is a three dimensional vector class.
+
+Below are examples of Vector3 use.
+
+>>> from vector3 import Vector3
+>>> origin = Vector3()
+>>> origin
+0.0, 0.0, 0.0
+>>> pythagoras = Vector3( 3, 4, 0 )
+>>> pythagoras
+3.0, 4.0, 0.0
+>>> pythagoras.magnitude()
+5.0
+>>> pythagoras.magnitudeSquared()
+25
+>>> triplePythagoras = pythagoras * 3.0
+>>> triplePythagoras
+9.0, 12.0, 0.0
+>>> plane = pythagoras.dropAxis()
+>>> plane
+(3+4j)

+

+ + + + + +
 
+Modules
       
__init__
+
math
+
operator
+
fabmetheus_utilities.xml_simple_writer
+

+ + + + + +
 
+Classes
       
+
Vector3Index +
+

+ + + + + + + +
 
+class Vector3Index
   A three dimensional vector index class.
 
 Methods defined here:
+
__abs__(self)
Get the magnitude of the Vector3.
+ +
__add__(self, other)
Get the sum of this Vector3 and other one.
+ +
__copy__(self)
Get the copy of this Vector3.
+ +
__div__(self, other)
Get a new Vector3 by dividing each component of this one.
+ +
__eq__(self, other)
Determine whether this vector is identical to other one.
+ +
__floordiv__(self, other)
Get a new Vector3 by floor dividing each component of this one.
+ +
__hash__(self)
Determine whether this vector is identical to other one.
+ +
__iadd__(self, other)
Add other Vector3 to this one.
+ +
__idiv__(self, other)
Divide each component of this Vector3.
+ +
__ifloordiv__(self, other)
Floor divide each component of this Vector3.
+ +
__imul__(self, other)
Multiply each component of this Vector3.
+ +
__init__(self, index, x=0.0, y=0.0, z=0.0)
+ +
__isub__(self, other)
Subtract other Vector3 from this one.
+ +
__itruediv__(self, other)
True divide each component of this Vector3.
+ +
__mul__(self, other)
Get a new Vector3 by multiplying each component of this one.
+ +
__ne__(self, other)
Determine whether this vector is not identical to other one.
+ +
__neg__(self)
+ +
__nonzero__(self)
+ +
__pos__ = __copy__(self)
+ +
__rdiv__(self, other)
Get a new Vector3 by dividing each component of this one.
+ +
__repr__(self)
Get the string representation of this Vector3 index.
+ +
__rfloordiv__(self, other)
Get a new Vector3 by floor dividing each component of this one.
+ +
__rmul__(self, other)
Get a new Vector3 by multiplying each component of this one.
+ +
__rtruediv__(self, other)
Get a new Vector3 by true dividing each component of this one.
+ +
__sub__(self, other)
Get the difference between the Vector3 and other one.
+ +
__truediv__(self, other)
Get a new Vector3 by true dividing each component of this one.
+ +
copy = __copy__(self)
+ +
cross(self, other)
Calculate the cross product of this vector with other one.
+ +
distance(self, other)
Get the Euclidean distance between this vector and other one.
+ +
distanceSquared(self, other)
Get the square of the Euclidean distance between this vector and other one.
+ +
dot(self, other)
Calculate the dot product of this vector with other one.
+ +
dropAxis(self, which=2)
Get a complex by removing one axis of the vector3.
+ +
getFloatList(self)
Get the vector as a list of floats.
+ +
getIsDefault(self)
Determine if this is the zero vector.
+ +
getNormalized(self)
Get the normalized Vector3.
+ +
magnitude = __abs__(self)
+ +
magnitudeSquared(self)
Get the square of the magnitude of the Vector3.
+ +
maximize(self, other)
Maximize the Vector3.
+ +
minimize(self, other)
Minimize the Vector3.
+ +
normalize(self)
Scale each component of this Vector3 so that it has a magnitude of 1. If this Vector3 has a magnitude of 0, this method has no effect.
+ +
reflect(self, normal)
Reflect the Vector3 across the normal, which is assumed to be normalized.
+ +
setToVector3(self, other)
Set this Vector3 to be identical to other one.
+ +
setToXYZ(self, x, y, z)
Set the x, y, and z components of this Vector3.
+ +

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__credits__ = 'Nophead <http://forums.reprap.org/profile.php?12,28>\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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalGetAccessibleAttributeSet = ['x', 'y', 'z']
+globalSetAccessibleAttributeSet = ['x', 'y', 'z']

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://forums.reprap.org/profile.php?12,28>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.xml_simple_reader.html b/SkeinPyPy/documentation/fabmetheus_utilities.xml_simple_reader.html new file mode 100644 index 0000000..52bea85 --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.xml_simple_reader.html @@ -0,0 +1,711 @@ + + +Python: module fabmetheus_utilities.xml_simple_reader + + + + +
 
+ 
fabmetheus_utilities.xml_simple_reader ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/xml_simple_reader.py
+

The xml_simple_reader.py script is an xml parser that can parse a line separated xml text.
+
+This xml parser will read a line seperated xml text and produce a tree of the xml with a document element. Each element can have an attribute table, childNodes, a class name, parentNode, text and a link to the document element.
+
+This example gets an xml tree for the xml file boolean.xml. This example is run in a terminal in the folder which contains boolean.xml and xml_simple_reader.py.
+
+
+> python
+Python 2.5.1 (r251:54863, Sep 22 2007, 01:43:31)
+[GCC 4.2.1 (SUSE Linux)] on linux2
+Type "help", "copyright", "credits" or "license" for more information.
+>>> fileName = 'boolean.xml'
+>>> file = open(fileName, 'r')
+>>> xmlText = file.read()
+>>> file.close()
+>>> from xml_simple_reader import DocumentNode
+>>> xmlParser = DocumentNode(fileName, xmlText)
+>>> print(xmlParser)
+ ?xml, {'version': '1.0'}
+ ArtOfIllusion, {'xmlns:bf': '//babelfiche/codec', 'version': '2.0', 'fileversion': '3'}
+ Scene, {'bf:id': 'theScene'}
+ materials, {'bf:elem-type': 'java.lang.Object', 'bf:list': 'collection', 'bf:id': '1', 'bf:type': 'java.util.Vector'}
+..
+many more lines of the xml tree
+..

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+
cStringIO
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.geometry.geometry_utilities.evaluate
+fabmetheus_utilities.geometry.geometry_utilities.matrix
+
fabmetheus_utilities.xml_simple_writer
+

+ + + + + +
 
+Classes
       
+
CDATASectionMonad +
+
+
CommentMonad +
DocumentTypeMonad +
+
+
CDATASectionNode +
+
+
CommentNode +
DocumentTypeNode +
TextNode +
+
+
DocumentNode +
ElementEndMonad +
+
+
OpenChooseMonad +
OpenMonad +
+
+
ElementLocalNameMonad +
ElementNode +
ElementReadMonad +
KeyMonad +
TextMonad +
ValueMonad +
+

+ + + + + + + +
 
+class CDATASectionMonad
   A monad to handle a CDATASection node.
 
 Methods defined here:
+
__init__(self, input, parentNode)
Initialize.
+ +
getNextMonad(self, character)
Get the next monad.
+ +

+ + + + + + + +
 
+class CDATASectionNode
   A CDATASection node.
 
 Methods defined here:
+
__init__(self, parentNode, textContent='')
Initialize.
+ +
__repr__(self)
Get the string representation of this CDATASection node.
+ +
addToIdentifierDictionaries(self)
Add the element to the owner document identifier dictionaries.
+ +
addXML(self, depth, output)
Add xml for this CDATASection node.
+ +
appendSelfToParent(self)
Append self to the parentNode.
+ +
copyXMLChildNodes(self, idSuffix, parentNode)
Copy the xml childNodes.
+ +
getAttributes(self)
Get the attributes.
+ +
getChildNodes(self)
Get the empty set.
+ +
getCopy(self, idSuffix, parentNode)
Copy the xml element, set its dictionary and add it to the parentNode.
+ +
getCopyShallow(self, attributes=None)
Copy the node and set its parentNode.
+ +
getNodeName(self)
Get the node name.
+ +
getNodeType(self)
Get the node type.
+ +
getOwnerDocument(self)
Get the owner document.
+ +
getTextContent(self)
Get the text content.
+ +
removeChildNodesFromIDNameParent(self)
Remove the childNodes from the id and name dictionaries and the childNodes.
+ +
removeFromIDNameParent(self)
Remove this from the id and name dictionaries and the childNodes of the parentNode.
+ +
setParentAddToChildNodes(self, parentNode)
Set the parentNode and add this to its childNodes.
+ +
+Data descriptors defined here:
+
attributes
+
Get the attributes.
+
+
childNodes
+
Get the empty set.
+
+
nodeName
+
Get the node name.
+
+
nodeType
+
Get the node type.
+
+
ownerDocument
+
Get the owner document.
+
+

+ + + + + + + +
 
+class CommentMonad(CDATASectionMonad)
   A monad to handle a comment node.
 
 Methods defined here:
+
getNextMonad(self, character)
Get the next monad.
+ +
+Methods inherited from CDATASectionMonad:
+
__init__(self, input, parentNode)
Initialize.
+ +

+ + + + + + + +
 
+class CommentNode(CDATASectionNode)
   A comment node.
 
 Methods defined here:
+
getCopyShallow(self, attributes=None)
Copy the node and set its parentNode.
+ +
getNodeName(self)
Get the node name.
+ +
getNodeType(self)
Get the node type.
+ +
+Data descriptors defined here:
+
nodeName
+
Get the node name.
+
+
nodeType
+
Get the node type.
+
+
+Methods inherited from CDATASectionNode:
+
__init__(self, parentNode, textContent='')
Initialize.
+ +
__repr__(self)
Get the string representation of this CDATASection node.
+ +
addToIdentifierDictionaries(self)
Add the element to the owner document identifier dictionaries.
+ +
addXML(self, depth, output)
Add xml for this CDATASection node.
+ +
appendSelfToParent(self)
Append self to the parentNode.
+ +
copyXMLChildNodes(self, idSuffix, parentNode)
Copy the xml childNodes.
+ +
getAttributes(self)
Get the attributes.
+ +
getChildNodes(self)
Get the empty set.
+ +
getCopy(self, idSuffix, parentNode)
Copy the xml element, set its dictionary and add it to the parentNode.
+ +
getOwnerDocument(self)
Get the owner document.
+ +
getTextContent(self)
Get the text content.
+ +
removeChildNodesFromIDNameParent(self)
Remove the childNodes from the id and name dictionaries and the childNodes.
+ +
removeFromIDNameParent(self)
Remove this from the id and name dictionaries and the childNodes of the parentNode.
+ +
setParentAddToChildNodes(self, parentNode)
Set the parentNode and add this to its childNodes.
+ +
+Data descriptors inherited from CDATASectionNode:
+
attributes
+
Get the attributes.
+
+
childNodes
+
Get the empty set.
+
+
ownerDocument
+
Get the owner document.
+
+

+ + + + + + + +
 
+class DocumentNode
   A class to parse an xml text and store the elements.
 
 Methods defined here:
+
__init__(self, fileName, xmlText)
Initialize.
+ +
__repr__(self)
Get the string representation of this xml document.
+ +
appendChild(self, elementNode)
Append child elementNode to the child nodes.
+ +
getAttributes(self)
Get the attributes.
+ +
getCascadeBoolean(self, defaultBoolean, key)
Get the cascade boolean.
+ +
getCascadeFloat(self, defaultFloat, key)
Get the cascade float.
+ +
getDocumentElement(self)
Get the document element.
+ +
getElementsByLocalName(self, localName)
Get the descendents which have the given local name.
+ +
getImportNameChain(self, suffix='')
Get the import name chain with the suffix at the end.
+ +
getNodeName(self)
Get the node name.
+ +
getNodeType(self)
Get the node type.
+ +
getOriginalRoot(self)
Get the original reparsed document element.
+ +
getOwnerDocument(self)
Get the owner document.
+ +
+Data descriptors defined here:
+
attributes
+
Get the attributes.
+
+
documentElement
+
Get the document element.
+
+
nodeName
+
Get the node name.
+
+
nodeType
+
Get the node type.
+
+
ownerDocument
+
Get the owner document.
+
+

+ + + + + + + +
 
+class DocumentTypeMonad(CDATASectionMonad)
   A monad to handle a document type node.
 
 Methods defined here:
+
getNextMonad(self, character)
Get the next monad.
+ +
+Methods inherited from CDATASectionMonad:
+
__init__(self, input, parentNode)
Initialize.
+ +

+ + + + + + + +
 
+class DocumentTypeNode(CDATASectionNode)
   A document type node.
 
 Methods defined here:
+
getCopyShallow(self, attributes=None)
Copy the node and set its parentNode.
+ +
getNodeName(self)
Get the node name.
+ +
getNodeType(self)
Get the node type.
+ +
+Data descriptors defined here:
+
nodeName
+
Get the node name.
+
+
nodeType
+
Get the node type.
+
+
+Methods inherited from CDATASectionNode:
+
__init__(self, parentNode, textContent='')
Initialize.
+ +
__repr__(self)
Get the string representation of this CDATASection node.
+ +
addToIdentifierDictionaries(self)
Add the element to the owner document identifier dictionaries.
+ +
addXML(self, depth, output)
Add xml for this CDATASection node.
+ +
appendSelfToParent(self)
Append self to the parentNode.
+ +
copyXMLChildNodes(self, idSuffix, parentNode)
Copy the xml childNodes.
+ +
getAttributes(self)
Get the attributes.
+ +
getChildNodes(self)
Get the empty set.
+ +
getCopy(self, idSuffix, parentNode)
Copy the xml element, set its dictionary and add it to the parentNode.
+ +
getOwnerDocument(self)
Get the owner document.
+ +
getTextContent(self)
Get the text content.
+ +
removeChildNodesFromIDNameParent(self)
Remove the childNodes from the id and name dictionaries and the childNodes.
+ +
removeFromIDNameParent(self)
Remove this from the id and name dictionaries and the childNodes of the parentNode.
+ +
setParentAddToChildNodes(self, parentNode)
Set the parentNode and add this to its childNodes.
+ +
+Data descriptors inherited from CDATASectionNode:
+
attributes
+
Get the attributes.
+
+
childNodes
+
Get the empty set.
+
+
ownerDocument
+
Get the owner document.
+
+

+ + + + + + + +
 
+class ElementEndMonad
   A monad to look for the end of an ElementNode tag.
 
 Methods defined here:
+
__init__(self, parentNode)
Initialize.
+ +
getNextMonad(self, character)
Get the next monad.
+ +

+ + + + + + + +
 
+class ElementLocalNameMonad
   A monad to set the local name of an ElementNode.
 
 Methods defined here:
+
__init__(self, character, parentNode)
Initialize.
+ +
getNextMonad(self, character)
Get the next monad.
+ +
setLocalName(self)
Set the class name.
+ +

+ + + + + + + +
 
+class ElementNode
   An xml element.
 
 Methods defined here:
+
__init__(self, parentNode=None)
Initialize.
+ +
__repr__(self)
Get the string representation of this xml document.
+ +
addSuffixToID(self, idSuffix)
Add the suffix to the id.
+ +
addToIdentifierDictionaries(self)
Add the element to the owner document identifier dictionaries.
+ +
addXML(self, depth, output)
Add xml for this elementNode.
+ +
appendChild(self, elementNode)
Append child elementNode to the child nodes.
+ +
appendSelfToParent(self)
Append self to the parentNode.
+ +
copyXMLChildNodes(self, idSuffix, parentNode)
Copy the xml childNodes.
+ +
getCascadeBoolean(self, defaultBoolean, key)
Get the cascade boolean.
+ +
getCascadeFloat(self, defaultFloat, key)
Get the cascade float.
+ +
getChildElementsByLocalName(self, localName)
Get the childNodes which have the given local name.
+ +
getCopy(self, idSuffix, parentNode)
Copy the xml element, set its dictionary and add it to the parentNode.
+ +
getCopyShallow(self, attributes=None)
Copy the xml element and set its dictionary and parentNode.
+ +
getDocumentElement(self)
Get the document element.
+ +
getElementNodeByID(self, idKey)
Get the xml element by id.
+ +
getElementNodesByName(self, nameKey)
Get the xml elements by name.
+ +
getElementNodesByTag(self, tagKey)
Get the xml elements by tag.
+ +
getElementsByLocalName(self, localName)
Get the descendents which have the given local name.
+ +
getFirstChildByLocalName(self, localName)
Get the first childNode which has the given class name.
+ +
getIDSuffix(self, elementIndex=None)
Get the id suffix from the dictionary.
+ +
getImportNameChain(self, suffix='')
Get the import name chain with the suffix at the end.
+ +
getNodeName(self)
Get the node name.
+ +
getNodeType(self)
Get the node type.
+ +
getOwnerDocument(self)
Get the owner document.
+ +
getParser(self)
Get the parser.
+ +
getPaths(self)
Get all paths.
+ +
getPreviousElementNode(self)
Get previous ElementNode if it exists.
+ +
getPreviousVertex(self, defaultVector3=None)
Get previous vertex if it exists.
+ +
getStrippedAttributesValue(self, keyString)
Get the stripped attribute value if the length is at least one, otherwise return None.
+ +
getSubChildWithID(self, idReference)
Get the childNode which has the idReference.
+ +
getTagKeys(self)
Get stripped tag keys.
+ +
getTextContent(self)
Get the text from the child nodes.
+ +
getValueByKey(self, key)
Get value by the key.
+ +
getVertexes(self)
Get the vertexes.
+ +
getXMLProcessor(self)
Get the xmlProcessor.
+ +
linkObject(self, xmlObject)
Link self to xmlObject and add xmlObject to archivableObjects.
+ +
printAllVariables(self)
Print all variables.
+ +
printAllVariablesRoot(self)
Print all variables and the document element variables.
+ +
removeChildNodesFromIDNameParent(self)
Remove the childNodes from the id and name dictionaries and the childNodes.
+ +
removeFromIDNameParent(self)
Remove this from the id and name dictionaries and the childNodes of the parentNode.
+ +
setParentAddToChildNodes(self, parentNode)
Set the parentNode and add this to its childNodes.
+ +
setTextContent(self, textContent='')
Get the text from the child nodes.
+ +
+Data descriptors defined here:
+
nodeName
+
Get the node name.
+
+
nodeType
+
Get the node type.
+
+
ownerDocument
+
Get the owner document.
+
+
textContent
+
Get the text from the child nodes.
+
+

+ + + + + + + +
 
+class ElementReadMonad
   A monad to read the attributes of the ElementNode tag.
 
 Methods defined here:
+
__init__(self, elementNode)
Initialize.
+ +
getNextMonad(self, character)
Get the next monad.
+ +

+ + + + + + + +
 
+class KeyMonad
   A monad to set the key of an attribute of an ElementNode.
 
 Methods defined here:
+
__init__(self, character, elementNode)
Initialize.
+ +
getNextMonad(self, character)
Get the next monad.
+ +

+ + + + + + + +
 
+class OpenChooseMonad(ElementEndMonad)
   A monad to choose the next monad.
 
 Methods defined here:
+
getNextMonad(self, character)
Get the next monad.
+ +
+Methods inherited from ElementEndMonad:
+
__init__(self, parentNode)
Initialize.
+ +

+ + + + + + + +
 
+class OpenMonad(ElementEndMonad)
   A monad to handle the open tag character.
 
 Methods defined here:
+
getNextMonad(self, character)
Get the next monad.
+ +
+Methods inherited from ElementEndMonad:
+
__init__(self, parentNode)
Initialize.
+ +

+ + + + + + + +
 
+class TextMonad
   A monad to handle the open tag character and set the text.
 
 Methods defined here:
+
__init__(self, parentNode)
Initialize.
+ +
getNextMonad(self, character)
Get the next monad.
+ +

+ + + + + + + +
 
+class TextNode(CDATASectionNode)
   A text node.
 
 Methods defined here:
+
addXML(self, depth, output)
Add xml for this text node.
+ +
getCopyShallow(self, attributes=None)
Copy the node and set its parentNode.
+ +
getNodeName(self)
Get the node name.
+ +
getNodeType(self)
Get the node type.
+ +
+Data descriptors defined here:
+
nodeName
+
Get the node name.
+
+
nodeType
+
Get the node type.
+
+
+Methods inherited from CDATASectionNode:
+
__init__(self, parentNode, textContent='')
Initialize.
+ +
__repr__(self)
Get the string representation of this CDATASection node.
+ +
addToIdentifierDictionaries(self)
Add the element to the owner document identifier dictionaries.
+ +
appendSelfToParent(self)
Append self to the parentNode.
+ +
copyXMLChildNodes(self, idSuffix, parentNode)
Copy the xml childNodes.
+ +
getAttributes(self)
Get the attributes.
+ +
getChildNodes(self)
Get the empty set.
+ +
getCopy(self, idSuffix, parentNode)
Copy the xml element, set its dictionary and add it to the parentNode.
+ +
getOwnerDocument(self)
Get the owner document.
+ +
getTextContent(self)
Get the text content.
+ +
removeChildNodesFromIDNameParent(self)
Remove the childNodes from the id and name dictionaries and the childNodes.
+ +
removeFromIDNameParent(self)
Remove this from the id and name dictionaries and the childNodes of the parentNode.
+ +
setParentAddToChildNodes(self, parentNode)
Set the parentNode and add this to its childNodes.
+ +
+Data descriptors inherited from CDATASectionNode:
+
attributes
+
Get the attributes.
+
+
childNodes
+
Get the empty set.
+
+
ownerDocument
+
Get the owner document.
+
+

+ + + + + + + +
 
+class ValueMonad
   A monad to set the value of an attribute of an ElementNode.
 
 Methods defined here:
+
__init__(self, elementNode, key)
Initialize.
+ +
getNextMonad(self, character)
Get the next monad.
+ +

+ + + + + +
 
+Functions
       
createAppendByText(parentNode, xmlText)
Create and append the child nodes from the xmlText.
+
createAppendByTextb(parentNode, xmlText)
Create and append the child nodes from the xmlText.
+
getChildElementsByLocalName(childNodes, localName)
Get the childNodes which have the given local name.
+
getDocumentNode(fileName)
Get the document from the file name.
+
getElementsByLocalName(childNodes, localName)
Get the descendents which have the given local name.
+
getFileText(fileName, printWarning=True, readMode='r')
Get the entire text of a file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalGetAccessibleAttributeSet = set(['getPaths', 'getPreviousElementNode', 'getPreviousVertex', 'getVertexes', 'parentNode'])

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://hydraraptor.blogspot.com/>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/fabmetheus_utilities.xml_simple_writer.html b/SkeinPyPy/documentation/fabmetheus_utilities.xml_simple_writer.html new file mode 100644 index 0000000..78561aa --- /dev/null +++ b/SkeinPyPy/documentation/fabmetheus_utilities.xml_simple_writer.html @@ -0,0 +1,70 @@ + + +Python: module fabmetheus_utilities.xml_simple_writer + + + + +
 
+ 
fabmetheus_utilities.xml_simple_writer ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/fabmetheus_utilities/xml_simple_writer.py
+

XML tag writer utilities.

+

+ + + + + +
 
+Modules
       
__init__
+
cStringIO
+

+ + + + + +
 
+Functions
       
addBeginEndInnerXMLTag(attributes, depth, innerText, localName, output, text='')
Add the begin and end xml tag and the inner text if any.
+
addBeginXMLTag(attributes, depth, localName, output, text='')
Add the begin xml tag.
+
addClosedXMLTag(attributes, depth, localName, output, text='')
Add the closed xml tag.
+
addEndXMLTag(depth, localName, output)
Add the end xml tag.
+
addXMLFromLoopComplexZ(attributes, depth, loop, output, z)
Add xml from loop.
+
addXMLFromObjects(depth, objects, output)
Add xml from objects.
+
addXMLFromVertexes(depth, output, vertexes)
Add xml from loop.
+
addXMLFromXYZ(depth, index, output, x, y, z)
Add xml from x, y & z.
+
compareAttributeKeyAscending(key, otherKey)
Get comparison in order to sort attribute keys in ascending order, with the id key first and name second.
+
getAttributesString(attributes)
Add the closed xml tag.
+
getBeginGeometryXMLOutput(elementNode=None)
Get the beginning of the string representation of this boolean geometry object info.
+
getBeginXMLOutput()
Get the beginning of the string representation of this object info.
+
getDictionaryWithoutList(dictionary, withoutList)
Get the dictionary without the keys in the list.
+
getEndGeometryXMLString(output)
Get the string representation of this object info.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://hydraraptor.blogspot.com/>
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.html b/SkeinPyPy/documentation/skeinforge_application.html new file mode 100644 index 0000000..b8816ee --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.html @@ -0,0 +1,33 @@ + + +Python: package skeinforge_application + + + + +
 
+ 
skeinforge_application
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
skeinforge
+
skeinforge_plugins (package)
+
skeinforge_utilities (package)
+

+ + + + + +
 
+Data
       level = 1
+numberOfLevelsDeepInPackageHierarchy = 1
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge.html new file mode 100644 index 0000000..c873e4c --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge.html @@ -0,0 +1,364 @@ + + +Python: module skeinforge_application.skeinforge + + + + +
 
+ 
skeinforge_application.skeinforge ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge.py
+

+Previous / Next / Contents +

+


+
+Overview
+  Introduction
+  Command Line Interface
+  Contribute
+  Documentation
+  Fabrication
+  File Formats
+  Getting Skeinforge
+  Getting Started
+  License
+  Motto
+  Troubleshooting
+Examples
+
+

Overview

+
+ +

Introduction

+ +Skeinforge is a GPL tool chain to forge a gcode skein for a model.
+
+The tool chain starts with carve, which carves the model into layers, then the layers are modified by other tools in turn like fill, comb, tower, raft, stretch, hop, wipe, fillet & export. Each tool automatically gets the gcode from the previous tool. So if you want a carved & filled gcode, call the fill tool and it will call carve, then it will fill and output the gcode. If you want to use all the tools, call export and it will call in turn all the other tools down the chain to produce the gcode file.
+
+If you do not want a tool after preface to modify the output, deselect the Activate checkbox for that tool. When the Activate checkbox is off, the tool will just hand off the gcode to the next tool without modifying it.
+
+The skeinforge module provides a single place to call up all the setting dialogs. When the 'Skeinforge' button is clicked, skeinforge calls export, since that is the end of the chain.
+
+The plugin buttons which are commonly used are bolded and the ones which are rarely used have normal font weight.
+
+There are also tools which handle settings for the chain, like polyfile.
+
+The analyze tool calls plugins in the analyze_plugins folder, which will analyze the gcode in some way when it is generated if their Activate checkbox is selected.
+
+The interpret tool accesses and displays the import plugins.
+
+The default settings are similar to those on Nophead's machine. A setting which is often different is the 'Layer Height' in carve.
+
+

Command Line Interface

+ +To bring up the skeinforge dialog without a file name, type:
+python skeinforge_application/skeinforge.py
+
+Slicing a file from skeinforge_utilities/skeinforge_craft.py, for example:
+python skeinforge_application/skeinforge_utilities/skeinforge_craft.py test.stl
+
+will slice the file and exit. This is the correct option for programs which use skeinforge to only generate a gcode file.
+
+Slicing a file from skeinforge.py, for example:
+python skeinforge_application/skeinforge.py test.stl
+
+will slice the file and bring up the skeinforge window and the analyze windows and then skeinforge will wait for user input.
+
+Slicing a file from skeinforge_plugins/craft.py, for example:
+python skeinforge_application/skeinforge_plugins/craft.py test.stl
+
+will slice the file and bring up the analyze windows only and then skeinforge will wait for user input.
+
+

Contribute

+ +You can contribute by helping develop the manual at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge
+
+There is also a forum thread about how to contribute to skeinforge development at:
+http://dev.forums.reprap.org/read.php?12,27562
+
+I will only reply to emails from contributors or to complete bug reports.
+
+

Documentation

+ +There is a manual at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge
+
+There is also documentation is in the documentation folder, in the doc strings for each module and it can be called from the '?' button or the menu or by clicking F1 in each setting dialog.
+
+A list of other tutorials is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge#Tutorials
+
+Skeinforge tagged pages on thingiverse can be searched for at:
+http://www.thingiverse.com/search?cx=015525747728168968820%3Arqnsgx1xxcw&cof=FORID%3A9&ie=UTF-8&q=skeinforge&sa=Search&siteurl=www.thingiverse.com%2F#944
+
+

Fabrication

+ +To fabricate a model with gcode and the Arduino you can use the send.py in the fabricate folder. The documentation for it is in the folder as send.html and at:
+http://reprap.org/bin/view/Main/ArduinoSend
+
+Another way is to use an EMC2 or similar computer controlled milling machine, as described in the "ECM2 based repstrap" forum thread at:
+http://forums.reprap.org/read.php?1,12143
+
+using the M-Apps package, which is at:
+http://forums.reprap.org/file.php?1,file=772
+
+Another way is to use Zach's ReplicatorG at:
+http://replicat.org/
+
+There is also an older Processing script at:
+http://reprap.svn.sourceforge.net/viewvc/reprap/trunk/users/hoeken/arduino/GCode_Host/
+
+Yet another way is to use the reprap host, written in Java, to load and print gcode:
+http://dev.www.reprap.org/bin/view/Main/DriverSoftware#Load_GCode
+
+For jogging, the Metalab group wrote their own exerciser, also in Processing:
+http://reprap.svn.sourceforge.net/viewvc/reprap/trunk/users/metalab/processing/GCode_Exerciser/
+
+The Metalab group has descriptions of skeinforge in action and their adventures are described at:
+http://reprap.soup.io/
+
+There is a board about printing issues at:
+http://www.bitsfrombytes.com/fora/user/index.php?board=5.0
+
+You can buy the Rapman (an improved Darwin) from Bits from Bytes at:
+http://www.bitsfrombytes.com/
+
+You can buy the Makerbot from Makerbot Industries at:
+http://www.makerbot.com/
+
+

File Formats

+ +An explanation of the gcodes is at:
+http://reprap.org/bin/view/Main/Arduino_GCode_Interpreter
+
+and at:
+http://reprap.org/bin/view/Main/MCodeReference
+
+A gode example is at:
+http://forums.reprap.org/file.php?12,file=565
+
+The settings are saved as tab separated .csv files in the .skeinforge folder in your home directory. The settings can be set in the tool dialogs. The .csv files can also be edited with a text editor or a spreadsheet program set to separate tabs.
+
+The Scalable Vector Graphics file produced by vectorwrite can be opened by an SVG viewer or an SVG capable browser like Mozilla:
+http://www.mozilla.com/firefox/
+
+A good triangle surface format is the GNU Triangulated Surface format, which is supported by Mesh Viewer and described at:
+http://gts.sourceforge.net/reference/gts-surfaces.html#GTS-SURFACE-WRITE
+
+You can export GTS files from Art of Illusion with the Export GNU Triangulated Surface.bsh script in the Art of Illusion Scripts folder.
+
+STL is an inferior triangle surface format, described at:
+http://en.wikipedia.org/wiki/STL_(file_format)
+
+If you're using an STL file and you can't even carve it, try converting it to a GNU Triangulated Surface file in Art of Illusion. If it still doesn't carve, then follow the advice in the troubleshooting section.
+
+

Getting Skeinforge

+ +The latest version is at:
+http://members.axion.net/~enrique/reprap_python_beanshell.zip
+
+a sometimes out of date version is in the last reprap_python_beanshell.zip attachment in the last post of the Fabmetheus blog at:
+http://fabmetheus.blogspot.com/
+
+another sometimes out of date version is at:
+https://reprap.svn.sourceforge.net/svnroot/reprap/trunk/reprap/miscellaneous/python-beanshell-scripts/
+
+

Getting Started

+ +For skeinforge to run, install python 2.x on your machine, which is available from:
+http://www.python.org/download/
+
+To use the settings dialog you'll also need Tkinter, which probably came with the python installation. If it did not, look for it at:
+http://www.tcl.tk/software/tcltk/
+
+If you want python and Tkinter together on MacOS, you can try:
+http://www.astro.washington.edu/users/rowen/ROPackage/Overview.html
+
+If you want python and Tkinter together on all platforms and don't mind filling out forms, you can try the ActivePython package from Active State at:
+http://www.activestate.com/Products/activepython/feature_list.mhtml
+
+The computation intensive python modules will use psyco if it is available and run about twice as fast. Psyco is described at:
+http://psyco.sourceforge.net/index.html
+
+The psyco download page is:
+http://psyco.sourceforge.net/download.html
+
+Skeinforge imports Stereolithography (.stl) files or GNU Triangulated Surface (.gts) files. If importing an STL file directly doesn't work, an indirect way to import an STL file is by turning it into a GTS file is by using the Export GNU Triangulated Surface script at:
+http://members.axion.net/~enrique/Export%20GNU%20Triangulated%20Surface.bsh
+
+The Export GNU Triangulated Surface script is also in the Art of Illusion folder, which is in the same folder as skeinforge.py. To bring the script into Art of Illusion, drop it into the folder ArtOfIllusion/Scripts/Tools/. Then import the STL file using the STL import plugin in the import submenu of the Art of Illusion file menu. Then from the Scripts submenu in the Tools menu, choose 'Export GNU Triangulated Surface' and select the imported STL shape. Click the 'Export Selected' checkbox and click OK. Once you've created the GTS file, you can turn it into gcode by typing in a shell in the same folder as skeinforge:
+> python skeinforge.py
+
+When the skeinforge dialog pops up, click 'Skeinforge', choose the file which you exported in 'Export GNU Triangulated Surface' and the gcode file will be saved with the suffix '_export.gcode'.
+
+Or you can turn files into gcode by adding the file name, for example:
+> python skeinforge.py Screw Holder Bottom.stl
+
+

License

+ +GNU Affero General Public License
+http://www.gnu.org/licenses/agpl.html
+
+

Motto

+ +I may be slow, but I get there in the end.
+
+

Troubleshooting

+ +If there's a bug, try downloading the very latest version because skeinforge is often updated without an announcement. The very latest version is at:
+http://members.axion.net/~enrique/reprap_python_beanshell.zip
+
+If there is still a bug, then first prepare the following files:
+
+1. stl file
+2. pictures explaining the problem
+3. your settings (pack the whole .skeinforge directory with all your settings)
+4. alterations folder, if you have any active alterations files
+
+Then zip all the files.
+
+Second, write a description of the error, send the description and the archive to the developer, enrique ( perez_enrique AT yahoo.com.removethispart ). After a bug fix is released, test the new version and report the results to enrique, whether the fix was successful or not.
+
+If the dialog window is too big for the screen, on most Linux window managers you can move a window by holding down the Alt key and then drag the window with the left mouse button to get to the off screen widgets.
+
+If you can't use the graphical interface, you can change the settings for skeinforge by using a text editor or spreadsheet to change the settings in the profiles folder in the .skeinforge folder in your home directory.
+
+Comments and suggestions are welcome, however, I won't reply unless you are a contributor. Likewise, I will only answer your questions if you contribute to skeinforge in some way. Some ways of contributing to skeinforge are in the contributions thread at:
+http://dev.forums.reprap.org/read.php?12,27562
+
+You could also contribute articles to demozendium on any topic:
+http://fabmetheus.crsndoo.com/wiki/index.php/Main_Page
+
+If you contribute in a significant way to another open source project, I will consider that also.
+
+When I answered everyone's questions, eventually I received more questions than I had time to answer, so now I only answer questions from contributors.
+
+I reserve the right to make any correspondence public. Do not send me any correspondence marked confidential. If you do I will delete it.
+
+
+

Examples

+
+ +The following examples forge the STL file Screw Holder.stl. The examples are run in a terminal in the folder which contains Screw Holder.gts and skeinforge.py.
+
+> python skeinforge.py
+This brings up the dialog, after clicking 'Skeinforge', the following is printed:
+The exported file is saved as Screw Holder_export.gcode
+
+> python skeinforge.py Screw Holder.stl
+The exported file is saved as Screw Holder_export.gcode
+
+To run only fill for example, type in the craft_plugins folder which fill is in:
+> python fill.py
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+os
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
SkeinforgeRepository +
+

+ + + + + + + +
 
+class SkeinforgeRepository
   A class to handle the skeinforge settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Skeinforge button has been clicked.
+ +
save(self)
Profile has been saved and profile menu should be updated.
+ +

+ + + + + +
 
+Functions
       
addToProfileMenu(profileSelection, profileType, repository)
Add a profile menu.
+
getNewRepository()
Get new repository.
+
getPluginFileNames()
Get skeinforge plugin fileNames.
+
getRadioPluginsAddPluginGroupFrame(directoryPath, importantFileNames, names, repository)
Get the radio plugins and add the plugin frame.
+
main()
Display the skeinforge dialog.
+
writeOutput(fileName)
Craft a file, display dialog.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__credits__ = '\nAdrian Bowyer <http://forums.reprap.org/profile...:\nArt of Illusion <http://www.artofillusion.org/>'
+__date__ = '$Date: 2008/02/05 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       
+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/>

+Organizations:
+Art of Illusion <http://www.artofillusion.org/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze.html new file mode 100644 index 0000000..f903b4c --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze.html @@ -0,0 +1,82 @@ + + +Python: module skeinforge_application.skeinforge_plugins.analyze + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.analyze ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/analyze.py
+

+Previous / Next / Contents +

+


+Analyze is a script to access the plugins which analyze a gcode file.
+
+The plugin buttons which are commonly used are bolded and the ones which are rarely used have normal font weight.
+
+
+Gcodes
+
+

Gcodes

+
+ +An explanation of the gcodes is at:
+http://reprap.org/bin/view/Main/Arduino_GCode_Interpreter
+
+and at:
+http://reprap.org/bin/view/Main/MCodeReference
+
+A gode example is at:
+http://forums.reprap.org/file.php?12,file=565
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_analyze
+
sys
+

+ + + + + +
 
+Functions
       
addToMenu(master, menu, repository, window)
Add a tool plugin menu.
+
getNewRepository()
Get new repository.
+
main()
Display the analyze dialog.
+
writeOutput(fileName)
Analyze a gcode file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.display_line.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.display_line.html new file mode 100644 index 0000000..f180146 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.display_line.html @@ -0,0 +1,127 @@ + + +Python: module skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.display_line + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.display_line ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/display_line.py
+

+Previous / Next / Contents +

+


+Display line is a mouse tool to select and display information about the line.
+
+When a line is clicked, the line will be selected and information about the line will be displayed. If a gcode line is clicked, the information will be file line count of the line clicked, counting from one, and the line itself.
+
+When the display line tool is chosen and the canvas has the focus, display line will listen to the arrow keys. Clicking in the canvas gives the canvas the focus, and when the canvas has the focus a thick black border is drawn around the canvas. When the right arrow key is pressed, display line will increase the line index of the layer by one, and change the selection accordingly. If the line index of the layer goes over the index of the last line, the layer index will be increased by one and the new line index will be zero. When the left arrow key is pressed, the index will be decreased. If the line index goes below the index of the first line, the layer index will be decreased by one and the new line index will be at the last line. The up arrow key increases the layer index by one and the down arow key decreases the line index.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.gcodec
+
fabmetheus_utilities.settings
+

+ + + + + +
 
+Classes
       
+
skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base.MouseToolBase +
+
+
DisplayLine +
+
+
+

+ + + + + + + +
 
+class DisplayLine(skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base.MouseToolBase)
   Display the line when it is clicked.
 
 Methods defined here:
+
button1(self, event, shift=False)
Print line text and connection line.
+ +
destroyEverything(self)
Destroy items.
+ +
drawLineText(self, location, tags)
Draw the line text.
+ +
drawSelectedColoredLineText(self)
Draw the selected line and text.
+ +
getSelectedColoredLine(self)
Draw the selected line, add it to the items and return the colored line.
+ +
isSelectionTool(self)
Return if this mouse tool is a selection tool.
+ +
keyPressDown(self, event)
The down arrow was pressed.
+ +
keyPressLeft(self, event)
The left arrow was pressed.
+ +
keyPressRight(self, event)
The right arrow was pressed.
+ +
keyPressUp(self, event)
The up arrow was pressed.
+ +
update(self)
Update the mouse tool.
+ +
+Methods inherited from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base.MouseToolBase:
+
buttonRelease1(self, event)
The left button was released, <ButtonRelease-1> function.
+ +
destroyEverythingGetFocus(self)
Destroy items and get the focus for the canvas.
+ +
getReset(self, window)
Reset the mouse tool to default.
+ +
getTagsGivenXY(self, x, y)
Get the tag for the x and y.
+ +
keyPressReturn(self, event)
The return key was pressed.
+ +
motion(self, event, shift=False)
The mouse moved, <Motion> function.
+ +
setWindowItems(self, window)
Set the canvas and items.
+ +

+ + + + + +
 
+Functions
       
getNewMouseTool()
Get a new mouse tool.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.html new file mode 100644 index 0000000..d008db6 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.html @@ -0,0 +1,37 @@ + + +Python: package skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
display_line
+mouse_tool_base
+
tableau
+view_move
+
view_rotate
+zoom_in
+
zoom_out
+

+ + + + + +
 
+Data
       level = 4
+numberOfLevelsDeepInPackageHierarchy = 4
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base.html new file mode 100644 index 0000000..6c296aa --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base.html @@ -0,0 +1,89 @@ + + +Python: module skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/mouse_tool_base.py
+

Display line is a mouse tool to display the line index of the line clicked, counting from one, and the line itself.

+

+ + + + + +
 
+Modules
       
__init__
+

+ + + + + +
 
+Classes
       
+
MouseToolBase +
+

+ + + + + + + +
 
+class MouseToolBase
   The mouse tool base class, which does nothing.
 
 Methods defined here:
+
button1(self, event)
The left button was clicked, <Button-1> function.
+ +
buttonRelease1(self, event)
The left button was released, <ButtonRelease-1> function.
+ +
destroyEverything(self)
Destroy items.
+ +
destroyEverythingGetFocus(self)
Destroy items and get the focus for the canvas.
+ +
getReset(self, window)
Reset the mouse tool to default.
+ +
getTagsGivenXY(self, x, y)
Get the tag for the x and y.
+ +
isSelectionTool(self)
Return if this mouse tool is a selection tool.
+ +
keyPressDown(self, event)
The down arrow was pressed.
+ +
keyPressLeft(self, event)
The left arrow was pressed.
+ +
keyPressReturn(self, event)
The return key was pressed.
+ +
keyPressRight(self, event)
The right arrow was pressed.
+ +
keyPressUp(self, event)
The up arrow was pressed.
+ +
motion(self, event, shift=False)
The mouse moved, <Motion> function.
+ +
setWindowItems(self, window)
Set the canvas and items.
+ +
update(self)
Update the mouse tool.
+ +

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.tableau.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.tableau.html new file mode 100644 index 0000000..8e62562 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.tableau.html @@ -0,0 +1,262 @@ + + +Python: module skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.tableau + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.tableau ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/tableau.py
+

Tableau has a couple of base classes for analyze viewers.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.gcodec
+math
+os
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.zoom_in
+skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.zoom_out
+

+ + + + + +
 
+Classes
       
+
ColoredLine +
ExportCanvasDialog +
TableauRepository +
TableauWindow +
+

+ + + + + + + +
 
+class ColoredLine
   A colored index line.
 
 Methods defined here:
+
__init__(self, begin, colorName, displayString, end, tagString)
Set the color name and corners.
+ +
__repr__(self)
Get the string representation of this colored index line.
+ +

+ + + + + + + +
 
+class ExportCanvasDialog
   A class to display the export canvas repository dialog.
 
 Methods defined here:
+
addPluginToMenu(self, canvas, fileName, menu, name, suffix)
Add the display command to the menu.
+ +
display(self)
Display the export canvas repository dialog.
+ +

+ + + + + + + +
 
+class TableauRepository
   The viewer base repository class.
 
 Methods defined here:
+
addAnimation(self)
Add the animation settings.
+ +
addScaleScreenSlide(self)
Add the scale, screen and slide show settings.
+ +
setToDisplaySave(self, event=None)
Set the setting values to the display, save the new values.
+ +

+ + + + + +
 
+class TableauWindow
    Methods defined here:
+
activateMouseModeTool(self)
Activate the mouse mode tool.
+ +
addCanvasMenuRootScrollSkein(self, repository, skein, suffix, title)
Add the canvas, menu bar, scroll bar, skein panes, tableau repository, root and skein.
+ +
addLayer(self, gridPosition)
Add the layer frame items.
+ +
addLine(self, gridPosition)
Add the line frame items.
+ +
addMouseInstantTool(self, fileName, gridPosition, mouseInstantTool)
Add the mouse instant tool and derived photo button.
+ +
addMouseToolsBind(self)
Add the mouse tool and bind button one clicked, button one released and motion.
+ +
addPhotoImage(self, fileName, gridPosition)
Get a PhotoImage button, grid the button and increment the grid position.
+ +
addScale(self, gridPosition)
Add the line frame items.
+ +
addSettingsMenuSetWindowGeometry(self, center)
Add the settings menu, center the scroll region, update, and set the window geometry.
+ +
button1(self, event)
The button was clicked.
+ +
buttonRelease1(self, event)
The button was released.
+ +
cancel(self, event=None)
Set all entities to their saved state.
+ +
cancelTimer(self, event=None)
Cancel the timer and set it to none.
+ +
cancelTimerResetButtons(self)
Cancel the timer and set it to none.
+ +
close(self, event=None)
The dialog was closed.
+ +
createMouseModeTool(self)
Create the mouse mode tool.
+ +
destroyAllDialogWindows(self)
Destroy all the dialog windows.
+ +
destroyMouseToolRaiseMouseButtons(self)
Destroy the mouse tool and raise the mouse buttons.
+ +
dive(self)
Dive, go down periodically.
+ +
diveCycle(self)
Start the dive cycle.
+ +
getAnimationLineDelay(self, coloredLine)
Get the animation line delay in milliseconds.
+ +
getDrawnLineText(self, location, tags, text)
Get the line text drawn on the canvas.
+ +
getEntityFromName(self, name)
Get the entity of the given name.
+ +
getPhotoButtonGridIncrement(self, commandFunction, fileName, gridPosition)
Get a PhotoImage button, grid the button and increment the grid position.
+ +
getRoundedRulingText(self, extraDecimalPlaces, number)
Get the rounded ruling text.
+ +
getRulingSeparationWidthPixels(self, rank)
Get the separation width in pixels.
+ +
getScrollPaneCenter(self)
Get the center of the scroll pane.
+ +
getScrollPaneFraction(self)
Get the scroll pane top left.
+ +
getSlideShowDelay(self)
Get the slide show delay in milliseconds.
+ +
getUpdateSkeinPanes(self)
Get the update skein panes.
+ +
isLineBelowZeroSetLayer(self)
Determine if the line index is below zero, and if so set the layer index.
+ +
isLineBeyondListSetLayer(self)
Determine if the line index is beyond the end of the list, and if so set the layer index.
+ +
keyPressDown(self, event)
The down arrow was pressed.
+ +
keyPressLeft(self, event)
The left arrow was pressed.
+ +
keyPressReturn(self, event)
The return key was pressed.
+ +
keyPressRight(self, event)
The right arrow was pressed.
+ +
keyPressUp(self, event)
The up arrow was pressed.
+ +
layerEntryReturnPressed(self, event=None)
The layer index entry return was pressed.
+ +
limitIndex(self)
Limit the index so it is not below zero or above the top.
+ +
limitIndexSetArrowMouseDeleteCanvas(self)
Limit the index, set the arrow type, and delete all the canvas items.
+ +
lineDive(self)
Line dive, go down periodically.
+ +
lineDiveCycle(self)
Start the line dive cycle.
+ +
lineEntryReturnPressed(self, event=None)
The line index entry return was pressed.
+ +
lineSoar(self)
Line soar, go up periodically.
+ +
lineSoarCycle(self)
Start the line soar cycle.
+ +
motion(self, event)
The mouse moved.
+ +
phoenixUpdate(self)
Update the skein, and deiconify a new window and destroy the old.
+ +
redisplayWindowUpdate(self, event=None)
Deiconify a new window and destroy the old.
+ +
relayXview(self, *args)
Relay xview changes.
+ +
relayYview(self, *args)
Relay yview changes.
+ +
resetPeriodicButtonsText(self)
Reset the text of the periodic buttons.
+ +
save(self)
Set the setting values to the display, save the new values.
+ +
scaleEntryReturnPressed(self, event=None)
The scale entry return was pressed.
+ +
setButtonImageText(self, button, text)
Set the text of the e periodic buttons.
+ +
setDisplayLayerIndex(self)
Set the display of the layer index entry field and buttons.
+ +
setInsetToCanvas(self, event=None)
Set the repository insets to the canvas.
+ +
setLayerIndex(self, layerIndex)
Set the layer index.
+ +
setLineButtonsState(self)
Set the state of the line buttons.
+ +
setWindowNewMouseTool(self, getNewMouseToolFunction, mouseTool)
Set the getNewMouseTool function and the update function.
+ +
setWindowToDisplaySavePhoenixUpdate(self, event=None)
Set the setting values to the display, save the new values, then call the update function.
+ +
setWindowToDisplaySaveUpdate(self, event=None)
Set the setting values to the display, save the new values, then call the update function.
+ +
shiftButtonRelease1(self, event)
The button was released while the shift key was pressed.
+ +
shiftMotion(self, event)
The mouse moved.
+ +
soar(self)
Soar, go up periodically.
+ +
soarCycle(self)
Start the soar cycle.
+ +
updateDeiconify(self, center=(0.5+0.5j))
Update and deiconify the window.
+ +
updateMouseToolIfSelection(self)
Update the mouse tool if it is a selection tool.
+ +
updateNewDestroyOld(self, scrollPaneCenter)
Update and deiconify a window and destroy the old.
+ +

+ + + + + +
 
+Functions
       
getGeometricDifference(first, second)
Get the geometric difference of the two numbers.
+
getGridHorizontalFrame(gridPosition)
Get the grid horizontal object with a frame from the grid position.
+
getIsLayerStart(firstWord, skein, splitLine)
Determine if the line is the start of a layer.
+
getLengthMinusOneMinimumOne(elementList)
Get the length of the length minus one, minimum one.
+
getPluginsDirectoryPath()
Get the plugins directory path.
+
getScrollbarCanvasPortion(scrollbar)
Get the canvas portion of the scrollbar.
+
setStateNormalDisabled(active, widget)
Set the state of the widget to normal if active and disabled if inactive.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.view_move.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.view_move.html new file mode 100644 index 0000000..e7ad96b --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.view_move.html @@ -0,0 +1,124 @@ + + +Python: module skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.view_move + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.view_move ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/view_move.py
+

+Previous / Next / Contents +

+


+Viewpoint move is a mouse tool to move the viewpoint in the xy plane.
+
+When the mouse is clicked and dragged on the canvas, viewpoint move will drag the scroll pane accordingly. If the shift key is also pressed, the scroll pane will be moved only in the x or y direction, whichever is largest.
+
+When the viewpoint move tool is chosen and the canvas has the focus, viewpoint move will listen to the arrow keys. Clicking in the canvas gives the canvas the focus, and when the canvas has the focus a thick black border is drawn around the canvas. When the right arrow key is pressed, viewpoint move will move the scroll pane to the right by a pixel. When the left arrow key is pressed, the scroll pane will be moved a pixel to the left. The up arrow key moves the scroll pane a pixel up and the down arow key moves the scroll pane a pixel down.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.settings
+

+ + + + + +
 
+Classes
       
+
skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base.MouseToolBase +
+
+
ViewpointMove +
+
+
+

+ + + + + + + +
 
+class ViewpointMove(skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base.MouseToolBase)
   Display the line when it is clicked.
 
 Methods defined here:
+
button1(self, event, shift=False)
Print line text and connection line.
+ +
buttonRelease1(self, event, shift=False)
The left button was released, <ButtonRelease-1> function.
+ +
destroyEverything(self)
Destroy items.
+ +
keyPressDown(self, event)
The down arrow was pressed.
+ +
keyPressLeft(self, event)
The left arrow was pressed.
+ +
keyPressRight(self, event)
The right arrow was pressed.
+ +
keyPressUp(self, event)
The up arrow was pressed.
+ +
motion(self, event, shift=False)
The mouse moved, <Motion> function.
+ +
relativeMove(self, relativeMotion)
Move the view given the relative motion.
+ +
setScrollPaneMove(self, relativeMotion)
The up arrow was pressed.
+ +
+Methods inherited from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base.MouseToolBase:
+
destroyEverythingGetFocus(self)
Destroy items and get the focus for the canvas.
+ +
getReset(self, window)
Reset the mouse tool to default.
+ +
getTagsGivenXY(self, x, y)
Get the tag for the x and y.
+ +
isSelectionTool(self)
Return if this mouse tool is a selection tool.
+ +
keyPressReturn(self, event)
The return key was pressed.
+ +
setWindowItems(self, window)
Set the canvas and items.
+ +
update(self)
Update the mouse tool.
+ +

+ + + + + +
 
+Functions
       
getNewMouseTool()
Get a new mouse tool.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.view_rotate.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.view_rotate.html new file mode 100644 index 0000000..fdebf67 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.view_rotate.html @@ -0,0 +1,146 @@ + + +Python: module skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.view_rotate + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.view_rotate ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/view_rotate.py
+

+Previous / Next / Contents +

+


+Viewpoint rotate is a mouse tool to rotate the viewpoint around the origin.
+
+When the mouse is clicked, dragged and released on the canvas, viewpoint rotate will rotate the longitude by the amount the mouse is dragged around the origin. If the mouse is moved towards the origin, the latitude will be increased, so the viewpoint will be closer to the top. If the mouse is moved away from the origin, the latitude will be decreased. If the shift key is also pressed, only the latitude or longitude will be changed, whichever is being changed the most.
+
+When the viewpoint rotate tool is chosen and the canvas has the focus, viewpoint rotate will listen to the arrow keys. Clicking in the canvas gives the canvas the focus, and when the canvas has the focus a thick black border is drawn around the canvas. When the right arrow key is pressed, viewpoint rotate will increase the preview longitude by one degree. When the left arrow key is pressed, the preview longitude will be decreased. The up arrow key increase the preview latitude by one degree and the down arow decreases the preview latitude. Pressing the <Return> key implements the preview.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.euclidean
+
math
+
fabmetheus_utilities.settings
+

+ + + + + +
 
+Classes
       
+
skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base.MouseToolBase +
+
+
ViewpointRotate +
+
+
LatitudeLongitude +
+

+ + + + + + + +
 
+class LatitudeLongitude
   A latitude and longitude.
 
 Methods defined here:
+
__init__(self, buttonOnePressedCanvasCoordinate, newCoordinate, skeinWindow, shift)
Set the latitude and longitude.
+ +

+ + + + + + + +
 
+class ViewpointRotate(skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base.MouseToolBase)
   Display the line when it is clicked.
 
 Methods defined here:
+
button1(self, event, shift=False)
Print line text and connection line.
+ +
buttonRelease1(self, event, shift=False)
The left button was released, <ButtonRelease-1> function.
+ +
destroyEverything(self)
Destroy items.
+ +
getMoveCoordinate(self)
Get the movement coordinate from the class relative latitude and longitude.
+ +
keyPressDown(self, event)
The down arrow was pressed.
+ +
keyPressLeft(self, event)
The left arrow was pressed.
+ +
keyPressMotion(self)
Move the motion viewpoint for the class key press coordinates.
+ +
keyPressReturn(self, event)
The return key was pressed.
+ +
keyPressRight(self, event)
The right arrow was pressed.
+ +
keyPressStart(self)
If necessary, destroy everything and calculate the keyStartCanvasCoordinate.
+ +
keyPressUp(self, event)
The up arrow was pressed.
+ +
motion(self, event, shift=False)
Move the motion viewpoint if the mouse was moved.
+ +
motionGivenCoordinates(self, motionCoordinate, shift, startCoordinate)
Move the motion viewpoint given the motion coordinates.
+ +
moveViewpointGivenCoordinates(self, moveCoordinate, shift, startCoordinate)
Move the viewpoint given the move coordinates.
+ +
+Methods inherited from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base.MouseToolBase:
+
destroyEverythingGetFocus(self)
Destroy items and get the focus for the canvas.
+ +
getReset(self, window)
Reset the mouse tool to default.
+ +
getTagsGivenXY(self, x, y)
Get the tag for the x and y.
+ +
isSelectionTool(self)
Return if this mouse tool is a selection tool.
+ +
setWindowItems(self, window)
Set the canvas and items.
+ +
update(self)
Update the mouse tool.
+ +

+ + + + + +
 
+Functions
       
getBoundedLatitude(latitude)
Get the bounded latitude.later get rounded
+
getNewMouseTool()
Get a new mouse tool.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.zoom_in.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.zoom_in.html new file mode 100644 index 0000000..ae4710e --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.zoom_in.html @@ -0,0 +1,120 @@ + + +Python: module skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.zoom_in + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.zoom_in ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/zoom_in.py
+

+Previous / Next / Contents +

+


+Zoom in is a mouse tool to zoom in the display at the point where the mouse was clicked, increasing the scale by a factor of two.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.settings
+

+ + + + + +
 
+Classes
       
+
skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base.MouseToolBase +
+
+
ZoomIn +
+
+
+

+ + + + + + + +
 
+class ZoomIn(skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base.MouseToolBase)
   The zoom in mouse tool.
 
 Methods defined here:
+
button1(self, event, shift=False)
Print line text and connection line.
+ +
click(self, event=None)
Set the window mouse tool to this.
+ +
getMultiplier(self)
Get the scale multiplier.
+ +
getReset(self, window)
Reset the mouse tool to default.
+ +
+Methods inherited from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base.MouseToolBase:
+
buttonRelease1(self, event)
The left button was released, <ButtonRelease-1> function.
+ +
destroyEverything(self)
Destroy items.
+ +
destroyEverythingGetFocus(self)
Destroy items and get the focus for the canvas.
+ +
getTagsGivenXY(self, x, y)
Get the tag for the x and y.
+ +
isSelectionTool(self)
Return if this mouse tool is a selection tool.
+ +
keyPressDown(self, event)
The down arrow was pressed.
+ +
keyPressLeft(self, event)
The left arrow was pressed.
+ +
keyPressReturn(self, event)
The return key was pressed.
+ +
keyPressRight(self, event)
The right arrow was pressed.
+ +
keyPressUp(self, event)
The up arrow was pressed.
+ +
motion(self, event, shift=False)
The mouse moved, <Motion> function.
+ +
setWindowItems(self, window)
Set the canvas and items.
+ +
update(self)
Update the mouse tool.
+ +

+ + + + + +
 
+Functions
       
getNewMouseTool()
Get a new mouse tool.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.zoom_out.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.zoom_out.html new file mode 100644 index 0000000..5b9dcd2 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.zoom_out.html @@ -0,0 +1,129 @@ + + +Python: module skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.zoom_out + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.zoom_out ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/zoom_out.py
+

+Previous / Next / Contents +

+


+Zoom out is a mouse tool to zoom out the display at the point where the mouse was clicked, decreasing the scale by a factor of two.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.zoom_in
+

+ + + + + +
 
+Classes
       
+
skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.zoom_in.ZoomIn(skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base.MouseToolBase) +
+
+
ZoomOut +
+
+
+

+ + + + + + + +
 
+class ZoomOut(skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.zoom_in.ZoomIn)
   The zoom out mouse tool.
 
 
Method resolution order:
+
ZoomOut
+
skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.zoom_in.ZoomIn
+
skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base.MouseToolBase
+
+
+Methods defined here:
+
getMultiplier(self)
Get the scale multiplier.
+ +
+Methods inherited from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.zoom_in.ZoomIn:
+
button1(self, event, shift=False)
Print line text and connection line.
+ +
click(self, event=None)
Set the window mouse tool to this.
+ +
getReset(self, window)
Reset the mouse tool to default.
+ +
+Methods inherited from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base.MouseToolBase:
+
buttonRelease1(self, event)
The left button was released, <ButtonRelease-1> function.
+ +
destroyEverything(self)
Destroy items.
+ +
destroyEverythingGetFocus(self)
Destroy items and get the focus for the canvas.
+ +
getTagsGivenXY(self, x, y)
Get the tag for the x and y.
+ +
isSelectionTool(self)
Return if this mouse tool is a selection tool.
+ +
keyPressDown(self, event)
The down arrow was pressed.
+ +
keyPressLeft(self, event)
The left arrow was pressed.
+ +
keyPressReturn(self, event)
The return key was pressed.
+ +
keyPressRight(self, event)
The right arrow was pressed.
+ +
keyPressUp(self, event)
The up arrow was pressed.
+ +
motion(self, event, shift=False)
The mouse moved, <Motion> function.
+ +
setWindowItems(self, window)
Set the canvas and items.
+ +
update(self)
Update the mouse tool.
+ +

+ + + + + +
 
+Functions
       
getNewMouseTool()
Get a new mouse tool.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.clairvoyance.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.clairvoyance.html new file mode 100644 index 0000000..4f72714 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.clairvoyance.html @@ -0,0 +1,127 @@ + + +Python: module skeinforge_application.skeinforge_plugins.analyze_plugins.clairvoyance + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.analyze_plugins.clairvoyance ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/analyze_plugins/clairvoyance.py
+

+Previous / Next / Contents +

+


+Clairvoyance is an analyze plugin to open the gcode file with an outside program.
+
+The clairvoyance manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Clairvoyance
+
+
+Operation
+Settings
+  Gcode Program
+Examples
+
+

Operation

+
+ +The default 'Activate Clairvoyance' checkbox is off. When it is on, the functions described below will work when called from the skeinforge toolchain, when it is off, the functions will not be called from the toolchain. The functions will still be called, whether or not the 'Activate Clairvoyance' checkbox is on, when clairvoyance is run directly.
+
+

Settings

+
+ +

Gcode Program

+ +Default is webbrowser.
+
+If the 'Gcode Program' is set to webbrowser, the gcode file will be sent to the default browser to be opened. If the 'Gcode Program' is set to a program name, the gcode file will be sent to that program to be opened. A good gcode viewer is Pleasant3D, at:
+http://www.pleasantsoftware.com/developer/pleasant3d/index.shtml
+
+

Examples

+
+ +Below are examples of clairvoyance being used. These examples are run in a terminal in the folder which contains Screw Holder_penultimate.gcode and clairvoyance.py.
+
+> python clairvoyance.py
+This brings up the clairvoyance dialog.
+
+> python clairvoyance.py Screw Holder_penultimate.gcode
+The file is opened by an outside program
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+subprocess
+
sys
+traceback
+

+ + + + + +
 
+Classes
       
+
ClairvoyanceRepository +
+

+ + + + + + + +
 
+class ClairvoyanceRepository
   A class to handle the clairvoyance settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Write button has been clicked.
+ +

+ + + + + +
 
+Functions
       
getNewRepository()
Get new repository.
+
getWindowAnalyzeFile(fileName, repository=None)
Open penultimate file with outside program.
+
main()
Display the clairvoyance dialog.
+
writeOutput(fileName, fileNamePenultimate, fileNameSuffix, filePenultimateWritten, gcodeText='')
Open penultimate file with outside program given text.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.comment.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.comment.html new file mode 100644 index 0000000..f588d70 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.comment.html @@ -0,0 +1,152 @@ + + +Python: module skeinforge_application.skeinforge_plugins.analyze_plugins.comment + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.analyze_plugins.comment ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/analyze_plugins/comment.py
+

+Previous / Next / Contents +

+


+Comment is an analyze plugin to comment a gcode file.
+
+The comment manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Comment
+
+
+Operation
+Gcodes
+Examples
+
+

Operation

+
+ +The default 'Activate Comment' checkbox is off. When it is on, the file will be commented when called from the skeinforge toolchain, when it is off, the file will not be commented when called from the toolchain. The file will still be commented, whether or not the 'Activate Comment' checkbox is on, when comment is run directly.
+
+

Gcodes

+
+ +An explanation of the gcodes is at:
+http://reprap.org/bin/view/Main/Arduino_GCode_Interpreter
+
+and at:
+http://reprap.org/bin/view/Main/MCodeReference
+
+A gode example is at:
+http://forums.reprap.org/file.php?12,file=565
+
+

Examples

+
+ +Below are examples of comment being used. These examples are run in a terminal in the folder which contains Screw_Holder_penultimate.gcode and comment.py.
+
+> python comment.py
+This brings up the comment dialog.
+
+> python comment.py Screw Holder_penultimate.gcode
+The comment file is saved as Screw_Holder_penultimate_comment.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+
cStringIO
+fabmetheus_utilities.gcodec
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
CommentRepository +
CommentSkein +
+

+ + + + + + + +
 
+class CommentRepository
   A class to handle the comment settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Write button has been clicked.
+ +

+ + + + + + + +
 
+class CommentSkein
   A class to comment a gcode skein.
 
 Methods defined here:
+
__init__(self)
+ +
addComment(self, comment)
Add a gcode comment and a newline to the output.
+ +
linearMove(self, splitLine)
Comment a linear move.
+ +
parseGcode(self, gcodeText)
Parse gcode text and store the commented gcode.
+ +
parseLine(self, line)
Parse a gcode line and add it to the commented gcode.
+ +
setHelicalMoveEndpoint(self, splitLine)
Get the endpoint of a helical move.
+ +

+ + + + + +
 
+Functions
       
getNewRepository()
Get new repository.
+
getWindowAnalyzeFile(fileName)
Comment a gcode file.
+
getWindowAnalyzeFileGivenText(fileName, gcodeText)
Write a commented gcode file for a gcode file.
+
main()
Display the comment dialog.
+
writeOutput(fileName, fileNamePenultimate, fileNameSuffix, filePenultimateWritten, gcodeText='')
Write a commented gcode file for a skeinforge gcode file, if 'Write Commented File for Skeinforge Chain' is selected.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.export_canvas_plugins.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.export_canvas_plugins.html new file mode 100644 index 0000000..778d736 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.export_canvas_plugins.html @@ -0,0 +1,32 @@ + + +Python: package skeinforge_application.skeinforge_plugins.analyze_plugins.export_canvas_plugins + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.analyze_plugins.export_canvas_plugins
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/analyze_plugins/export_canvas_plugins/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
postscript
+
scalable_vector_graphics
+

+ + + + + +
 
+Data
       level = 4
+numberOfLevelsDeepInPackageHierarchy = 4
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.export_canvas_plugins.postscript.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.export_canvas_plugins.postscript.html new file mode 100644 index 0000000..284a1df --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.export_canvas_plugins.postscript.html @@ -0,0 +1,100 @@ + + +Python: module skeinforge_application.skeinforge_plugins.analyze_plugins.export_canvas_plugins.postscript + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.analyze_plugins.export_canvas_plugins.postscript ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/analyze_plugins/export_canvas_plugins/postscript.py
+

+Previous / Next / Contents +

+


+Postscript is an export canvas plugin to export the canvas to a postscript file.
+
+When the export menu item in the file menu in an analyze viewer tool, like skeinlayer or skeiniso is clicked, the postscript dialog will be displayed. When the 'Export to Postscript' button on that dialog is clicked, the canvas will be exported as a postscript file. If the 'Postscript Program' is set to a program name, the postscript file will be sent to that program to be opened. The default is gimp, the Gnu Image Manipulation Program (Gimp), which is open source, can open postscript and save in a variety of formats. It is available at:
+http://www.gimp.org/
+
+If furthermore the 'File Extension' is set to a file extension, the postscript file will be sent to the program, along with the file extension for the converted output. The default is blank because some systems do not have an image conversion program; if you have or will install an image conversion program, a common 'File Extension' is png. A good open source conversion program is Image Magick, which is available at:
+http://www.imagemagick.org/script/index.php
+
+An export canvas plugin is a script in the export_canvas_plugins folder which has the function getNewRepository, and which has a repository class with the functions setCanvasFileNameSuffix to set variables and execute to save the file. It is meant to be run from an analyze viewer tool, like skeinlayer or skeiniso. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+
cStringIO
+fabmetheus_utilities.gcodec
+
os
+fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
PostscriptRepository +
+

+ + + + + + + +
 
+class PostscriptRepository
   A class to handle the export settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Convert to postscript button has been clicked. Export the canvas as a postscript file.
+ +
setCanvasFileNameSuffix(self, canvas, fileName, suffix)
Set the canvas and initialize the execute title.
+ +

+ + + + + +
 
+Functions
       
getNewRepository()
Get new repository.
+
main()
Display the file or directory dialog.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.export_canvas_plugins.scalable_vector_graphics.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.export_canvas_plugins.scalable_vector_graphics.html new file mode 100644 index 0000000..c76f6a7 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.export_canvas_plugins.scalable_vector_graphics.html @@ -0,0 +1,104 @@ + + +Python: module skeinforge_application.skeinforge_plugins.analyze_plugins.export_canvas_plugins.scalable_vector_graphics + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.analyze_plugins.export_canvas_plugins.scalable_vector_graphics ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/analyze_plugins/export_canvas_plugins/scalable_vector_graphics.py
+

+Previous / Next / Contents +

+


+Scalable vector graphics is an export canvas plugin to export the canvas to a scalable vector graphics (.svg) file.
+
+When the export menu item in the file menu in an analyze viewer tool, like skeinlayer or skeiniso is clicked, the postscript dialog will be displayed. When the 'Export to Scalable Vector Graphics' button on that dialog is clicked, the canvas will be exported as a scalable vector graphics file. If the 'Scalable Vector Graphics Program' is set to the default 'webbrowser', the scalable vector graphics file will be sent to the default browser to be opened. If the 'Scalable Vector Graphics Program' is set to a program name, the scalable vector graphics file will be sent to that program to be opened.
+
+If furthermore the 'File Extension' is set to a file extension, the scalable vector graphics file will be sent to the program, along with the file extension for the converted output. The default is blank because some systems do not have an image conversion program; if you have or will install an image conversion program, a common 'File Extension' is png. A good open source conversion program is Image Magick, which is available at:
+http://www.imagemagick.org/script/index.php
+
+An export canvas plugin is a script in the export_canvas_plugins folder which has the function getNewRepository, and which has a repository class with the functions setCanvasFileNameSuffix to set variables and execute to save the file. It is meant to be run from an analyze viewer tool, like skeinlayer or skeiniso. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+
cStringIO
+fabmetheus_utilities.gcodec
+
os
+fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
ScalableVectorGraphicsRepository +
+

+ + + + + + + +
 
+class ScalableVectorGraphicsRepository
   A class to handle the export settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
addCanvasLineToOutput(self, canvasLinesOutput, objectIDNumber)
Add the canvas line to the output.
+ +
execute(self)
Export the canvas as an svg file.
+ +
getCanvasLinesOutput(self)
Add the canvas line to the output.
+ +
setCanvasFileNameSuffix(self, canvas, fileName, suffix)
Set the canvas and initialize the execute title.
+ +

+ + + + + +
 
+Functions
       
getNewRepository()
Get new repository.
+
main()
Display the file or directory dialog.
+
parseLineReplace(firstWordTable, line, output)
Parse the line and replace it if the first word of the line is in the first word table.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.html new file mode 100644 index 0000000..0269955 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.html @@ -0,0 +1,40 @@ + + +Python: package skeinforge_application.skeinforge_plugins.analyze_plugins + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.analyze_plugins
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/analyze_plugins/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
analyze_utilities (package)
+clairvoyance
+comment
+
export_canvas_plugins (package)
+interpret
+skeiniso
+
skeinlayer
+statistic
+synopsis
+
vectorwrite
+

+ + + + + +
 
+Data
       level = 3
+numberOfLevelsDeepInPackageHierarchy = 3
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.interpret.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.interpret.html new file mode 100644 index 0000000..2fbef18 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.interpret.html @@ -0,0 +1,104 @@ + + +Python: module skeinforge_application.skeinforge_plugins.analyze_plugins.interpret + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.analyze_plugins.interpret ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/analyze_plugins/interpret.py
+

+Previous / Next / Contents +

+


+Interpret is an analyze plugin to interpret a file, turning a 2D file into svg and a 3D file into constructive solid geometry xml.
+
+The comment manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Interpret
+
+
+Operation
+Settings
+  Print Interpretion
+  Text Program
+Examples
+
+

Operation

+
+ +The default 'Activate Interpret' checkbox is off. When it is on, the functions described below will work when called from the skeinforge toolchain, when it is off, the functions will not be called from the tool chain. The functions will still be called, whether or not the 'Activate Interpret' checkbox is on, when interpret is run directly.
+
+

Settings

+
+ +

Print Interpretion

+ +Default is off.
+
+When selected, the xml text will be printed to the console.
+
+

Text Program

+ +Default is webbrowser.
+
+If the 'Text Program' is set the default 'webbrowser', the XML file will be sent to the default browser to be opened. If the 'Text Program' is set to a program name, the XML file will be sent to that program to be opened.
+
+

Examples

+
+ +Below are examples of interpret being used. These examples are run in a terminal in the folder which contains Screw_Holder.stl and interpret.py.
+
+> python interpret.py
+This brings up the interpret dialog.
+
+> python interpret.py Screw_Holder.stl
+The comment file is saved as Screw_Holder_interpret.xml
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+
fabmetheus_utilities.settings
+
sys
+

+ + + + + +
 
+Functions
       
getNewRepository()
Get new repository.
+
main()
Display the interpret dialog.
+
writeOutput(fileName, fileNamePenultimate, fileNameSuffix, filePenultimateWritten, gcodeText='')
Write file interpretation, if activate interpret is selected.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.skeiniso.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.skeiniso.html new file mode 100644 index 0000000..83ea451 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.skeiniso.html @@ -0,0 +1,693 @@ + + +Python: module skeinforge_application.skeinforge_plugins.analyze_plugins.skeiniso + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.analyze_plugins.skeiniso ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/analyze_plugins/skeiniso.py
+

+Previous / Next / Contents +

+


+Skeiniso is an analyze viewer to display a gcode file in an isometric view.
+
+The skeiniso manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Skeiniso
+
+
+Operation
+Settings
+  Animation
+    Animation Line Quickening
+    Animation Slide Show Rate
+  Axis Rulings
+  Banding
+    Band Height
+    Bottom Band Brightness
+    Bottom Layer Brightness
+    Bright Band Start
+      From the Bottom
+      From the Top
+  Draw Arrows
+  Export Menu
+  Go Around Extruder Off Travel
+  Layers
+    Layer
+    Layer Extra Span
+  Line
+  Mouse Mode
+    Display Line
+    Viewpoint Move
+    Viewpoint Rotate
+  Number of Fill Layers
+    Number of Fill Bottom Layers
+  Number of Fill Top Layers
+  Scale
+  Screen Inset
+    Screen Horizontal Inset
+    Screen Vertical Inset
+  Viewpoint
+    Viewpoint Latitude
+    Viewpoint Longitude
+  Width
+    Width of Axis Negative Side
+    Width of Axis Positive Side
+    Width of Infill Thread
+    Width of Fill Bottom Thread
+    Width of Fill Top Thread
+    Width of Loop Thread
+    Width of Perimeter Inside Thread
+    Width of Perimeter Outside Thread
+    Width of Raft Thread
+    Width of Selection Thread
+    Width of Travel Thread
+Icons
+Gcodes
+Examples
+
+

Operation

+
+ +The default 'Activate Skeiniso' checkbox is off. When it is on, the functions described below will work when called from the skeinforge toolchain, when it is off, the functions will not be called from the toolchain. The functions will still be called, whether or not the 'Activate Skeiniso' checkbox is on, when skeiniso is run directly.
+
+Skeiniso requires skeinforge comments in the gcode file to distinguish the loops and edges. If the comments are deleted, all threads will be displayed as generic threads. To get the penultimate file of the tool chain, just before export deletes the comments, select 'Save Penultimate Gcode' in export, and open the gcode file with the suffix '_penultimate.gcode' with skeiniso.
+
+The viewer is simple, the viewpoint can only be moved in a sphere around the center of the model by changing the viewpoint latitude and longitude. Different regions of the model can be hidden by setting the width of the thread to zero. The alternating bands act as contour bands and their brightness and width can be changed.
+
+

Settings

+
+ +

Animation

+ +

Animation Line Quickening

+ +Default is one.
+
+The quickness of the tool animation over the quickness of the actual tool.
+
+

Animation Slide Show Rate

+ +Default is two layers per second.
+
+The rate, in layers per second, at which the layer changes when the soar or dive button is pressed..
+
+

Axis Rulings

+ +Default is on.
+
+When selected, rulings will be drawn on the axis lines.
+
+

Banding

+ +

Band Height

+ +Default is five layers.
+
+Defines the height of the band in layers, a pair of bands is twice that height.
+
+

Bottom Band Brightness

+ +Default is 0.7.
+
+Defines the ratio of the brightness of the bottom band over the brightness of the top band. The higher it is the brighter the bottom band will be.
+
+

Bottom Layer Brightness

+ +Default is one.
+
+Defines the ratio of the brightness of the bottom layer over the brightness of the top layer. With a low bottom layer brightness ratio the bottom of the model will be darker than the top of the model, as if it was being illuminated by a light just above the top.
+
+

Bright Band Start

+ +Default choice is 'From the Top'.
+
+The button group that determines where the bright band starts from.
+
+
From the Bottom
+ +When selected, the bright bands will start from the bottom.
+
+
From the Top
+ +When selected, the bright bands will start from the top.
+
+

Draw Arrows

+ +Default is on.
+
+When selected, arrows will be drawn at the end of each line segment.
+
+

Export Menu

+ +When the submenu in the export menu item in the file menu is clicked, an export canvas dialog will be displayed, which can export the canvas to a file.
+
+

Go Around Extruder Off Travel

+ +Default is off.
+
+When selected, the display will include the travel when the extruder is off, which means it will include the nozzle wipe path if any.
+
+

Layers

+ +

Layer

+ +Default is zero.
+
+On the display window, the Up button increases the 'Layer' by one, and the Down button decreases the layer by one. When the layer displayed in the layer spin box is changed then <Return> is hit, the layer shown will be set to the spin box, to a mimimum of zero and to a maximum of the highest index layer.The Soar button increases the layer at the 'Animation Slide Show Rate', and the Dive (double left arrow button beside the layer field) button decreases the layer at the slide show rate.
+
+

Layer Extra Span

+ +Default is a huge number.
+
+The viewer will draw the layers in the range including the 'Layer' index and the 'Layer' index plus the 'Layer Extra Span'. If the 'Layer Extra Span' is negative, the layers viewed will start at the 'Layer' index, plus the 'Layer Extra Span', and go up to and include the 'Layer' index. If the 'Layer Extra Span' is zero, only the 'Layer' index layer will be displayed. If the 'Layer Extra Span' is positive, the layers viewed will start at the 'Layer' index, and go up to and include the 'Layer' index plus the 'Layer Extra Span'.
+
+

Line

+ +Default is zero.
+
+The index of the selected line on the layer that is highlighted when the 'Display Line' mouse tool is chosen. The line spin box up button increases the 'Line' by one. If the line index of the layer goes over the index of the last line, the layer index will be increased by one and the new line index will be zero. The down button decreases the line index by one. If the line index goes below the index of the first line, the layer index will be decreased by one and the new line index will be at the last line. When the line displayed in the line field is changed then <Return> is hit, the line shown will be set to the line field, to a mimimum of zero and to a maximum of the highest index line. The Soar button increases the line at the speed at which the extruder would move, times the 'Animation Line Quickening' ratio, and the Dive (double left arrow button beside the line field) button decreases the line at the animation line quickening ratio.
+
+

Mouse Mode

+ +Default is 'Display Line'.
+
+The mouse tool can be changed from the 'Mouse Mode' menu button or picture button. The mouse tools listen to the arrow keys when the canvas has the focus. Clicking in the canvas gives the canvas the focus, and when the canvas has the focus a thick black border is drawn around the canvas.
+
+

Display Line

+ +The 'Display Line' tool will display the highlight the selected line, and display the file line count, counting from one, and the gcode line itself. When the 'Display Line' tool is active, clicking the canvas will select the closest line to the mouse click.
+
+

Viewpoint Move

+ +The 'Viewpoint Move' tool will move the viewpoint in the xy plane when the mouse is clicked and dragged on the canvas.
+
+

Viewpoint Rotate

+ +The 'Viewpoint Rotate' tool will rotate the viewpoint around the origin, when the mouse is clicked and dragged on the canvas, or the arrow keys have been used and <Return> is pressed. The viewpoint can also be moved by dragging the mouse. The viewpoint latitude will be increased when the mouse is dragged from the center towards the edge. The viewpoint longitude will be changed by the amount around the center the mouse is dragged. This is not very intuitive, but I don't know how to do this the intuitive way and I have other stuff to develop. If the shift key is pressed; if the latitude is changed more than the longitude, only the latitude will be changed, if the longitude is changed more only the longitude will be changed.
+
+

Number of Fill Layers

+ +

Number of Fill Bottom Layers

+ +Default is one.
+
+The "Number of Fill Bottom Layers" is the number of layers at the bottom which will be colored olive.
+
+

Number of Fill Top Layers

+ +Default is one.
+
+The "Number of Fill Top Layers" is the number of layers at the top which will be colored blue.
+
+

Scale

+ +Default is ten.
+
+The scale setting is the scale of the image in pixels per millimeter, the higher the number, the greater the size of the display.
+
+The zoom in mouse tool will zoom in the display at the point where the mouse was clicked, increasing the scale by a factor of two. The zoom out tool will zoom out the display at the point where the mouse was clicked, decreasing the scale by a factor of two.
+
+

Screen Inset

+ +

Screen Horizontal Inset

+ +Default is one hundred.
+
+The "Screen Horizontal Inset" determines how much the canvas will be inset in the horizontal direction from the edge of screen, the higher the number the more it will be inset and the smaller it will be.
+
+

Screen Vertical Inset

+ +Default is two hundred and twenty.
+
+The "Screen Vertical Inset" determines how much the canvas will be inset in the vertical direction from the edge of screen, the higher the number the more it will be inset and the smaller it will be..
+
+

Viewpoint

+ +

Viewpoint Latitude

+ +Default is fifteen degrees.
+
+The "Viewpoint Latitude" is the latitude of the viewpoint, a latitude of zero is the top pole giving a top view, a latitude of ninety gives a side view and a latitude of 180 gives a bottom view.
+
+

Viewpoint Longitude

+ +Default is 210 degrees.
+
+The "Viewpoint Longitude" is the longitude of the viewpoint.
+
+

Width

+ +The width of each type of thread and of each axis can be changed. If the width is set to zero, the thread will not be visible.
+
+

Width of Axis Negative Side

+ +Default is two.
+
+Defines the width of the negative side of the axis.
+
+

Width of Axis Positive Side

+ +Default is six.
+
+Defines the width of the positive side of the axis.
+
+

Width of Infill Thread

+ +Default is one.
+
+The "Width of Infill Thread" sets the width of the green extrusion threads, those threads which are not loops and not part of the raft.
+
+

Width of Fill Bottom Thread

+ +Default is two.
+
+The "Width of Fill Bottom Thread" sets the width of the olive extrusion threads at the bottom of the model.
+
+

Width of Fill Top Thread

+ +Default is two.
+
+The "Width of Fill Top Thread" sets the width of the blue extrusion threads at the top of the model.
+
+

Width of Loop Thread

+ +Default is three.
+
+The "Width of Loop Thread" sets the width of the yellow loop threads, which are not edges.
+
+

Width of Perimeter Inside Thread

+ +Default is eight.
+
+The "Width of Perimeter Inside Thread" sets the width of the orange inside edge threads.
+
+

Width of Perimeter Outside Thread

+ +Default is eight.
+
+The "Width of Perimeter Outside Thread" sets the width of the red outside edge threads.
+
+

Width of Raft Thread

+ +Default is one.
+
+The "Width of Raft Thread" sets the width of the brown raft threads.
+
+

Width of Selection Thread

+ +Default is six.
+
+The "Width of Selection Thread" sets the width of the selected line.
+
+

Width of Travel Thread

+ +Default is zero.
+
+The "Width of Travel Thread" sets the width of the grey extruder off travel threads.
+
+

Icons

+
+ +The dive, soar and zoom icons are from Mark James' soarSilk icon set 1.3 at:
+http://www.famfamfam.com/lab/icons/silk/
+
+

Gcodes

+
+ +An explanation of the gcodes is at:
+http://reprap.org/bin/view/Main/Arduino_GCode_Interpreter
+
+and at:
+http://reprap.org/bin/view/Main/MCodeReference
+
+A gode example is at:
+http://forums.reprap.org/file.php?12,file=565
+
+

Examples

+
+ +Below are examples of skeiniso being used. These examples are run in a terminal in the folder which contains Screw Holder_penultimate.gcode and skeiniso.py.
+
+> python skeiniso.py
+This brings up the skeiniso dialog.
+
+> python skeiniso.py Screw Holder_penultimate.gcode
+This brings up the skeiniso viewer to view the gcode file.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.display_line
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.gcodec
+math
+fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.tableau
+skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.view_move
+
skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.view_rotate
+

+ + + + + +
 
+Classes
       
+
skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.tableau.TableauRepository +
+
+
SkeinisoRepository +
+
+
skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.tableau.TableauWindow +
+
+
SkeinWindow +
+
+
Ruling +
SkeinPane +
SkeinisoSkein +
+

+ + + + + +
 
+class Ruling
    Methods defined here:
+
__init__(self, modelDistance, roundedRulingText)
Initialize the ruling.
+ +

+ + + + + + + +
 
+class SkeinPane
   A class to hold the colored lines for a layer.
 
 Methods defined here:
+
__init__(self, sequenceIndex)
Create empty line lists.
+ +

+ + + + + +
 
+class SkeinWindow(skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.tableau.TableauWindow)
    Methods defined here:
+
__init__(self, repository, skein)
Initialize the skein window.
+ +
drawRuling(self, projectiveSpace, relativeRulingEnd, ruling, tags, viewBegin, viewEnd)
Draw ruling.
+ +
drawRulings(self, axisLine, projectiveSpace, rulings)
Draw rulings for the axis line.
+ +
drawSkeinPane(self, projectiveSpace, skeinPane)
Draw colored lines.
+ +
drawXYAxisLines(self, projectiveSpace)
Draw the x and y axis lines.
+ +
drawZAxisLine(self, projectiveSpace)
Draw the z axis line.
+ +
getCanvasRadius(self)
Get half of the minimum of the canvas height and width.
+ +
getCentered(self, coordinate)
Get the centered coordinate.
+ +
getCenteredScreened(self, coordinate)
Get the normalized centered coordinate.
+ +
getColoredLines(self)
Get the colored lines from the skein pane.
+ +
getCopy(self)
Get a copy of this window.
+ +
getCopyWithNewSkein(self)
Get a copy of this window with a new skein.
+ +
getDrawnColoredLine(self, arrowType, coloredLine, projectiveSpace, tags, width)
Draw colored line.
+ +
getDrawnColoredLineMotion(self, coloredLine, projectiveSpace, width)
Draw colored line with motion stipple and tag.
+ +
getDrawnColoredLineWithoutArrow(self, coloredLine, projectiveSpace, tags, width)
Draw colored line without an arrow.
+ +
getDrawnColoredLines(self, coloredLines, projectiveSpace, width)
Draw colored lines.
+ +
getDrawnSelectedColoredLine(self, coloredLine)
Get the drawn selected colored line.
+ +
getScreenComplex(self, pointComplex)
Get the point in screen perspective.
+ +
getScreenView(self, point, projectiveSpace)
Get the point in screen view perspective.
+ +
printHexadecimalColorName(self, name)
Print the color name in hexadecimal.
+ +
update(self)
Update the screen.
+ +
+Methods inherited from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.tableau.TableauWindow:
+
activateMouseModeTool(self)
Activate the mouse mode tool.
+ +
addCanvasMenuRootScrollSkein(self, repository, skein, suffix, title)
Add the canvas, menu bar, scroll bar, skein panes, tableau repository, root and skein.
+ +
addLayer(self, gridPosition)
Add the layer frame items.
+ +
addLine(self, gridPosition)
Add the line frame items.
+ +
addMouseInstantTool(self, fileName, gridPosition, mouseInstantTool)
Add the mouse instant tool and derived photo button.
+ +
addMouseToolsBind(self)
Add the mouse tool and bind button one clicked, button one released and motion.
+ +
addPhotoImage(self, fileName, gridPosition)
Get a PhotoImage button, grid the button and increment the grid position.
+ +
addScale(self, gridPosition)
Add the line frame items.
+ +
addSettingsMenuSetWindowGeometry(self, center)
Add the settings menu, center the scroll region, update, and set the window geometry.
+ +
button1(self, event)
The button was clicked.
+ +
buttonRelease1(self, event)
The button was released.
+ +
cancel(self, event=None)
Set all entities to their saved state.
+ +
cancelTimer(self, event=None)
Cancel the timer and set it to none.
+ +
cancelTimerResetButtons(self)
Cancel the timer and set it to none.
+ +
close(self, event=None)
The dialog was closed.
+ +
createMouseModeTool(self)
Create the mouse mode tool.
+ +
destroyAllDialogWindows(self)
Destroy all the dialog windows.
+ +
destroyMouseToolRaiseMouseButtons(self)
Destroy the mouse tool and raise the mouse buttons.
+ +
dive(self)
Dive, go down periodically.
+ +
diveCycle(self)
Start the dive cycle.
+ +
getAnimationLineDelay(self, coloredLine)
Get the animation line delay in milliseconds.
+ +
getDrawnLineText(self, location, tags, text)
Get the line text drawn on the canvas.
+ +
getEntityFromName(self, name)
Get the entity of the given name.
+ +
getPhotoButtonGridIncrement(self, commandFunction, fileName, gridPosition)
Get a PhotoImage button, grid the button and increment the grid position.
+ +
getRoundedRulingText(self, extraDecimalPlaces, number)
Get the rounded ruling text.
+ +
getRulingSeparationWidthPixels(self, rank)
Get the separation width in pixels.
+ +
getScrollPaneCenter(self)
Get the center of the scroll pane.
+ +
getScrollPaneFraction(self)
Get the scroll pane top left.
+ +
getSlideShowDelay(self)
Get the slide show delay in milliseconds.
+ +
getUpdateSkeinPanes(self)
Get the update skein panes.
+ +
isLineBelowZeroSetLayer(self)
Determine if the line index is below zero, and if so set the layer index.
+ +
isLineBeyondListSetLayer(self)
Determine if the line index is beyond the end of the list, and if so set the layer index.
+ +
keyPressDown(self, event)
The down arrow was pressed.
+ +
keyPressLeft(self, event)
The left arrow was pressed.
+ +
keyPressReturn(self, event)
The return key was pressed.
+ +
keyPressRight(self, event)
The right arrow was pressed.
+ +
keyPressUp(self, event)
The up arrow was pressed.
+ +
layerEntryReturnPressed(self, event=None)
The layer index entry return was pressed.
+ +
limitIndex(self)
Limit the index so it is not below zero or above the top.
+ +
limitIndexSetArrowMouseDeleteCanvas(self)
Limit the index, set the arrow type, and delete all the canvas items.
+ +
lineDive(self)
Line dive, go down periodically.
+ +
lineDiveCycle(self)
Start the line dive cycle.
+ +
lineEntryReturnPressed(self, event=None)
The line index entry return was pressed.
+ +
lineSoar(self)
Line soar, go up periodically.
+ +
lineSoarCycle(self)
Start the line soar cycle.
+ +
motion(self, event)
The mouse moved.
+ +
phoenixUpdate(self)
Update the skein, and deiconify a new window and destroy the old.
+ +
redisplayWindowUpdate(self, event=None)
Deiconify a new window and destroy the old.
+ +
relayXview(self, *args)
Relay xview changes.
+ +
relayYview(self, *args)
Relay yview changes.
+ +
resetPeriodicButtonsText(self)
Reset the text of the periodic buttons.
+ +
save(self)
Set the setting values to the display, save the new values.
+ +
scaleEntryReturnPressed(self, event=None)
The scale entry return was pressed.
+ +
setButtonImageText(self, button, text)
Set the text of the e periodic buttons.
+ +
setDisplayLayerIndex(self)
Set the display of the layer index entry field and buttons.
+ +
setInsetToCanvas(self, event=None)
Set the repository insets to the canvas.
+ +
setLayerIndex(self, layerIndex)
Set the layer index.
+ +
setLineButtonsState(self)
Set the state of the line buttons.
+ +
setWindowNewMouseTool(self, getNewMouseToolFunction, mouseTool)
Set the getNewMouseTool function and the update function.
+ +
setWindowToDisplaySavePhoenixUpdate(self, event=None)
Set the setting values to the display, save the new values, then call the update function.
+ +
setWindowToDisplaySaveUpdate(self, event=None)
Set the setting values to the display, save the new values, then call the update function.
+ +
shiftButtonRelease1(self, event)
The button was released while the shift key was pressed.
+ +
shiftMotion(self, event)
The mouse moved.
+ +
soar(self)
Soar, go up periodically.
+ +
soarCycle(self)
Start the soar cycle.
+ +
updateDeiconify(self, center=(0.5+0.5j))
Update and deiconify the window.
+ +
updateMouseToolIfSelection(self)
Update the mouse tool if it is a selection tool.
+ +
updateNewDestroyOld(self, scrollPaneCenter)
Update and deiconify a window and destroy the old.
+ +

+ + + + + + + +
 
+class SkeinisoRepository(skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.tableau.TableauRepository)
   A class to handle the skeiniso settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Write button has been clicked.
+ +
+Methods inherited from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.tableau.TableauRepository:
+
addAnimation(self)
Add the animation settings.
+ +
addScaleScreenSlide(self)
Add the scale, screen and slide show settings.
+ +
setToDisplaySave(self, event=None)
Set the setting values to the display, save the new values.
+ +

+ + + + + + + +
 
+class SkeinisoSkein
   A class to write a get a scalable vector graphics text for a gcode skein.
 
 Methods defined here:
+
__init__(self)
+ +
addToPath(self, line, location)
Add a point to travel and maybe extrusion.
+ +
getLayerTop(self)
Get the layer top.
+ +
getLayerZoneIndex(self, z)
Get the layer zone index.
+ +
initializeActiveLocation(self)
Set variables to default.
+ +
linearCorner(self, splitLine)
Update the bounding corners.
+ +
linearMove(self, line, location)
Get statistics for a linear move.
+ +
moveColoredThreadToSkeinPane(self)
Move a colored thread to the skein pane.
+ +
parseCorner(self, line)
Parse a gcode line and use the location to update the bounding corners.
+ +
parseGcode(self, fileName, gcodeText, repository)
Parse gcode text and store the vector output.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the vector output.
+ +
setColoredLineColor(self, coloredLine, colorTuple)
Set the color and stipple of the colored line.
+ +
setColoredThread(self, colorTuple, lineList)
Set the colored thread, then move it to the line list and stipple of the colored line.
+ +

+ + + + + +
 
+Functions
       
compareLayerSequence(first, second)
Get comparison in order to sort skein panes in ascending order of layer zone index then sequence index.
+
getNewRepository()
Get new repository.
+
getWindowAnalyzeFile(fileName)
Skeiniso a gcode file.
+
getWindowAnalyzeFileGivenText(fileName, gcodeText, repository=None)
Display a skeiniso gcode file for a gcode file.
+
getWindowGivenTextRepository(fileName, gcodeText, repository)
Display the gcode text in a skeiniso viewer.
+
main()
Display the skeiniso dialog.
+
writeOutput(fileName, fileNamePenultimate, fileNameSuffix, filePenultimateWritten, gcodeText='')
Write a skeinisoed gcode file for a skeinforge gcode file, if 'Activate Skeiniso' is selected.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.skeinlayer.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.skeinlayer.html new file mode 100644 index 0000000..2415bda --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.skeinlayer.html @@ -0,0 +1,510 @@ + + +Python: module skeinforge_application.skeinforge_plugins.analyze_plugins.skeinlayer + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.analyze_plugins.skeinlayer ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/analyze_plugins/skeinlayer.py
+

+Previous / Next / Contents +

+


+Skeinlayer is an analyze viewer to display each layer of a gcode file.
+
+The skeinlayer manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Skeinlayer
+
+Skeinlayer is derived from Nophead's preview script. The extruded lines are in the resistor colors red, orange, yellow, green, blue, purple & brown. When the extruder is off, the travel line is grey. Skeinlayer is useful for a detailed view of the extrusion, skeiniso is better to see the orientation of the shape. To get an initial overview of the skein, when the skeinlayer display window appears, click the Soar button (double right arrow button beside the layer field).
+
+
+Operation
+Settings
+  Animation
+    Animation Line Quickening
+    Animation Slide Show Rate
+  Draw Arrows
+  Export Menu
+  Go Around Extruder Off Travel
+  Layers
+    Layer
+    Layer Extra Span
+  Line
+  Mouse Mode
+    Display Line
+    Viewpoint Move
+  Numeric Pointer
+  Scale
+  Screen Inset
+    Screen Horizontal Inset
+    Screen Vertical Inset
+  Width
+    Width of Extrusion Thread
+    Width of Selection Thread
+    Width of Travel Thread
+Icons
+Gcodes
+Examples
+
+

Operation

+
+ +The default 'Activate Skeinlayer' checkbox is on. When it is on, the functions described below will work when called from the skeinforge toolchain, when it is off, the functions will not be called from the toolchain. The functions will still be called, whether or not the 'Activate Skeinlayer' checkbox is on, when skeinlayer is run directly. Skeinlayer has trouble separating the layers when it reads gcode without comments.
+
+

Settings

+
+ +

Animation

+ +

Animation Line Quickening

+ +Default is one.
+
+The quickness of the tool animation over the quickness of the actual tool.
+
+

Animation Slide Show Rate

+ +Default is two layers per second.
+
+The rate, in layers per second, at which the layer changes when the soar or dive button is pressed..
+
+

Draw Arrows

+ +Default is on.
+
+When selected, arrows will be drawn at the end of each line segment.
+
+

Export Menu

+ +When the submenu in the export menu item in the file menu is clicked, an export canvas dialog will be displayed, which can export the canvas to a file.
+
+

Go Around Extruder Off Travel

+ +Default is off.
+
+When selected, the display will include the travel when the extruder is off, which means it will include the nozzle wipe path if any.
+
+

Layers

+ +

Layer

+ +Default is zero.
+
+On the display window, the Up button increases the 'Layer' by one, and the Down button decreases the layer by one. When the layer displayed in the layer spin box is changed then <Return> is hit, the layer shown will be set to the spin box, to a mimimum of zero and to a maximum of the highest index layer.The Soar button increases the layer at the 'Animation Slide Show Rate', and the Dive (double left arrow button beside the layer field) button decreases the layer at the slide show rate.
+
+

Layer Extra Span

+ +Default is zero.
+
+The viewer will draw the layers in the range including the 'Layer' index and the 'Layer' index plus the 'Layer Extra Span'. If the 'Layer Extra Span' is negative, the layers viewed will start at the 'Layer' index, plus the 'Layer Extra Span', and go up to and include the 'Layer' index. If the 'Layer Extra Span' is zero, only the 'Layer' index layer will be displayed. If the 'Layer Extra Span' is positive, the layers viewed will start at the 'Layer' index, and go up to and include the 'Layer' index plus the 'Layer Extra Span'.
+
+

Line

+ +Default is zero.
+
+The index of the selected line on the layer that is highlighted when the 'Display Line' mouse tool is chosen. The line spin box up button increases the 'Line' by one. If the line index of the layer goes over the index of the last line, the layer index will be increased by one and the new line index will be zero. The down button decreases the line index by one. If the line index goes below the index of the first line, the layer index will be decreased by one and the new line index will be at the last line. When the line displayed in the line field is changed then <Return> is hit, the line shown will be set to the line field, to a mimimum of zero and to a maximum of the highest index line. The Soar button increases the line at the speed at which the extruder would move, times the 'Animation Line Quickening' ratio, and the Dive (double left arrow button beside the line field) button decreases the line at the animation line quickening ratio.
+
+

Mouse Mode

+ +Default is 'Display Line'.
+
+The mouse tool can be changed from the 'Mouse Mode' menu button or picture button. The mouse tools listen to the arrow keys when the canvas has the focus. Clicking in the canvas gives the canvas the focus, and when the canvas has the focus a thick black border is drawn around the canvas.
+
+

Display Line

+ +The 'Display Line' tool will display the highlight the selected line, and display the file line count, counting from one, and the gcode line itself. When the 'Display Line' tool is active, clicking the canvas will select the closest line to the mouse click.
+
+

Viewpoint Move

+ +The 'Viewpoint Move' tool will move the viewpoint in the xy plane when the mouse is clicked and dragged on the canvas.
+
+

Numeric Pointer

+ +Default is on.
+
+When selected, the distance along the ruler of the arrow pointers will be drawn next to the pointers.
+
+

Scale

+ +Default is ten.
+
+The scale setting is the scale of the image in pixels per millimeter, the higher the number, the greater the size of the display.
+
+The zoom in mouse tool will zoom in the display at the point where the mouse was clicked, increasing the scale by a factor of two. The zoom out tool will zoom out the display at the point where the mouse was clicked, decreasing the scale by a factor of two.
+
+

Screen Inset

+ +

Screen Horizontal Inset

+ +Default is one hundred.
+
+The "Screen Horizontal Inset" determines how much the canvas will be inset in the horizontal direction from the edge of screen, the higher the number the more it will be inset and the smaller it will be.
+
+

Screen Vertical Inset

+ +Default is two hundred and twenty.
+
+The "Screen Vertical Inset" determines how much the canvas will be inset in the vertical direction from the edge of screen, the higher the number the more it will be inset and the smaller it will be.
+
+

Width

+ +The width of each type of thread and of each axis can be changed. If the width is set to zero, the thread will not be visible.
+
+

Width of Extrusion Thread

+ +Default is three.
+
+The "Width of Extrusion Thread" sets the width of the extrusion threads.
+
+

Width of Selection Thread

+ +Default is six.
+
+The "Width of Selection Thread" sets the width of the selected line.
+
+

Width of Travel Thread

+ +Default is one.
+
+The "Width of Travel Thread" sets the width of the grey extruder off travel threads.
+
+

Icons

+
+ +The dive, soar and zoom icons are from Mark James' soarSilk icon set 1.3 at:
+http://www.famfamfam.com/lab/icons/silk/
+
+

Gcodes

+
+ +An explanation of the gcodes is at:
+http://reprap.org/bin/view/Main/Arduino_GCode_Interpreter
+
+and at:
+http://reprap.org/bin/view/Main/MCodeReference
+
+A gode example is at:
+http://forums.reprap.org/file.php?12,file=565
+
+

Examples

+
+ +Below are examples of skeinlayer being used. These examples are run in a terminal in the folder which contains Screw Holder_penultimate.gcode and skeinlayer.py.
+
+> python skeinlayer.py
+This brings up the skeinlayer dialog.
+
+> python skeinlayer.py Screw Holder_penultimate.gcode
+This brings up the skeinlayer viewer to view each layer of a gcode file.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.display_line
+
fabmetheus_utilities.euclidean
+fabmetheus_utilities.gcodec
+os
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+
sys
+skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.tableau
+skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.view_move
+

+ + + + + +
 
+Classes
       
+
skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.tableau.TableauRepository +
+
+
SkeinlayerRepository +
+
+
skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.tableau.TableauWindow +
+
+
SkeinWindow +
+
+
SkeinlayerSkein +
+

+ + + + + +
 
+class SkeinWindow(skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.tableau.TableauWindow)
    Methods defined here:
+
__init__(self, repository, skein)
Initialize the skein window.setWindowNewMouseTool
+ +
addHorizontalRulerRuling(self, xMillimeters)
Add a ruling to the horizontal ruler.
+ +
addVerticalRulerRuling(self, yMillimeters)
Add a ruling to the vertical ruler.
+ +
createHorizontalLine(self, begin, yPixel)
Create a horizontal line for the horizontal ruler.
+ +
createRulers(self)
Create the rulers..
+ +
createVerticalLine(self, begin, xPixel)
Create a vertical line for the horizontal ruler.
+ +
getColoredLines(self)
Get the colored lines from the skein pane.
+ +
getCopy(self)
Get a copy of this window.
+ +
getCopyWithNewSkein(self)
Get a copy of this window with a new skein.
+ +
getDrawnColoredLine(self, coloredLine, tags, width)
Get the drawn colored line.
+ +
getDrawnColoredLineIfThick(self, coloredLine, width)
Get the drawn colored line if it has a positive thickness.
+ +
getDrawnSelectedColoredLine(self, coloredLine)
Get the drawn selected colored line.
+ +
motion(self, event)
The mouse moved.
+ +
qqqmotion(self, event)
The mouse moved.
+ +
relayXview(self, *args)
Relay xview changes.
+ +
relayYview(self, *args)
Relay yview changes.
+ +
update(self)
Update the window.
+ +
+Methods inherited from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.tableau.TableauWindow:
+
activateMouseModeTool(self)
Activate the mouse mode tool.
+ +
addCanvasMenuRootScrollSkein(self, repository, skein, suffix, title)
Add the canvas, menu bar, scroll bar, skein panes, tableau repository, root and skein.
+ +
addLayer(self, gridPosition)
Add the layer frame items.
+ +
addLine(self, gridPosition)
Add the line frame items.
+ +
addMouseInstantTool(self, fileName, gridPosition, mouseInstantTool)
Add the mouse instant tool and derived photo button.
+ +
addMouseToolsBind(self)
Add the mouse tool and bind button one clicked, button one released and motion.
+ +
addPhotoImage(self, fileName, gridPosition)
Get a PhotoImage button, grid the button and increment the grid position.
+ +
addScale(self, gridPosition)
Add the line frame items.
+ +
addSettingsMenuSetWindowGeometry(self, center)
Add the settings menu, center the scroll region, update, and set the window geometry.
+ +
button1(self, event)
The button was clicked.
+ +
buttonRelease1(self, event)
The button was released.
+ +
cancel(self, event=None)
Set all entities to their saved state.
+ +
cancelTimer(self, event=None)
Cancel the timer and set it to none.
+ +
cancelTimerResetButtons(self)
Cancel the timer and set it to none.
+ +
close(self, event=None)
The dialog was closed.
+ +
createMouseModeTool(self)
Create the mouse mode tool.
+ +
destroyAllDialogWindows(self)
Destroy all the dialog windows.
+ +
destroyMouseToolRaiseMouseButtons(self)
Destroy the mouse tool and raise the mouse buttons.
+ +
dive(self)
Dive, go down periodically.
+ +
diveCycle(self)
Start the dive cycle.
+ +
getAnimationLineDelay(self, coloredLine)
Get the animation line delay in milliseconds.
+ +
getDrawnLineText(self, location, tags, text)
Get the line text drawn on the canvas.
+ +
getEntityFromName(self, name)
Get the entity of the given name.
+ +
getPhotoButtonGridIncrement(self, commandFunction, fileName, gridPosition)
Get a PhotoImage button, grid the button and increment the grid position.
+ +
getRoundedRulingText(self, extraDecimalPlaces, number)
Get the rounded ruling text.
+ +
getRulingSeparationWidthPixels(self, rank)
Get the separation width in pixels.
+ +
getScrollPaneCenter(self)
Get the center of the scroll pane.
+ +
getScrollPaneFraction(self)
Get the scroll pane top left.
+ +
getSlideShowDelay(self)
Get the slide show delay in milliseconds.
+ +
getUpdateSkeinPanes(self)
Get the update skein panes.
+ +
isLineBelowZeroSetLayer(self)
Determine if the line index is below zero, and if so set the layer index.
+ +
isLineBeyondListSetLayer(self)
Determine if the line index is beyond the end of the list, and if so set the layer index.
+ +
keyPressDown(self, event)
The down arrow was pressed.
+ +
keyPressLeft(self, event)
The left arrow was pressed.
+ +
keyPressReturn(self, event)
The return key was pressed.
+ +
keyPressRight(self, event)
The right arrow was pressed.
+ +
keyPressUp(self, event)
The up arrow was pressed.
+ +
layerEntryReturnPressed(self, event=None)
The layer index entry return was pressed.
+ +
limitIndex(self)
Limit the index so it is not below zero or above the top.
+ +
limitIndexSetArrowMouseDeleteCanvas(self)
Limit the index, set the arrow type, and delete all the canvas items.
+ +
lineDive(self)
Line dive, go down periodically.
+ +
lineDiveCycle(self)
Start the line dive cycle.
+ +
lineEntryReturnPressed(self, event=None)
The line index entry return was pressed.
+ +
lineSoar(self)
Line soar, go up periodically.
+ +
lineSoarCycle(self)
Start the line soar cycle.
+ +
phoenixUpdate(self)
Update the skein, and deiconify a new window and destroy the old.
+ +
redisplayWindowUpdate(self, event=None)
Deiconify a new window and destroy the old.
+ +
resetPeriodicButtonsText(self)
Reset the text of the periodic buttons.
+ +
save(self)
Set the setting values to the display, save the new values.
+ +
scaleEntryReturnPressed(self, event=None)
The scale entry return was pressed.
+ +
setButtonImageText(self, button, text)
Set the text of the e periodic buttons.
+ +
setDisplayLayerIndex(self)
Set the display of the layer index entry field and buttons.
+ +
setInsetToCanvas(self, event=None)
Set the repository insets to the canvas.
+ +
setLayerIndex(self, layerIndex)
Set the layer index.
+ +
setLineButtonsState(self)
Set the state of the line buttons.
+ +
setWindowNewMouseTool(self, getNewMouseToolFunction, mouseTool)
Set the getNewMouseTool function and the update function.
+ +
setWindowToDisplaySavePhoenixUpdate(self, event=None)
Set the setting values to the display, save the new values, then call the update function.
+ +
setWindowToDisplaySaveUpdate(self, event=None)
Set the setting values to the display, save the new values, then call the update function.
+ +
shiftButtonRelease1(self, event)
The button was released while the shift key was pressed.
+ +
shiftMotion(self, event)
The mouse moved.
+ +
soar(self)
Soar, go up periodically.
+ +
soarCycle(self)
Start the soar cycle.
+ +
updateDeiconify(self, center=(0.5+0.5j))
Update and deiconify the window.
+ +
updateMouseToolIfSelection(self)
Update the mouse tool if it is a selection tool.
+ +
updateNewDestroyOld(self, scrollPaneCenter)
Update and deiconify a window and destroy the old.
+ +

+ + + + + + + +
 
+class SkeinlayerRepository(skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.tableau.TableauRepository)
   A class to handle the skeinlayer settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Write button has been clicked.
+ +
+Methods inherited from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.tableau.TableauRepository:
+
addAnimation(self)
Add the animation settings.
+ +
addScaleScreenSlide(self)
Add the scale, screen and slide show settings.
+ +
setToDisplaySave(self, event=None)
Set the setting values to the display, save the new values.
+ +

+ + + + + + + +
 
+class SkeinlayerSkein
   A class to write a get a scalable vector graphics text for a gcode skein.
 
 Methods defined here:
+
__init__(self)
Initialize.
+ +
addToPath(self, line, location)
Add a point to travel and maybe extrusion.
+ +
getModelCoordinates(self, screenCoordinates)
Get the model coordinates.
+ +
getScreenCoordinates(self, pointComplex)
Get the screen coordinates.
+ +
initializeActiveLocation(self)
Set variables to default.
+ +
linearCorner(self, splitLine)
Update the bounding corners.
+ +
linearMove(self, line, location)
Get statistics for a linear move.
+ +
parseCorner(self, line)
Parse a gcode line and use the location to update the bounding corners.
+ +
parseGcode(self, fileName, gcodeText, repository)
Parse gcode text and store the vector output.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the vector output.
+ +

+ + + + + +
 
+Functions
       
getNewRepository()
Get new repository.
+
getRankIndex(rulingSeparationWidthMillimeters, screenOrdinate)
Get rank index.
+
getWindowAnalyzeFile(fileName)
Display a gcode file in a skeinlayer window.
+
getWindowAnalyzeFileGivenText(fileName, gcodeText, repository=None)
Display a gcode file in a skeinlayer window given the text.
+
getWindowGivenTextRepository(fileName, gcodeText, repository)
Display a gcode file in a skeinlayer window given the text and settings.
+
main()
Display the skeinlayer dialog.
+
writeOutput(fileName, fileNamePenultimate, fileNameSuffix, filePenultimateWritten, gcodeText='')
Display a skeinlayered gcode file for a skeinforge gcode file, if 'Activate Skeinlayer' is selected.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.statistic.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.statistic.html new file mode 100644 index 0000000..bbc8d3c --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.statistic.html @@ -0,0 +1,239 @@ + + +Python: module skeinforge_application.skeinforge_plugins.analyze_plugins.statistic + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.analyze_plugins.statistic ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/analyze_plugins/statistic.py
+

+Previous / Next / Contents +

+


+Statistic is an extremely valuable analyze plugin to print and/or save the statistics of the generated gcode.
+
+The statistic manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Statistic
+
+
+Operation
+Settings
+  Extrusion Diameter over Thickness
+  Print Statistics
+  Save Statistics
+Gcodes
+Examples
+
+

Operation

+
+ +The default 'Activate Statistic' checkbox is on. When it is on, the functions described below will work when called from the skeinforge toolchain, when it is off, the functions will not be called from the toolchain. The functions will still be called, whether or not the 'Activate Statistic' checkbox is on, when statistic is run directly.
+
+

Settings

+
+ +

Extrusion Diameter over Thickness

+ +Default is 1.25.
+
+The 'Extrusion Diameter over Thickness is the ratio of the extrusion diameter over the layer height, the default is 1.25. The extrusion fill density ratio that is printed to the console, ( it is derived quantity not a parameter ) is the area of the extrusion diameter over the extrusion width over the layer height. Assuming the extrusion diameter is correct, a high value means the filament will be packed tightly, and the object will be almost as dense as the filament. If the fill density ratio is too high, there could be too little room for the filament, and the extruder will end up plowing through the extra filament. A low fill density ratio means the filaments will be far away from each other, the object will be leaky and light. The fill density ratio with the default extrusion settings is around 0.68.
+
+

Print Statistics

+ +Default is on.
+
+When the 'Print Statistics' checkbox is on, the statistics will be printed to the console.
+
+

Save Statistics

+ +Default is off.
+
+When the 'Save Statistics' checkbox is on, the statistics will be saved as a .txt file.
+
+

Gcodes

+
+ +An explanation of the gcodes is at:
+http://reprap.org/bin/view/Main/Arduino_GCode_Interpreter
+
+and at:
+http://reprap.org/bin/view/Main/MCodeReference
+
+A gode example is at:
+http://forums.reprap.org/file.php?12,file=565
+
+

Examples

+
+ +Below are examples of statistic being used. These examples are run in a terminal in the folder which contains Screw Holder_penultimate.gcode and statistic.py. The 'Save Statistics' checkbox is selected.
+
+> python statistic.py
+This brings up the statistic dialog.
+
+> python statistic.py Screw Holder_penultimate.gcode
+Statistics are being generated for the file /home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/models/Screw Holder_penultimate.gcode
+
+Cost
+Machine time cost is 0.31$.
+Material cost is 0.2$.
+Total cost is 0.51$.
+
+Extent
+X axis extrusion starts at 61 mm and ends at 127 mm, for a width of 65 mm.
+Y axis extrusion starts at 81 mm and ends at 127 mm, for a depth of 45 mm.
+Z axis extrusion starts at 0 mm and ends at 15 mm, for a height of 15 mm.
+
+Extruder
+Build time is 18 minutes 47 seconds.
+Distance extruded is 46558.4 mm.
+Distance traveled is 58503.3 mm.
+Extruder speed is 50.0
+Extruder was extruding 79.6 percent of the time.
+Extruder was toggled 1688 times.
+Operating flow rate is 9.8 mm3/s.
+Feed rate average is 51.9 mm/s, (3113.8 mm/min).
+
+Filament
+Cross section area is 0.2 mm2.
+Extrusion diameter is 0.5 mm.
+Extrusion fill density ratio is 0.68
+
+Material
+Mass extruded is 9.8 grams.
+Volume extruded is 9.1 cc.
+
+Meta
+Text has 33738 lines and a size of 1239.0 KB.
+Version is 11.09.28
+
+Procedures
+carve
+bottom
+preface
+inset
+fill
+multiply
+speed
+temperature
+raft
+skirt
+dimension
+bookend
+
+Profile
+UM-PLA-HighQuality
+
+Slice
+Edge width is 0.72 mm.
+Layer height is 0.4 mm.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+cStringIO
+
fabmetheus_utilities.euclidean
+fabmetheus_utilities.gcodec
+math
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+
sys
+

+ + + + + +
 
+Classes
       
+
StatisticRepository +
StatisticSkein +
+

+ + + + + + + +
 
+class StatisticRepository
   A class to handle the statistics settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Write button has been clicked.
+ +

+ + + + + + + +
 
+class StatisticSkein
   A class to get statistics for a gcode skein.
 
 Methods defined here:
+
__init__(self)
+ +
addLine(self, line)
Add a line of text and a newline to the output.
+ +
addToPath(self, location)
Add a point to travel and maybe extrusion.
+ +
extruderSet(self, active)
Maybe increment the number of times the extruder was toggled.
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the statistics.
+ +
getLocationSetFeedRateToSplitLine(self, splitLine)
Get location ans set feed rate to the plsit line.
+ +
helicalMove(self, isCounterclockwise, splitLine)
Get statistics for a helical move.
+ +
linearMove(self, splitLine)
Get statistics for a linear move.
+ +
parseLine(self, line)
Parse a gcode line and add it to the statistics.
+ +

+ + + + + +
 
+Functions
       
getNewRepository()
Get new repository.
+
getWindowAnalyzeFile(fileName)
Write statistics for a gcode file.
+
getWindowAnalyzeFileGivenText(fileName, gcodeText, repository=None)
Write statistics for a gcode file.
+
main()
Display the statistics dialog.
+
writeOutput(fileName, fileNamePenultimate, fileNameSuffix, filePenultimateWritten, gcodeText='')
Write statistics for a skeinforge gcode file, if 'Write Statistics File for Skeinforge Chain' is selected.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.synopsis.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.synopsis.html new file mode 100644 index 0000000..c283ec0 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.synopsis.html @@ -0,0 +1,179 @@ + + +Python: module skeinforge_application.skeinforge_plugins.analyze_plugins.synopsis + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.analyze_plugins.synopsis ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/analyze_plugins/synopsis.py
+

+Previous / Next / Contents +

+


+Synopsis is an analyze plugin to export the profile from a skeinforge gcode file as a csv or zip file.
+
+The synopsis manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Synopsis
+
+
+Operation
+Settings
+  Export Profile As CSV File
+  Export Profile As Zip File
+Examples
+
+

Operation

+
+ +The default 'Activate Synopsis' checkbox is off. When it is on, the functions described below will work when called from the skeinforge toolchain, when it is off, the functions will not be called from the toolchain. The functions will still be called, whether or not the 'Activate Synopsis' checkbox is on, when synopsis is run directly.
+
+

Settings

+
+ +

Export Profile As CSV File

+ +Default is on.
+
+If 'Export Profile As CSV File' is selected, the profile from a skeinforge gcode file with comments will be exported as a csv (comma separated values) file.
+
+

Export Profile As Zip File

+ +Default is off.
+
+If 'Export Profile As Zip File' is selected, the profile from a skeinforge gcode file with comments will be exported as a zip file.
+
+

Examples

+
+ +Below are examples of synopsis being used. These examples are run in a terminal in the folder which contains Screw Holder_penultimate.gcode and synopsis.py.
+
+> python synopsis.py
+This brings up the synopsis dialog.
+
+> python synopsis.py Screw Holder_penultimate.gcode
+The synopsis file is saved as Screw_Holder_penultimate_synopsis.csv
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.gcodec
+os
+fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+
time
+zipfile
+

+ + + + + +
 
+Classes
       
+
AbridgedSetting +
FileNamePath +
SynopsisRepository +
+

+ + + + + + + +
 
+class AbridgedSetting
   A class to handle an abridged setting.
 
 Methods defined here:
+
__init__(self, splitLine)
Initialize.
+ +
__repr__(self)
Get the tab separated representation of this AbridgedSetting.
+ +

+ + + + + + + +
 
+class FileNamePath
   A class to handle a file name and path.
 
 Methods defined here:
+
__init__(self, directoryName, fileName)
Initialize.
+ +
__repr__(self)
Get the tab separated representation of this FileNamePath.
+ +

+ + + + + + + +
 
+class SynopsisRepository
   A class to handle the synopsis settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Write button has been clicked.
+ +

+ + + + + +
 
+Functions
       
addAbridgedSettings(abridgedSettings, repositoryWriter)
Add the abridged settings to a repository writer.
+
exportProfileAsCSVFile(abridgedSettings, suffixFileNameWithoutExtension)
Export the profile from the gcode text as a csv file.
+
exportProfileAsZipFile(abridgedSettings, suffixDirectoryPath, suffixFileNameWithoutExtension)
Export the profile from the gcode text as a zip file.
+
getAbridgedSettings(gcodeText)
Get the abridged settings from the gcode text.
+
getNewRepository()
Get new repository.
+
getWindowAnalyzeFile(fileName)
Write scalable vector graphics for a gcode file.
+
getWindowAnalyzeFileGivenText(fileName, gcodeText, repository=None)
Write scalable vector graphics for a gcode file given the settings.
+
main()
Display the synopsis dialog.
+
writeOutput(fileName, fileNamePenultimate, fileNameSuffix, filePenultimateWritten, gcodeText='')
Write scalable vector graphics for a skeinforge gcode file, if activate synopsis is selected.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__credits__ = 'Gary Hodgson <http://garyhodgson.com/reprap/2011/06/hacking-skeinforge-export-module/>'
+__date__ = '$Date: 2008/21/04 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Gary Hodgson <http://garyhodgson.com/reprap/2011/06/hacking-skeinforge-export-module/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.vectorwrite.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.vectorwrite.html new file mode 100644 index 0000000..74adaa1 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.analyze_plugins.vectorwrite.html @@ -0,0 +1,282 @@ + + +Python: module skeinforge_application.skeinforge_plugins.analyze_plugins.vectorwrite + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.analyze_plugins.vectorwrite ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/analyze_plugins/vectorwrite.py
+

+Previous / Next / Contents +

+


+Vectorwrite is a very interesting analyze plugin that will create an SVG vector image for each layer that you can then use in some other printing system.
+
+The Scalable Vector Graphics file can be opened by an SVG viewer or an SVG capable browser like Mozilla:
+http://www.mozilla.com/firefox/
+
+The vectorwrite manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Vectorwrite
+
+
+Operation
+Settings
+  Add Loops
+  Add Paths
+  Add Perimeters
+  Layers
+    Layers From
+    Layers To
+  SVG Viewer
+Examples
+
+

Operation

+
+ +The default 'Activate Vectorwrite' checkbox is off. When it is on, the functions described below will work when called from the skeinforge toolchain, when it is off, the functions will not be called from the toolchain. The functions will still be called, whether or not the 'Activate Vectorwrite' checkbox is on, when vectorwrite is run directly.
+
+

Settings

+
+ +

Add Loops

+ +Default is on.
+
+If 'Add Loops' is selected, the loops will be added in yellow to the the scalable vector graphics output.
+
+

Add Paths

+ +Default is on.
+
+If 'Add Paths' is selected, the paths will be added in pink to the the scalable vector graphics output.
+
+

Add Perimeters

+ +Default is on.
+
+If 'Add Perimeters' is selected, the edges will be added to the the scalable vector graphics output. The outer edges will be red and the inner edges will be orange.
+
+

Layers

+ +

Layers From

+ +Default is zero.
+
+The "Layers From" is the index of the bottom layer that will be displayed. If the layer from is the default zero, the display will start from the lowest layer. If the the layer from index is negative, then the display will start from the layer from index below the top layer.
+
+

Layers To

+ +Default is a huge number, which will be limited to the highest index layer.
+
+The "Layers To" is the index of the top layer that will be displayed. If the layer to index is a huge number like the default, the display will go to the top of the model, at least until we model habitats:) If the layer to index is negative, then the display will go to the layer to index below the top layer. The layer from until layer to index is a python slice.
+
+

SVG Viewer

+ +Default is webbrowser.
+
+If the 'SVG Viewer' is set to the default 'webbrowser', the scalable vector graphics file will be sent to the default browser to be opened. If the 'SVG Viewer' is set to a program name, the scalable vector graphics file will be sent to that program to be opened.
+
+

Examples

+
+ +Below are examples of vectorwrite being used. These examples are run in a terminal in the folder which contains Screw Holder_penultimate.gcode and vectorwrite.py.
+
+> python vectorwrite.py
+This brings up the vectorwrite dialog.
+
+> python vectorwrite.py Screw Holder_penultimate.gcode
+The vectorwrite file is saved as Screw_Holder_penultimate_vectorwrite.svg
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+cStringIO
+
fabmetheus_utilities.euclidean
+fabmetheus_utilities.gcodec
+os
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+
fabmetheus_utilities.svg_writer
+sys
+time
+

+ + + + + +
 
+Classes
       
+
fabmetheus_utilities.svg_writer.SVGWriter +
+
+
SVGWriterVectorwrite +
+
+
ThreadLayer +
VectorwriteRepository +
VectorwriteSkein +
+

+ + + + + + + +
 
+class SVGWriterVectorwrite(fabmetheus_utilities.svg_writer.SVGWriter)
   A class to vectorwrite a carving.
 
 Methods defined here:
+
addLoopLayerToOutput(self, layerIndex, threadLayer)
Add rotated boundary layer to the output.
+ +
addPaths(self, colorName, paths, transformString)
Add paths to the output.
+ +
+Methods inherited from fabmetheus_utilities.svg_writer.SVGWriter:
+
__init__(self, addLayerTemplateToSVG, cornerMaximum, cornerMinimum, decimalPlacesCarried, layerHeight, edgeWidth=None)
Initialize.
+ +
addLayerBegin(self, layerIndex, loopLayer)
Add the start lines for the layer.
+ +
addLoopLayersToOutput(self, loopLayers)
Add rotated boundary layers to the output.
+ +
addOriginalAsComment(self, elementNode)
Add original elementNode as a comment.
+ +
getReplacedSVGTemplate(self, fileName, loopLayers, procedureName, elementNode=None)
Get the lines of text from the layer_template.svg file.
+ +
getRounded(self, number)
Get number rounded to the number of carried decimal places as a string.
+ +
getRoundedComplexString(self, point)
Get the rounded complex string.
+ +
getSVGStringForLoop(self, loop)
Get the svg loop string.
+ +
getSVGStringForLoops(self, loops)
Get the svg loops string.
+ +
getSVGStringForPath(self, path)
Get the svg path string.
+ +
getTransformString(self)
Get the svg transform string.
+ +
setDimensionTexts(self, key, valueString)
Set the texts to the valueString followed by mm.
+ +
setMetadataNoscriptElement(self, key, prefix, value)
Set the metadata value and the text.
+ +
setTexts(self, key, valueString)
Set the texts to the valueString.
+ +

+ + + + + + + +
 
+class ThreadLayer
   Threads with a z.
 
 Methods defined here:
+
__init__(self, z)
+ +
__repr__(self)
Get the string representation of this loop layer.
+ +
getTotalNumberOfThreads(self)
Get the total number of loops, paths and edges.
+ +
maximize(self, vector3)
Maximize the vector3 over the loops, paths and edges.
+ +
minimize(self, vector3)
Minimize the vector3 over the loops, paths and edges.
+ +

+ + + + + + + +
 
+class VectorwriteRepository
   A class to handle the vectorwrite settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Write button has been clicked.
+ +

+ + + + + + + +
 
+class VectorwriteSkein
   A class to vectorwrite a carving.
 
 Methods defined here:
+
__init__(self)
Initialize.
+ +
addLoopLayer(self, z)
Add loop layer.
+ +
addToLoops(self)
Add the thread to the loops.
+ +
addToPerimeters(self)
Add the thread to the edges.
+ +
getCarveLayerHeight(self)
Get the layer height.
+ +
getCarvedSVG(self, fileName, gcodeText, repository)
Parse gnu triangulated surface text and store the vectorwrite gcode.
+ +
linearMove(self, splitLine)
Get statistics for a linear move.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the outset skein.
+ +
removeEmptyLayers(self)
Remove empty layers.
+ +

+ + + + + +
 
+Functions
       
getNewRepository()
Get new repository.
+
getWindowAnalyzeFile(fileName)
Write scalable vector graphics for a gcode file.
+
getWindowAnalyzeFileGivenText(fileName, gcodeText, repository=None)
Write scalable vector graphics for a gcode file given the settings.
+
main()
Display the vectorwrite dialog.
+
writeOutput(fileName, fileNamePenultimate, fileNameSuffix, filePenultimateWritten, gcodeText='')
Write scalable vector graphics for a skeinforge gcode file, if activate vectorwrite is selected.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__credits__ = 'Nophead <http://hydraraptor.blogspot.com/>'
+__date__ = '$Date: 2008/21/04 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Nophead <http://hydraraptor.blogspot.com/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft.html new file mode 100644 index 0000000..56342d2 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft.html @@ -0,0 +1,116 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft.py
+

+Previous / Next / Contents +

+


+Craft is a script to access the plugins which craft a gcode file.
+
+The plugin buttons which are commonly used are bolded and the ones which are rarely used have normal font weight.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.gcodec
+os
+fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
CraftMenuSaveListener +
CraftRadioButtonsSaveListener +
+

+ + + + + + + +
 
+class CraftMenuSaveListener
   A class to update a craft menu.
 
 Methods defined here:
+
__init__(self, menu, window)
Set the menu.
+ +
save(self)
Profile has been saved and profile menu should be updated.
+ +

+ + + + + + + +
 
+class CraftRadioButtonsSaveListener
   A class to update the craft radio buttons.
 
 Methods defined here:
+
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
getFromRadioPlugins(self, radioPlugins, repository)
Initialize.
+ +
save(self)
Profile has been saved and craft radio plugins should be updated.
+ +
setRadioButtons(self)
Profile has been saved and craft radio plugins should be updated.
+ +

+ + + + + +
 
+Functions
       
addSubmenus(menu, pluginFileName, pluginFolderPath, pluginPath)
Add a tool plugin menu.
+
addToCraftMenu(menu)
Add a craft plugin menu.
+
addToMenu(master, menu, repository, window)
Add a tool plugin menu.
+
getNewRepository()
Get new repository.
+
main()
Display the craft dialog.
+
writeOutput(fileName)
Craft a gcode file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.alteration.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.alteration.html new file mode 100644 index 0000000..7cae1f5 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.alteration.html @@ -0,0 +1,207 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.alteration + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.alteration ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/alteration.py
+

+Previous / Next / Contents +

+


+The alteration plugin adds the start and end files to the gcode.
+
+This plugin also removes the alteration prefix tokens from the alteration lines. Alteration lines have a prefix token so they can go through the craft plugins without being modified. However, the tokens are not recognized by the firmware so they have to be removed before export. The alteration token is:
+(<alterationDeleteThisPrefix/>)
+
+The alteration manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Alteration
+
+
+Operation
+Settings
+  Name of End File
+  Name of Start File
+  Remove Redundant Mcode
+  Replace Variable with Setting
+Examples
+
+

Operation

+
+ +The default 'Activate Alteration' checkbox is on. When it is on, the functions described below will work, when it is off, nothing will be done.
+
+

Settings

+
+ +Alteration looks for alteration files in the alterations folder in the .skeinforge folder in the home directory. Alteration does not care if the text file names are capitalized, but some file systems do not handle file name cases properly, so to be on the safe side you should give them lower case names. If it doesn't find the file it then looks in the alterations folder in the skeinforge_plugins folder.
+
+

Name of End File

+ +Default is 'end.gcode'.
+
+If there is a file with the name of the "Name of End File" setting, it will be added to the very end of the gcode.
+
+

Name of Start File

+ +Default is 'start.gcode'.
+
+If there is a file with the name of the "Name of Start File" setting, it will be added to the very beginning of the gcode.
+
+

Remove Redundant Mcode

+ +Default: True
+
+If 'Remove Redundant Mcode' is selected then M104 and M108 lines which are followed by a different value before there is a movement will be removed. For example, if there is something like:
+M113 S1.0
+M104 S60.0
+(<layer> 0.72 )
+M104 S200.0
+(<skirt>)
+
+with Remove Redundant Mcode selected, that snippet would become:
+M113 S1.0
+M104 S200.0
+(<layer> 0.72 )
+(<skirt>)
+
+This is a relatively safe procedure, the only reason it is optional is because someone might make an alteration file which, for some unknown reason, requires the redundant mcode.
+
+

Replace Variable with Setting

+ +Default: True
+
+If 'Replace Variable with Setting' is selected and there is an alteration line with a setting token, the token will be replaced by the value.
+
+For example, if there is an alteration line like:
+
+M140 S<setting.chamber.BedTemperature>
+
+the token would be replaced with the value and assuming the bed chamber was 60.0, the output would be:
+
+M140 S60.0
+
+

Examples

+
+ +The following examples add the alteration information to the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and alteration.py.
+
+> python alteration.py
+This brings up the alteration dialog.
+
+> python alteration.py Screw Holder Bottom.stl
+The alteration tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The alteration tool has created the file:
+.. Screw Holder Bottom_alteration.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+cStringIO
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+
sys
+

+ + + + + +
 
+Classes
       
+
AlterationRepository +
AlterationSkein +
+

+ + + + + + + +
 
+class AlterationRepository
   A class to handle the alteration settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Alteration button has been clicked.
+ +

+ + + + + + + +
 
+class AlterationSkein
   A class to alteration a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
Initialize.
+ +
addFromUpperLowerFile(self, fileName)
Add lines of text from the fileName or the lowercase fileName, if there is no file by the original fileName in the directory.
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the bevel gcode.
+ +
getReplacedAlterationLine(self, alterationFileLine, searchIndex=0)
Get the alteration file line with variables replaced with the settings.
+ +
getReplacedAlterationText(self)
Replace the alteration lines if there are settings.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
setSettingDictionary(self)
Set the setting dictionary from the gcode text.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text='', repository=None)
Alteration a gcode linear move text.
+
getCraftedTextFromText(gcodeText, repository=None)
Alteration a gcode linear move text.
+
getGcodeTextWithoutRedundantMcode(gcodeText)
Get gcode text without redundant M104 and M108.
+
getLinesWithoutRedundancy(duplicateWord, lines)
Get gcode lines without redundant first words.
+
getNewRepository()
Get new repository.
+
main()
Display the alteration dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Alteration a gcode linear move file.  Chain alteration the gcode if the alteration procedure has not been done.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__date__ = '$Date: 2008/02/05 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.bottom.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.bottom.html new file mode 100644 index 0000000..8fecd93 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.bottom.html @@ -0,0 +1,165 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.bottom + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.bottom ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/bottom.py
+

+Previous / Next / Contents +

+


+Bottom sets the bottom of the carving to the defined altitude.
+
+The bottom manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Bottom
+
+
+Operation
+Settings
+  Additional Height over Layer Thickness
+  Altitude
+  SVG Viewer
+Examples
+
+

Operation

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

Settings

+
+ +

Additional Height over Layer Thickness

+ +Default is half.
+
+The layers will start at the altitude plus the 'Additional Height over Layer Thickness' times the layer height. The default value of half means that the bottom layer is at the height of the bottom slice, because each slice is made through the middle of each layer. Raft expects the layers to start at an additional half layer height. You should only change 'Additional Height over Layer Thickness' if you are manipulating the skeinforge output with your own program which does not use the raft tool.
+
+

Altitude

+ +Default is zero.
+
+Defines the altitude of the bottom of the model. The bottom slice has a z of the altitude plus the 'Additional Height over Layer Thickness' times the layer height.
+
+

SVG Viewer

+ +Default is webbrowser.
+
+If the 'SVG Viewer' is set to the default 'webbrowser', the scalable vector graphics file will be sent to the default browser to be opened. If the 'SVG Viewer' is set to a program name, the scalable vector graphics file will be sent to that program to be opened.
+
+

Examples

+
+ +The following examples bottom the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and bottom.py.
+
+> python bottom.py
+This brings up the bottom dialog.
+
+> python bottom.py Screw Holder Bottom.stl
+The bottom tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The bottom tool has created the file:
+.. Screw Holder Bottom_bottom.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+cStringIO
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+os
+fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+fabmetheus_utilities.svg_writer
+
sys
+time
+fabmetheus_utilities.xml_simple_writer
+

+ + + + + +
 
+Classes
       
+
BottomRepository +
BottomSkein +
+

+ + + + + + + +
 
+class BottomRepository
   A class to handle the bottom settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Bottom button has been clicked.
+ +

+ + + + + + + +
 
+class BottomSkein
   A class to bottom a skein of extrusions.
 
 Methods defined here:
+
getCraftedGcode(self, fileName, repository, svgText)
Parse svgText and store the bottom svgText.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, svgText='', repository=None)
Bottom and convert an svg file or svgText.
+
getCraftedTextFromText(fileName, svgText, repository=None)
Bottom and convert an svgText.
+
getNewRepository()
Get new repository.
+
main()
Display the bottom dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Bottom the carving.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__date__ = '$Date: 2008/02/05 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.carve.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.carve.html new file mode 100644 index 0000000..320fa3e --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.carve.html @@ -0,0 +1,228 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.carve + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.carve ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/carve.py
+

+Previous / Next / Contents +

+


+Carve is the most important plugin to define for your printer.
+
+It carves a shape into svg slice layers. It also sets the layer height and edge width for the rest of the tool chain.
+
+The carve manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Carve
+
+On the Arcol Blog a method of deriving the layer height is posted. That article "Machine Calibrating" is at:
+http://blog.arcol.hu/?p=157
+
+
+Settings
+  Add Layer Template to SVG
+  Edge Width over Height
+  Extra Decimal Places
+  Import Coarseness
+  Layer Height
+  Layers
+    Layers From
+    Layers To
+  Mesh Type
+    Correct Mesh
+    Unproven Mesh
+  SVG Viewer
+Examples
+
+

Settings

+
+ +

Add Layer Template to SVG

+ +Default is on.
+
+When selected, the layer template will be added to the svg output, which adds javascript control boxes. So 'Add Layer Template to SVG' should be selected when the svg will be viewed in a browser.
+
+When off, no controls will be added, the svg output will only include the fabrication paths. So 'Add Layer Template to SVG' should be deselected when the svg will be used by other software, like Inkscape.
+
+

Edge Width over Height

+ +Default is 1.8.
+
+Defines the ratio of the extrusion edge width to the layer height. This parameter tells skeinforge how wide the edge wall is expected to be in relation to the layer height. Default value of 1.8 for the default layer height of 0.4 states that a single filament edge wall should be 0.4 mm * 1.8 = 0.72 mm wide. The higher the value the more the edge will be inset. A ratio of one means the extrusion is a circle, the default ratio of 1.8 means the extrusion is a wide oval.
+
+This is an important value because if you are calibrating your machine you need to ensure that the speed of the head and the extrusion rate in combination produce a wall that is 'Layer Height' * 'Edge Width over Height' wide. To start with 'Edge Width over Height' is probably best left at the default of 1.8 and the extrusion rate adjusted to give the correct calculated wall thickness.
+
+Adjustment is in the 'Speed' section with 'Feed Rate' controlling speed of the head in X & Y and 'Flow Rate' controlling the extrusion rate. Initially it is probably easier to start adjusting the flow rate only a little at a time until you get a single filament of the correct width. If you change too many parameters at once you can get in a right mess.
+
+

Extra Decimal Places

+ +Default is two.
+
+Defines the number of extra decimal places export will output compared to the number of decimal places in the layer height. The higher the 'Extra Decimal Places', the more significant figures the output numbers will have.
+
+

Import Coarseness

+ +Default is one.
+
+When a triangle mesh has holes in it, the triangle mesh slicer switches over to a slow algorithm that spans gaps in the mesh. The higher the 'Import Coarseness' setting, the wider the gaps in the mesh it will span. An import coarseness of one means it will span gaps of the edge width.
+
+

Layer Height

+ +Default is 0.4 mm.
+
+Defines the the height of the layers skeinforge will cut your object into, in the z direction. This is the most important carve setting, many values in the toolchain are derived from the layer height.
+
+For a 0.5 mm nozzle usable values are 0.3 mm to 0.5 mm. Note; if you are using thinner layers make sure to adjust the extrusion speed as well.
+
+

Layers

+ +Carve slices from bottom to top. To get a single layer, set the "Layers From" to zero and the "Layers To" to one. The 'Layers From' until 'Layers To' range is a python slice.
+
+

Layers From

+ +Default is zero.
+
+Defines the index of the bottom layer that will be carved. If the 'Layers From' is the default zero, the carving will start from the lowest layer. If the 'Layers From' index is negative, then the carving will start from the 'Layers From' index below the top layer.
+
+For example if your object is 5 mm tall and your layer thicknes is 1 mm if you set layers from to 3 you will ignore the first 3 mm and start from 3 mm.
+
+

Layers To

+ +Default is a huge number, which will be limited to the highest index layer.
+
+Defines the index of the top layer that will be carved. If the 'Layers To' index is a huge number like the default, the carving will go to the top of the model. If the 'Layers To' index is negative, then the carving will go to the 'Layers To' index below the top layer.
+
+This is the same as layers from, only it defines when to end the generation of gcode.
+
+

Mesh Type

+ +Default is 'Correct Mesh'.
+
+

Correct Mesh

+ +When selected, the mesh will be accurately carved, and if a hole is found, carve will switch over to the algorithm that spans gaps.
+
+

Unproven Mesh

+ +When selected, carve will use the gap spanning algorithm from the start. The problem with the gap spanning algothm is that it will span gaps, even if there is not actually a gap in the model.
+
+

SVG Viewer

+ +Default is webbrowser.
+
+If the 'SVG Viewer' is set to the default 'webbrowser', the scalable vector graphics file will be sent to the default browser to be opened. If the 'SVG Viewer' is set to a program name, the scalable vector graphics file will be sent to that program to be opened.
+
+

Examples

+
+ +The following examples carve the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and carve.py.
+
+> python carve.py
+This brings up the carve dialog.
+
+> python carve.py Screw Holder Bottom.stl
+The carve tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The carve tool has created the file:
+.. Screw Holder Bottom_carve.svg
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+
fabmetheus_utilities.gcodec
+math
+os
+fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+fabmetheus_utilities.svg_writer
+sys
+
time
+

+ + + + + +
 
+Classes
       
+
CarveRepository +
CarveSkein +
+

+ + + + + + + +
 
+class CarveRepository
   A class to handle the carve settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Carve button has been clicked.
+ +

+ + + + + + + +
 
+class CarveSkein
   A class to carve a carving.
 
 Methods defined here:
+
getCarvedSVG(self, carving, fileName, repository)
Parse gnu triangulated surface text and store the carved gcode.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, gcodeText='', repository=None)
Get carved text.
+
getNewRepository()
Get new repository.
+
main()
Display the carve dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Carve a GNU Triangulated Surface file.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__date__ = '$Date: 2008/02/05 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.chamber.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.chamber.html new file mode 100644 index 0000000..c9b345f --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.chamber.html @@ -0,0 +1,308 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.chamber + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.chamber ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/chamber.py
+

+Previous / Next / Contents +

+


+Some filaments contract too much and warp the extruded object. To prevent this you have to print the object in a temperature regulated chamber and/or on a temperature regulated bed. The chamber tool allows you to control the bed and chamber temperature and the holding pressure.
+
+The chamber gcodes are also described at:
+
+http://reprap.org/wiki/Mendel_User_Manual:_RepRapGCodes
+
+The chamber manual page is at:
+
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Chamber
+
+
+Operation
+Settings
+  Bed
+    Bed Temperature
+    Bed Temperature Begin Change Height
+    Bed Temperature End Change Height
+    Bed Temperature End
+  Chamber Temperature
+  Holding Force
+Heated Beds
+  Bothacker
+  Domingo
+  Jmil
+  Metalab
+  Nophead
+  Prusajr
+  Zaggo
+Examples
+
+

Operation

+
+ +The default 'Activate Chamber' checkbox is on. When it is on, the functions described below will work, when it is off, nothing will be done.
+
+

Settings

+
+ +

Bed

+ +The initial bed temperature is defined by 'Bed Temperature'. If the 'Bed Temperature End Change Height' is greater or equal to the 'Bed Temperature Begin Change Height' and the 'Bed Temperature Begin Change Height' is greater or equal to zero, then the temperature will be ramped toward the 'Bed Temperature End'. The ramp will start once the extruder reaches the 'Bed Temperature Begin Change Height', then the bed temperature will approach the 'Bed Temperature End' as the extruder reaches the 'Bed Temperature End Change Height', finally the bed temperature will stay at the 'Bed Temperature End' for the remainder of the build.
+
+

Bed Temperature

+ +Default: 60C
+
+Defines the initial print bed temperature in Celcius by adding an M140 command.
+
+

Bed Temperature Begin Change Height

+ +Default: -1 mm
+
+Defines the height of the beginning of the temperature ramp. If the 'Bed Temperature End Change Height' is less than zero, the bed temperature will remain at the initial 'Bed Temperature'.
+
+

Bed Temperature End Change Height

+ +Default: -1 mm
+
+Defines the height of the end of the temperature ramp. If the 'Bed Temperature End Change Height' is less than zero or less than the 'Bed Temperature Begin Change Height', the bed temperature will remain at the initial 'Bed Temperature'.
+
+

Bed Temperature End

+ +Default: 20C
+
+Defines the end bed temperature if there is a temperature ramp.
+
+

Chamber Temperature

+ +Default: 30C
+
+Defines the chamber temperature in Celcius by adding an M141 command.
+
+

Holding Force

+ +Default: 0
+
+Defines the holding pressure of a mechanism, like a vacuum table or electromagnet, to hold the bed surface or object, by adding an M142 command. The holding pressure is in bars. For hardware which only has on/off holding, when the holding pressure is zero, turn off holding, when the holding pressure is greater than zero, turn on holding.
+
+

Heated Beds

+
+ +

Bothacker

+ +A resistor heated aluminum plate by Bothacker:
+
+http://bothacker.com
+
+with an article at:
+
+http://bothacker.com/2009/12/18/heated-build-platform/
+
+

Domingo

+ +A heated copper build plate by Domingo:
+
+http://casainho-emcrepstrap.blogspot.com/
+
+with articles at:
+
+http://casainho-emcrepstrap.blogspot.com/2010/01/first-time-with-pla-testing-it-also-on.html
+
+http://casainho-emcrepstrap.blogspot.com/2010/01/call-for-helpideas-to-develop-heated.html
+
+http://casainho-emcrepstrap.blogspot.com/2010/01/new-heated-build-platform.html
+
+http://casainho-emcrepstrap.blogspot.com/2010/01/no-acrylic-and-instead-kapton-tape-on.html
+
+http://casainho-emcrepstrap.blogspot.com/2010/01/problems-with-heated-build-platform-and.html
+
+http://casainho-emcrepstrap.blogspot.com/2010/01/perfect-build-platform.html
+
+http://casainho-emcrepstrap.blogspot.com/2009/12/almost-no-warp.html
+
+http://casainho-emcrepstrap.blogspot.com/2009/12/heated-base-plate.html
+
+

Jmil

+ +A heated build stage by jmil, over at:
+
+http://www.hive76.org
+
+with articles at:
+
+http://www.hive76.org/handling-hot-build-surfaces
+
+http://www.hive76.org/heated-build-stage-success
+
+

Metalab

+ +A heated base by the Metalab folks:
+
+http://reprap.soup.io
+
+with information at:
+
+http://reprap.soup.io/?search=heated%20base
+
+

Nophead

+ +A resistor heated aluminum bed by Nophead:
+
+http://hydraraptor.blogspot.com
+
+with articles at:
+
+http://hydraraptor.blogspot.com/2010/01/will-it-stick.html
+
+http://hydraraptor.blogspot.com/2010/01/hot-metal-and-serendipity.html
+
+http://hydraraptor.blogspot.com/2010/01/new-year-new-plastic.html
+
+http://hydraraptor.blogspot.com/2010/01/hot-bed.html
+
+

Prusajr

+ +A resistive wire heated plexiglass plate by prusajr:
+
+http://prusadjs.cz/
+
+with articles at:
+
+http://prusadjs.cz/2010/01/heated-reprap-print-bed-mk2/
+
+http://prusadjs.cz/2009/11/look-ma-no-warping-heated-reprap-print-bed/
+
+

Zaggo

+ +A resistor heated aluminum plate by Zaggo at Pleasant Software:
+
+http://pleasantsoftware.com/developer/3d/
+
+with articles at:
+
+http://pleasantsoftware.com/developer/3d/2009/12/05/raftless/
+
+http://pleasantsoftware.com/developer/3d/2009/11/15/living-in-times-of-warp-free-printing/
+
+http://pleasantsoftware.com/developer/3d/2009/11/12/canned-heat/
+
+

Examples

+
+ +The following examples chamber the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and chamber.py.
+
+> python chamber.py
+This brings up the chamber dialog.
+
+> python chamber.py Screw Holder Bottom.stl
+The chamber tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The chamber tool has created the file:
+Screw Holder Bottom_chamber.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+
sys
+

+ + + + + +
 
+Classes
       
+
ChamberRepository +
ChamberSkein +
+

+ + + + + + + +
 
+class ChamberRepository
   A class to handle the chamber settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Chamber button has been clicked.
+ +

+ + + + + + + +
 
+class ChamberSkein
   A class to chamber a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
Initialize.
+ +
addBedTemperature(self, bedTemperature)
Add bed temperature if it is different from the old.
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the chamber gcode.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the chamber skein.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text='', repository=None)
Chamber the file or text.
+
getCraftedTextFromText(gcodeText, repository=None)
Chamber a gcode linear move text.
+
getNewRepository()
Get new repository.
+
main()
Display the chamber dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Chamber a gcode linear move file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.chop.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.chop.html new file mode 100644 index 0000000..1e6bada --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.chop.html @@ -0,0 +1,219 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.chop + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.chop ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/chop.py
+

+Previous / Next / Contents +

+


+Chop is a script to chop a shape into svg slice layers.
+
+
+Settings
+  Add Layer Template to SVG
+  Add Extra Top Layer if Necessary
+  Extra Decimal Places
+  Import Coarseness
+  Layer Height
+  Layers
+    Layers From
+    Layers To
+  Mesh Type
+    Correct Mesh
+    Unproven Mesh
+  Perimeter Width
+  SVG Viewer
+Examples
+
+

Settings

+
+ +

Add Layer Template to SVG

+ +Default is on.
+
+When selected, the layer template will be added to the svg output, which adds javascript control boxes. So 'Add Layer Template to SVG' should be selected when the svg will be viewed in a browser.
+
+When off, no controls will be added, the svg output will only include the fabrication paths. So 'Add Layer Template to SVG' should be deselected when the svg will be used by other software, like Inkscape.
+
+

Add Extra Top Layer if Necessary

+ +Default is on.
+
+When selected, chop will add an extra layer at the very top of the object if the top of the object is more than half the layer height above the first slice. This is so the cutting tool doesn't cut too deeply through the top of the object on its first pass.
+
+

Extra Decimal Places

+ +Default is two.
+
+Defines the number of extra decimal places export will output compared to the number of decimal places in the layer height. The higher the 'Extra Decimal Places', the more significant figures the output numbers will have.
+
+

Import Coarseness

+ +Default is one.
+
+When a triangle mesh has holes in it, the triangle mesh slicer switches over to a slow algorithm that spans gaps in the mesh. The higher the 'Import Coarseness' setting, the wider the gaps in the mesh it will span. An import coarseness of one means it will span gaps of the edge width.
+
+

Layer Height

+ +Default is 0.4 mm.
+
+Defines the height of the layer, this is the most important chop setting.
+
+

Layers

+ +Chop slices from top to bottom. To get only the bottom layer, set the "Layers From" to minus one. The 'Layers From' until 'Layers To' range is a python slice.
+
+

Layers From

+ +Default is zero.
+
+Defines the index of the top layer that will be chopped. If the 'Layers From' is the default zero, the carving will start from the top layer. If the 'Layers From' index is negative, then the carving will start from the 'Layers From' index above the bottom layer.
+
+

Layers To

+ +Default is a huge number, which will be limited to the highest index number.
+
+Defines the index of the bottom layer that will be chopped. If the 'Layers To' index is a huge number like the default, the carving will go to the bottom of the model. If the 'Layers To' index is negative, then the carving will go to the 'Layers To' index above the bottom layer.
+
+

Mesh Type

+ +Default is 'Correct Mesh'.
+
+

Correct Mesh

+ +When selected, the mesh will be accurately chopped, and if a hole is found, chop will switch over to the algorithm that spans gaps.
+
+

Unproven Mesh

+ +When selected, chop will use the gap spanning algorithm from the start. The problem with the gap spanning algothm is that it will span gaps, even if there is not actually a gap in the model.
+
+

Perimeter Width

+ +Default is 2 mm.
+
+Defines the width of the edge.
+
+

SVG Viewer

+ +Default is webbrowser.
+
+If the 'SVG Viewer' is set to the default 'webbrowser', the scalable vector graphics file will be sent to the default browser to be opened. If the 'SVG Viewer' is set to a program name, the scalable vector graphics file will be sent to that program to be opened.
+
+

Examples

+
+ +The following examples chop the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and chop.py.
+
+> python chop.py
+This brings up the chop dialog.
+
+> python chop.py Screw Holder Bottom.stl
+The chop tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The chop tool has created the file:
+.. Screw Holder Bottom_chop.svg
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+
fabmetheus_utilities.gcodec
+math
+os
+fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+fabmetheus_utilities.svg_writer
+sys
+
time
+

+ + + + + +
 
+Classes
       
+
ChopRepository +
ChopSkein +
+

+ + + + + + + +
 
+class ChopRepository
   A class to handle the chop settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Chop button has been clicked.
+ +

+ + + + + + + +
 
+class ChopSkein
   A class to chop a carving.
 
 Methods defined here:
+
addExtraTopLayerIfNecessary(self, carving, layerHeight, loopLayers)
Add extra top layer if necessary.
+ +
getCarvedSVG(self, carving, fileName, repository)
Parse gnu triangulated surface text and store the chopped gcode.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, gcodeText='', repository=None)
Get chopped text.
+
getNewRepository()
Get new repository.
+
main()
Display the chop dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Chop a GNU Triangulated Surface file.  If no fileName is specified, chop the first GNU Triangulated Surface file in this folder.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__date__ = '$Date: 2008/02/05 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.cleave.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.cleave.html new file mode 100644 index 0000000..660afc5 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.cleave.html @@ -0,0 +1,210 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.cleave + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.cleave ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/cleave.py
+

+Previous / Next / Contents +

+


+Cleave is a script to cleave a shape into svg slice layers.
+
+
+Settings
+  Add Layer Template to SVG
+  Extra Decimal Places
+  Import Coarseness
+  Layer Height
+  Layers
+    Layers From
+    Layers To
+  Mesh Type
+    Correct Mesh
+    Unproven Mesh
+  Perimeter Width
+  SVG Viewer
+Examples
+
+

Settings

+
+ +

Add Layer Template to SVG

+ +Default is on.
+
+When selected, the layer template will be added to the svg output, which adds javascript control boxes. So 'Add Layer Template to SVG' should be selected when the svg will be viewed in a browser.
+
+When off, no controls will be added, the svg output will only include the fabrication paths. So 'Add Layer Template to SVG' should be deselected when the svg will be used by other software, like Inkscape.
+
+

Extra Decimal Places

+ +Default is two.
+
+Defines the number of extra decimal places export will output compared to the number of decimal places in the layer height. The higher the 'Extra Decimal Places', the more significant figures the output numbers will have.
+
+

Import Coarseness

+ +Default is one.
+
+When a triangle mesh has holes in it, the triangle mesh slicer switches over to a slow algorithm that spans gaps in the mesh. The higher the 'Import Coarseness' setting, the wider the gaps in the mesh it will span. An import coarseness of one means it will span gaps of the edge width.
+
+

Layer Height

+ +Default is 0.4 mm.
+
+Defines the height of the layer, this is the most important cleave setting.
+
+

Layers

+ +Cleave slices from bottom to top. To get a single layer, set the "Layers From" to zero and the "Layers To" to one. The layer from until layer to range is a python slice.
+
+

Layers From

+ +Default is zero.
+
+Defines the index of the bottom layer that will be cleaved. If the layer from is the default zero, the carving will start from the lowest layer. If the 'Layers From' index is negative, then the carving will start from the 'Layers From' index below the top layer.
+
+

Layers To

+ +Default is a huge number, which will be limited to the highest index layer.
+
+Defines the index of the top layer that will be cleaved. If the 'Layers To' index is a huge number like the default, the carving will go to the top of the model. If the 'Layers To' index is negative, then the carving will go to the 'Layers To' index below the top layer.
+
+

Mesh Type

+ +Default is 'Correct Mesh'.
+
+

Correct Mesh

+ +When selected, the mesh will be accurately cleaved, and if a hole is found, cleave will switch over to the algorithm that spans gaps.
+
+

Unproven Mesh

+ +When selected, cleave will use the gap spanning algorithm from the start. The problem with the gap spanning algothm is that it will span gaps, even if there is not actually a gap in the model.
+
+

Perimeter Width

+ +Default is two millimeters.
+
+Defines the width of the edge.
+
+

SVG Viewer

+ +Default is webbrowser.
+
+If the 'SVG Viewer' is set to the default 'webbrowser', the scalable vector graphics file will be sent to the default browser to be opened. If the 'SVG Viewer' is set to a program name, the scalable vector graphics file will be sent to that program to be opened.
+
+

Examples

+
+ +The following examples cleave the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and cleave.py.
+
+> python cleave.py
+This brings up the cleave dialog.
+
+> python cleave.py Screw Holder Bottom.stl
+The cleave tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The cleave tool has created the file:
+.. Screw Holder Bottom_cleave.svg
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+
fabmetheus_utilities.gcodec
+math
+os
+fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+fabmetheus_utilities.svg_writer
+sys
+
time
+

+ + + + + +
 
+Classes
       
+
CleaveRepository +
CleaveSkein +
+

+ + + + + + + +
 
+class CleaveRepository
   A class to handle the cleave settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Cleave button has been clicked.
+ +

+ + + + + + + +
 
+class CleaveSkein
   A class to cleave a carving.
 
 Methods defined here:
+
getCarvedSVG(self, carving, fileName, repository)
Parse gnu triangulated surface text and store the cleaved gcode.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, gcodeText='', repository=None)
Get cleaved text.
+
getNewRepository()
Get new repository.
+
main()
Display the cleave dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Cleave a GNU Triangulated Surface file.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__date__ = '$Date: 2008/02/05 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.clip.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.clip.html new file mode 100644 index 0000000..559506d --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.clip.html @@ -0,0 +1,183 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.clip + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.clip ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/clip.py
+

+Previous / Next / Contents +

+


+The clip plugin clips the loop ends to prevent bumps from forming, and connects loops.
+
+The clip manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Clip
+
+
+Operation
+Settings
+  Clip Over Perimeter Width
+  Maximum Connection Distance Over Perimeter Width
+Examples
+
+

Operation

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

Settings

+
+ +

Clip Over Perimeter Width

+ +Default is 0.2.
+
+Defines the ratio of the amount each end of the loop is clipped over the edge width. The total gap will therefore be twice the clip. If the ratio is too high loops will have a gap, if the ratio is too low there will be a bulge at the loop ends.
+
+This setting will affect the output of clip, and the output of the skin. In skin the half width edges will be clipped by according to this setting.
+
+

Maximum Connection Distance Over Perimeter Width

+ +Default is ten.
+
+Defines the ratio of the maximum connection distance between loops over the edge width.
+
+Clip will attempt to connect loops that end close to each other, combining them into a spiral, so that the extruder does not stop and restart. This setting sets the maximum gap size to connect. This feature can reduce the amount of extra material or gaps formed at the loop end.
+
+Setting this to zero disables this feature, preventing the loops from being connected.
+
+

Examples

+
+ +The following examples clip the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and clip.py.
+
+> python clip.py
+This brings up the clip dialog.
+
+> python clip.py Screw Holder Bottom.stl
+The clip tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The clip tool has created the file:
+.. Screw Holder Bottom_clip.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+
math
+fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+
skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
ClipRepository +
ClipSkein +
+

+ + + + + + + +
 
+class ClipRepository
   A class to handle the clip settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Clip button has been clicked.
+ +

+ + + + + + + +
 
+class ClipSkein
   A class to clip a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
addGcodeFromThreadZ(self, thread, z)
Add a gcode thread to the output.
+ +
addSegmentToPixelTables(self, location, oldLocation)
Add the segment to the layer and mask table.
+ +
addTailoredLoopPath(self, line)
Add a clipped loop path.
+ +
getConnectionIsCloseWithoutOverlap(self, location, path)
Determine if the connection is close enough and does not overlap another thread.
+ +
getCraftedGcode(self, clipRepository, gcodeText)
Parse gcode text and store the clip gcode.
+ +
getNextThreadIsACloseLoop(self, path)
Determine if the next thread is a loop.
+ +
isNextExtruderOn(self)
Determine if there is an extruder on command before a move command.
+ +
linearMove(self, splitLine)
Add to loop path if this is a loop or path.
+ +
parseInitialization(self, clipRepository)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the clip skein.
+ +
setLayerPixelTable(self)
Set the layer pixel table.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text, clipRepository=None)
Clip a gcode linear move file or text.
+
getCraftedTextFromText(gcodeText, clipRepository=None)
Clip a gcode linear move text.
+
getNewRepository()
Get new repository.
+
main()
Display the clip dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Clip a gcode linear move file.  Chain clip the gcode if it is not already clipped.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.coil.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.coil.html new file mode 100644 index 0000000..68221a5 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.coil.html @@ -0,0 +1,162 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.coil + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.coil ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/coil.py
+

+Previous / Next / Contents +

+


+Coil is a script to coil wire or filament around an object.
+
+
+Operation
+Settings
+  Minimum Tool Distance
+Examples
+
+

Operation

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

Settings

+
+ +

Minimum Tool Distance

+ +Default is twenty millimeters.
+
+Defines the minimum distance between the wire dispenser and the object. The 'Minimum Tool Distance' should be set to the maximum radius of the wire dispenser, times at least 1.3 to get a reasonable safety margin.
+
+

Examples

+
+ +The following examples coil the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and coil.py.
+
+> python coil.py
+This brings up the coil dialog.
+
+> python coil.py Screw Holder Bottom.stl
+The coil tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The coil tool has created the file:
+Screw Holder Bottom_coil.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+
fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+os
+fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+
fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
CoilRepository +
CoilSkein +
+

+ + + + + + + +
 
+class CoilRepository
   A class to handle the coil settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Coil button has been clicked.
+ +

+ + + + + + + +
 
+class CoilSkein
   A class to coil a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
addCoilLayer(self, boundaryLayers, radius, z)
Add a coil layer.
+ +
addCoilLayers(self)
Add the coil layers.
+ +
addCoilToThread(self, beginLocation, endZ, loop, thread)
Add a coil to the thread.
+ +
addGcodeFromThread(self, thread)
Add a thread to the output.
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the coil gcode.
+ +
parseBoundaries(self)
Parse the boundaries and add them to the boundary layers.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseUntilLayer(self)
Parse until the layer line and add it to the coil skein.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, gcodeText='', repository=None)
Coil the file or gcodeText.
+
getCraftedTextFromText(gcodeText, repository=None)
Coil a gcode linear move gcodeText.
+
getNewRepository()
Get new repository.
+
main()
Display the coil dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Coil a gcode linear move file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.comb.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.comb.html new file mode 100644 index 0000000..0d969ed --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.comb.html @@ -0,0 +1,211 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.comb + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.comb ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/comb.py
+

+Previous / Next / Contents +

+


+Comb is a craft plugin to bend the extruder travel paths around holes in the slices, to avoid stringers.
+
+The comb manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Comb
+
+
+Operation
+Settings
+  Running Jump Space
+Examples
+
+

Operation

+
+ +The default 'Activate Comb' checkbox is off. When it is on, the functions described below will work, when it is off, nothing will be done.
+
+

Settings

+
+ +

Running Jump Space

+ +Default: 2 mm
+
+Defines the running jump space that is added before going from one island to another. If the running jump space is greater than zero, the departure from the island will also be brought closer to the arrival point on the next island so that the stringer between islands will be shorter. For an extruder with acceleration code, an extra space before leaving the island means that it will be going at high speed as it exits the island, which means the stringer between islands will be thinner.
+
+

Examples

+
+ +The following examples comb the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and comb.py.
+
+> python comb.py
+This brings up the comb dialog.
+
+> python comb.py Screw Holder Bottom.stl
+The comb tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The comb tool has created the file:
+.. Screw Holder Bottom_comb.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+
math
+fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+
skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
BoundarySegment +
CombRepository +
CombSkein +
DistancePoint +
+

+ + + + + + + +
 
+class BoundarySegment
   A boundary and segment.
 
 Methods defined here:
+
__init__(self, begin)
Initialize
+ +
getSegment(self, boundarySegmentIndex, boundarySegments, edgeWidth, runningJumpSpace)
Get both paths along the loop from the point closest to the begin to the point closest to the end.
+ +

+ + + + + + + +
 
+class CombRepository
   A class to handle the comb settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Comb button has been clicked.
+ +

+ + + + + + + +
 
+class CombSkein
   A class to comb a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
Initialize
+ +
addGcodePathZ(self, feedRateMinute, path, z)
Add a gcode path, without modifying the extruder, to the output.
+ +
addIfTravel(self, splitLine)
Add travel move around loops if the extruder is off.
+ +
addToLoop(self, location)
Add a location to loop.
+ +
getAroundBetweenLineSegment(self, begin, boundaries, end)
Get the path around the loops in the way of the original line segment.
+ +
getAroundBetweenPath(self, begin, end)
Get the path around the loops in the way of the original line segment.
+ +
getBetweens(self)
Get betweens for the layer.
+ +
getBoundaries(self)
Get boundaries for the layer.
+ +
getBoundaryIndexes(self, begin, boundaries, end, points)
Get boundary indexes and set the points in the way of the original line segment.
+ +
getBoundarySegments(self, begin, boundaries, end)
Get the path broken into boundary segments whenever a different boundary is crossed.
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the comb gcode.
+ +
getInsidePointsAlong(self, begin, end, points)
Get the points along the segment if it is required to keep the path inside the widdershin boundaries.
+ +
getPathBetween(self, loop, points)
Add a path between the edge and the fill.
+ +
getWiddershins(self)
Get widdershins for the layer.
+ +
parseBoundariesLayers(self, line)
Parse a gcode line.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the comb skein.
+ +

+ + + + + + + +
 
+class DistancePoint
   A class to get the distance of the point along a segment inside a loop.
 
 Methods defined here:
+
__init__(self, begin, loop, runningJumpSpace, segment)
Initialize
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text, repository=None)
Comb a gcode linear move text.
+
getCraftedTextFromText(gcodeText, repository=None)
Comb a gcode linear move text.
+
getJumpPoint(begin, end, loop, runningJumpSpace)
Get running jump point inside loop.
+
getJumpPointIfInside(boundary, otherPoint, edgeWidth, runningJumpSpace)
Get the jump point if it is inside the boundary, otherwise return None.
+
getNewRepository()
Get new repository.
+
getPathsByIntersectedLoop(begin, end, loop)
Get both paths along the loop from the point closest to the begin to the point closest to the end.
+
main()
Display the comb dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Comb a gcode linear move file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.cool.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.cool.html new file mode 100644 index 0000000..3107216 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.cool.html @@ -0,0 +1,258 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.cool + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.cool ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py
+

+Previous / Next / Contents +

+


+Cool is a craft tool to cool the shape.
+
+Cool works well with a stepper extruder, it does not work well with a DC motor extruder.
+
+If enabled, before each layer that takes less then "Minimum Layer Time" to print the tool head will orbit around the printed area for 'Minimum Layer Time' minus 'the time it takes to print the layer' before it starts printing the layer. This is great way to let layers with smaller area cool before you start printing on top of them (so you do not overheat the area).
+
+The cool manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Cool
+
+Allan Ecker aka The Masked Retriever's has written the "Skeinforge Quicktip: Cool" at:
+http://blog.thingiverse.com/2009/07/28/skeinforge-quicktip-cool/
+
+
+Operation
+Settings
+  Bridge Cool
+  Cool Type
+    Orbit
+    Slow Down
+  Maximum Cool
+  Minimum Layer Time
+  Minimum Orbital Radius
+  Name of Alteration Files
+    Name of Cool End File
+    Name of Cool Start File
+  Orbital Outset
+  Turn Fan On at Beginning
+  Turn Fan Off at Ending
+Examples
+
+

Operation

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

Settings

+
+ +

Bridge Cool

+ +Default is one degree Celcius.
+
+If the layer is a bridge layer, then cool will lower the temperature by 'Bridge Cool' degrees Celcius.
+
+

Cool Type

+ +Default is 'Slow Down'.
+
+

Orbit

+ +When selected, cool will add orbits with the extruder off to give the layer time to cool, so that the next layer is not extruded on a molten base. The orbits will be around the largest island on that layer. Orbit should only be chosen if you can not upgrade to a stepper extruder.
+
+

Slow Down

+ +When selected, cool will slow down the extruder so that it will take the minimum layer time to extrude the layer. DC motors do not operate properly at slow flow rates, so if you have a DC motor extruder, you should upgrade to a stepper extruder, but if you can't do that, you can try using the 'Orbit' option.
+
+

Maximum Cool

+ +Default is 2 degrees Celcius.
+
+If it takes less time to extrude the layer than the minimum layer time, then cool will lower the temperature by the 'Maximum Cool' setting times the layer time over the minimum layer time.
+
+

Minimum Layer Time

+ +Default is 60 seconds.
+
+Defines the minimum amount of time the extruder will spend on a layer, this is an important setting.
+
+

Minimum Orbital Radius

+ +Default is 10 millimeters.
+
+When the orbit cool type is selected, if the area of the largest island is as large as the square of the "Minimum Orbital Radius" then the orbits will be just within the island. If the island is smaller, then the orbits will be in a square of the "Minimum Orbital Radius" around the center of the island. This is so that the hot extruder does not stay too close to small islands.
+
+

Name of Alteration Files

+ +Cool looks for alteration files in the alterations folder in the .skeinforge folder in the home directory. Cool does not care if the text file names are capitalized, but some file systems do not handle file name cases properly, so to be on the safe side you should give them lower case names. If it doesn't find the file it then looks in the alterations folder in the skeinforge_plugins folder. The cool start and end text idea is from:
+http://makerhahn.blogspot.com/2008/10/yay-minimug.html
+
+

Name of Cool End File

+ +Default is cool_end.gcode.
+
+If there is a file with the name of the "Name of Cool End File" setting, it will be added to the end of the orbits.
+
+

Name of Cool Start File

+ +Default is cool_start.gcode.
+
+If there is a file with the name of the "Name of Cool Start File" setting, it will be added to the start of the orbits.
+
+

Orbital Outset

+ +Default is 2 millimeters.
+
+When the orbit cool type is selected, the orbits will be outset around the largest island by 'Orbital Outset' millimeters. If 'Orbital Outset' is negative, the orbits will be inset instead.
+
+

Turn Fan On at Beginning

+ +Default is on.
+
+When selected, cool will turn the fan on at the beginning of the fabrication by adding the M106 command.
+
+

Turn Fan Off at Ending

+ +Default is on.
+
+When selected, cool will turn the fan off at the ending of the fabrication by adding the M107 command.
+
+

Examples

+
+ +The following examples cool the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and cool.py.
+
+> python cool.py
+This brings up the cool dialog.
+
+> python cool.py Screw Holder Bottom.stl
+The cool tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The cool tool has created the file:
+.. Screw Holder Bottom_cool.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+
os
+fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+
skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
CoolRepository +
CoolSkein +
+

+ + + + + + + +
 
+class CoolRepository
   A class to handle the cool settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Cool button has been clicked.
+ +

+ + + + + + + +
 
+class CoolSkein
   A class to cool a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
addCoolOrbits(self, remainingOrbitTime)
Add the minimum radius cool orbits.
+ +
addCoolTemperature(self, remainingOrbitTime)
Parse a gcode line and add it to the cool skein.
+ +
addFlowRate(self, flowRate)
Add a multipled line of flow rate if different.
+ +
addGcodeFromFeedRateMovementZ(self, feedRateMinute, point, z)
Add a movement to the output.
+ +
addOrbitsIfNecessary(self, remainingOrbitTime)
Parse a gcode line and add it to the cool skein.
+ +
addTemperature(self, temperature)
Add a line of temperature.
+ +
getCoolMove(self, line, location, splitLine)
Get cool line according to time spent on layer.
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the cool gcode.
+ +
getLayerTime(self)
Get the time the extruder spends on the layer.
+ +
getLayerTimeActive(self)
Get the time the extruder spends on the layer while active.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the cool skein.
+ +
setMultiplier(self, remainingOrbitTime)
Set the feed and flow rate multiplier.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text, repository=None)
Cool a gcode linear move text.
+
getCraftedTextFromText(gcodeText, repository=None)
Cool a gcode linear move text.
+
getNewRepository()
Get new repository.
+
main()
Display the cool dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Cool a gcode linear move file.  Chain cool the gcode if it is not already cooled.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.dimension.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.dimension.html new file mode 100644 index 0000000..ae40af9 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.dimension.html @@ -0,0 +1,259 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.dimension + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.dimension ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/dimension.py
+

+Previous / Next / Contents +

+


+Dimension adds Adrian's extruder distance E value so firmware does not have to calculate it on it's own and can set the extruder speed in relation to the distance that needs to be extruded. Some printers don't support this. Extruder distance is described at:
+
+http://blog.reprap.org/2009/05/4d-printing.html
+
+and in Erik de Bruijn's conversion script page at:
+
+http://objects.reprap.org/wiki/3D-to-5D-Gcode.php
+
+The dimension manual page is at:
+
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Dimension
+
+Nophead wrote an excellent article on how to set the filament parameters:
+
+http://hydraraptor.blogspot.com/2011/03/spot-on-flow-rate.html
+
+
+Operation
+Settings
+  Extrusion Distance Format Choice
+    Absolute Extrusion Distance
+    Relative Extrusion Distance
+  Extruder Retraction Speed
+  Filament
+    Filament Diameter
+    Filament Packing Density
+  Maximum E Value before Reset
+  Minimum Travel for Retraction
+  Retract Within Island
+  Retraction Distance
+  Restart Extra Distance
+Examples
+
+

Operation

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

Settings

+
+ +

Extrusion Distance Format Choice

+ +Default is 'Absolute Extrusion Distance' because in Adrian's description the distance is absolute. In future, because the relative distances are smaller than the cumulative absolute distances, hopefully the firmware will be able to use relative distance.
+
+

Absolute Extrusion Distance

+ +When selected, the extrusion distance output will be the total extrusion distance to that gcode line.
+
+

Relative Extrusion Distance

+ +When selected, the extrusion distance output will be the extrusion distance from the last gcode line.
+
+

Extruder Retraction Speed

+ +Default is 13.3 mm/s.
+
+Defines the extruder retraction feed rate. A high value will allow the retraction operation to complete before much material oozes out. If your extruder can handle it, this value should be much larger than your feed rate.
+
+As an example, I have a feed rate of 48 mm/s and a 'Extruder Retraction Speed' of 150 mm/s.
+
+

Filament

+ +

Filament Diameter

+ +Default is 2.8 millimeters.
+
+Defines the filament diameter.
+
+

Filament Packing Density

+ +Default is 0.85. This is for ABS.
+
+Defines the effective filament packing density.
+
+The default value is so low for ABS because ABS is relatively soft and with a pinch wheel extruder the teeth of the pinch dig in farther, so it sees a smaller effective diameter. With a hard plastic like PLA the teeth of the pinch wheel don't dig in as far, so it sees a larger effective diameter, so feeds faster, so for PLA the value should be around 0.97. This is with Wade's hobbed bolt. The effect is less significant with larger pinch wheels.
+
+Overall, you'll have to find the optimal filament packing density by experiment.
+
+

Maximum E Value before Reset

+ +Default: 91234.0
+
+Defines the maximum E value before it is reset with the 'G92 E0' command line. The reason it is reset only after the maximum E value is reached is because at least one firmware takes time to reset. The problem with waiting until the E value is high before resetting is that more characters are sent. So if your firmware takes a lot of time to reset, set this parameter to a high value, if it doesn't set this parameter to a low value or even zero.
+
+

Minimum Travel for Retraction

+ +Default: 1.0 millimeter
+
+Defines the minimum distance that the extruder head has to travel from the end of one thread to the beginning of another, in order to trigger the extruder retraction. Setting this to a high value means the extruder will retract only occasionally, setting it to a low value means the extruder will retract most of the time.
+
+

Retract Within Island

+ +Default is off.
+
+When selected, retraction will work even when the next thread is within the same island. If it is not selected, retraction will only work when crossing a boundary.
+
+

Retraction Distance

+ +Default is zero.
+
+Defines the amount the extruder retracts (sucks back) the extruded filament whenever an extruder stop is commanded. Using this seems to help prevent stringing. e.g. If set to 10 the extruder reverses the distance required to pull back 10mm of filament. In fact this does not actually happen but if you set this distance by trial and error you can get to a point where there is very little ooze from the extruder when it stops which is not normally the case.
+
+

Restart Extra Distance

+ +Default is zero.
+
+Defines the restart extra distance when the thread restarts. The restart distance will be the retraction distance plus the restart extra distance.
+
+If this is greater than zero when the extruder starts this distance is added to the retract value giving extra filament. It can be a negative value in which case it is subtracted from the retraction distance. On some Repstrap machines a negative value can stop the build up of plastic that can occur at the start of edges.
+
+

Examples

+
+ +The following examples dimension the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and dimension.py.
+
+> python dimension.py
+This brings up the dimension dialog.
+
+> python dimension.py Screw Holder Bottom.stl
+The dimension tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The dimension tool has created the file:
+.. Screw Holder Bottom_dimension.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
skeinforge_application.skeinforge_plugins.craft_plugins.__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+
fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+math
+os
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+
sys
+fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
DimensionRepository +
DimensionSkein +
+

+ + + + + + + +
 
+class DimensionRepository
   A class to handle the dimension settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Dimension button has been clicked.
+ +

+ + + + + + + +
 
+class DimensionSkein
   A class to dimension a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
Initialize.
+ +
addLinearMoveExtrusionDistanceLine(self, extrusionDistance)
Get the extrusion distance string from the extrusion distance.
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the dimension gcode.
+ +
getDimensionedArcMovement(self, line, splitLine)
Get a dimensioned arc movement.
+ +
getDimensionedLinearMovement(self, line, splitLine)
Get a dimensioned linear movement.
+ +
getDistanceToNextThread(self, lineIndex)
Get the travel distance to the next thread.
+ +
getExtrusionDistanceString(self, distance, splitLine)
Get the extrusion distance string.
+ +
getExtrusionDistanceStringFromExtrusionDistance(self, extrusionDistance)
Get the extrusion distance string from the extrusion distance.
+ +
getRetractionRatio(self, lineIndex)
Get the retraction ratio.
+ +
getSmallestEnclosureIndex(self, point)
Get the index of the smallest boundary loop which encloses the point.
+ +
parseBoundaries(self)
Parse the boundaries and add them to the boundary layers.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, lineIndex)
Parse a gcode line and add it to the dimension skein.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, gcodeText='', repository=None)
Dimension a gcode file or text.
+
getCraftedTextFromText(gcodeText, repository=None)
Dimension a gcode text.
+
getNewRepository()
Get new repository.
+
main()
Display the dimension dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Dimension a gcode file.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__date__ = '$Date: 2008/02/05 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.drill.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.drill.html new file mode 100644 index 0000000..2f9f8e0 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.drill.html @@ -0,0 +1,188 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.drill + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.drill ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/drill.py
+

+Previous / Next / Contents +

+


+Drill is a script to drill down small holes.
+
+
+Operation
+Settings
+  Drilling Margin
+  Drilling Margin on Top
+  Drilling Margin on Bottom
+Examples
+
+

Operation

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

Settings

+
+ +

Drilling Margin

+ +The drill script will move the tool from the top of the hole plus the 'Drilling Margin on Top', to the bottom of the hole minus the 'Drilling Margin on Bottom'.
+
+

Drilling Margin on Top

+ +Default is three millimeters.
+
+

Drilling Margin on Bottom

+ +Default is one millimeter.
+
+

Examples

+
+ +The following examples drill the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and drill.py.
+
+> python drill.py
+This brings up the drill dialog.
+
+> python drill.py Screw Holder Bottom.stl
+The drill tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The drill tool has created the file:
+.. Screw Holder Bottom_drill.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
DrillRepository +
DrillSkein +
ThreadLayer +
+

+ + + + + + + +
 
+class DrillRepository
   A class to handle the drill settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Drill button has been clicked.
+ +

+ + + + + + + +
 
+class DrillSkein
   A class to drill a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
addDrillHoles(self)
Parse a gcode line.
+ +
addGcodeFromVerticalThread(self, point, zBegin, zEnd)
Add a thread to the output.
+ +
addThreadLayerIfNone(self)
Add a thread layer if it is none.
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the drill gcode.
+ +
getDrillingCenterDepth(self, drillingCenterDepth, drillPoint)
Get the drilling center depth.
+ +
isPointClose(self, drillPoint, points)
Determine if a point on the thread layer is close.
+ +
linearMove(self, splitLine)
Add a linear move to the loop.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line.
+ +
parseNestedRing(self, line)
Parse a nested ring.
+ +

+ + + + + + + +
 
+class ThreadLayer
   A layer of loops and paths.
 
 Methods defined here:
+
__init__(self, z)
Thread layer constructor.
+ +
__repr__(self)
Get the string representation of this thread layer.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text, repository=None)
Drill a gcode linear move file or text.
+
getCraftedTextFromText(gcodeText, repository=None)
Drill a gcode linear move text.
+
getNewRepository()
Get new repository.
+
getPolygonCenter(polygon)
Get the centroid of a polygon.
+
main()
Display the drill dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Drill a gcode linear move file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.export.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.export.html new file mode 100644 index 0000000..7c2d994 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.export.html @@ -0,0 +1,271 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.export + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.export ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/export.py
+

+Previous / Next / Contents +

+


+Export is a craft tool to pick an export plugin, add information to the file name, and delete comments.
+
+The export manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Export
+
+
+Operation
+Settings
+  Add Descriptive Extension
+  Add Profile Extension
+  Add Timestamp Extension
+  Also Send Output To
+  Analyze Gcode
+  Comment Choice
+    Do Not Delete Comments
+    Delete Crafting Comments
+    Delete All Comments
+  Export Operations
+  File Extension
+  Name of Replace File
+  Save Penultimate Gcode
+Examples
+
+

Operation

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

Settings

+
+ +

Add Descriptive Extension

+ +Default is off.
+
+When selected, key profile values will be added as an extension to the gcode file. For example:
+test.04hx06w_03fill_2cx2r_33EL.gcode
+
+would mean:
+
+* . (Carve section.)
+* 04h = 'Layer Height (mm):' 0.4
+* x
+* 06w = 0.6 width i.e. 0.4 times 'Edge Width over Height (ratio):' 1.5
+* _ (Fill section.)
+* 03fill = 'Infill Solidity (ratio):' 0.3
+* _ (Multiply section; if there is one column and one row then this section is not shown.)
+* 2c = 'Number of Columns (integer):' 2
+* x
+* 2r = 'Number of Rows (integer):' 2.
+* _ (Speed section.)
+* 33EL = 'Feed Rate (mm/s):' 33.0 and 'Flow Rate Setting (float):' 33.0. If either value has a positive value after the decimal place then this is also shown, but if it is zero it is hidden. Also, if the values differ (which they shouldn't with 5D volumetrics) then each should be displayed separately. For example, 35.2E30L = 'Feed Rate (mm/s):' 35.2 and 'Flow Rate Setting (float):' 30.0.
+
+

Add Profile Extension

+ +Default is off.
+
+When selected, the current profile will be added to the file extension. For example:
+test.my_profile_name.gcode
+
+

Add Timestamp Extension

+ +Default is off.
+
+When selected, the current date and time is added as an extension in format YYYYmmdd_HHMMSS (so it is sortable if one has many files). For example:
+test.my_profile_name.20110613_220113.gcode
+
+

Also Send Output To

+ +Default is empty.
+
+Defines the output name for sending to a file or pipe. A common choice is stdout to print the output in the shell screen. Another common choice is stderr. With the empty default, nothing will be done. If the value is anything else, the output will be written to that file name.
+
+

Analyze Gcode

+ +Default is on.
+
+When selected, the penultimate gcode will be sent to the analyze plugins to be analyzed and viewed.
+
+

Comment Choice

+ +Default is 'Delete All Comments'.
+
+

Do Not Delete Comments

+ +When selected, export will not delete comments. Crafting comments slow down the processing in many firmware types, which leads to pauses and therefore a lower quality print.
+
+

Delete Crafting Comments

+ +When selected, export will delete the time consuming crafting comments, but leave the initialization comments. Since the crafting comments are deleted, there are no pauses during extrusion. The remaining initialization comments provide some useful information for the analyze tools.
+
+

Delete All Comments

+ +When selected, export will delete all comments. The comments are not necessary to run a fabricator. Some printers do not support comments at all so the safest way is choose this option.
+
+

Export Operations

+ +Export presents the user with a choice of the export plugins in the export_plugins folder. The chosen plugin will then modify the gcode or translate it into another format. There is also the "Do Not Change Output" choice, which will not change the output. An export plugin is a script in the export_plugins folder which has the getOutput function, the globalIsReplaceable variable and if it's output is not replaceable, the writeOutput function.
+
+

File Extension

+ +Default is gcode.
+
+Defines the file extension added to the name of the output file. The output file will be named as originalname_export.extension so if you are processing XYZ.stl the output will by default be XYZ_export.gcode
+
+

Name of Replace File

+ +Default is replace.csv.
+
+When export is exporting the code, if there is a tab separated file with the name of the "Name of Replace File" setting, it will replace the string in the first column by its replacement in the second column. If there is nothing in the second column, the first column string will be deleted, if this leads to an empty line, the line will be deleted. If there are replacement columns after the second, they will be added as extra lines of text. There is an example file replace_example.csv to demonstrate the tab separated format, which can be edited in a text editor or a spreadsheet.
+
+Export looks for the alteration file in the alterations folder in the .skeinforge folder in the home directory. Export does not care if the text file names are capitalized, but some file systems do not handle file name cases properly, so to be on the safe side you should give them lower case names. If it doesn't find the file it then looks in the alterations folder in the skeinforge_plugins folder.
+
+

Save Penultimate Gcode

+ +Default is off.
+
+When selected, export will save the gcode file with the suffix '_penultimate.gcode' just before it is exported. This is useful because the code after it is exported could be in a form which the viewers can not display well.
+
+

Examples

+
+ +The following examples export the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and export.py.
+
+> python export.py
+This brings up the export dialog.
+
+> python export.py Screw Holder Bottom.stl
+The export tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The export tool has created the file:
+.. Screw Holder Bottom_export.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+cStringIO
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+os
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_analyze
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+time
+

+ + + + + +
 
+Classes
       
+
ExportRepository +
ExportSkein +
+

+ + + + + + + +
 
+class ExportRepository
   A class to handle the export settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Export button has been clicked.
+ +

+ + + + + + + +
 
+class ExportSkein
   A class to export a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
addLine(self, line)
Add a line of text and a newline to the output.
+ +
getCraftedGcode(self, repository, gcodeText)
Parse gcode text and store the export gcode.
+ +
getLineWithTruncatedNumber(self, character, line, splitLine)
Get a line with the number after the character truncated.
+ +
parseLine(self, line)
Parse a gcode line.
+ +

+ + + + + +
 
+Functions
       
getCraftedTextFromText(gcodeText, repository=None)
Export a gcode linear move text.
+
getDescriptionCarve(lines)
Get the description for carve.
+
getDescriptionFill(lines)
Get the description for fill.
+
getDescriptionMultiply(lines)
Get the description for multiply.
+
getDescriptionSpeed(lines)
Get the description for speed.
+
getDescriptiveExtension(gcodeText)
Get the descriptive extension.
+
getDistanceGcode(exportText)
Get gcode lines with distance variable added, this is for if ever there is distance code.
+
getFirstValue(gcodeText, word)
Get the value from the first line which starts with the given word.
+
getNewRepository()
Get new repository.
+
getReplaceableExportGcode(nameOfReplaceFile, replaceableExportGcode)
Get text with strings replaced according to replace.csv file.
+
getSelectedPluginModule(plugins)
Get the selected plugin module.
+
getSettingString(lines, procedureName, settingNameStart)
Get the setting value from the lines, return None if there is no setting starting with that name.
+
main()
Display the export dialog.
+
sendOutputTo(outputTo, text)
Send output to a file or a standard output.
+
writeOutput(fileName, shouldAnalyze=True)
Export a gcode linear move file.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__credits__ = 'Gary Hodgson <http://garyhodgson.com/reprap/2011/06/hacking-skeinforge-export-module/>'
+__date__ = '$Date: 2008/21/04 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)

+ + + + + +
 
+Credits
       Gary Hodgson <http://garyhodgson.com/reprap/2011/06/hacking-skeinforge-export-module/>
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.binary_16_byte.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.binary_16_byte.html new file mode 100644 index 0000000..e396cd5 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.binary_16_byte.html @@ -0,0 +1,230 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.binary_16_byte + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.binary_16_byte ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/binary_16_byte.py
+

+Previous / Next / Contents +

+


+Binary 16 byte is an export plugin to convert gcode into 16 byte binary segments.
+
+An export plugin is a script in the export_plugins folder which has the getOutput function, the globalIsReplaceable variable and if it's output is not replaceable, the writeOutput function. It is meant to be run from the export tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name.
+
+The getOutput function of this script takes a gcode text and returns that text converted into 16 byte segments. The writeOutput function of this script takes a gcode text and writes that in a binary format converted into 16 byte segments.
+
+This plugin is just a starter to make a real binary converter.
+
+
+Settings
+  Feed Rate Step Length
+  File Extension
+  Offset
+    X Offset
+    Y Offset
+    Z Offset
+  Step Length
+    X Step Length
+    Y Step Length
+    Z Step Length
+Record structure
+
+

Settings

+
+ +

Feed Rate Step Length

+ +Default is 0.1 millimeters/second.
+
+Defines the feed rate step length.
+
+

File Extension

+ +Default is bin.
+
+Defines the file extension suffix.
+
+

Offset

+ +

X Offset

+ +Default is zero.
+
+Defines the X Offset.
+
+

Y Offset

+ +Default is zero.
+
+Defines the Y Offset.
+
+

Z Offset

+ +Default is zero.
+
+Defines the Z Offset.
+
+

Step Length

+ +

X Step Length

+ +Default is 0.1 millimeters.
+
+Defines the X axis step length.
+
+

Y Step Length

+ +Default is 0.1 millimeters.
+
+Defines the Y axis step length.
+
+

Z Step Length

+ +Default is 0.01 millimeters.
+
+Defines the Z axis step length.
+
+

Record structure

+
+ +BinArray(0) = AscW(Inst_Code_Letter)
+BinArray(1) = cInst_Code
+
+X Data
+sInt32_to_Hbytes(iXdim_1)
+BinArray(2) = lsb 'short lsb
+BinArray(3) = msb 'short msb
+
+Y Data
+sInt32_to_Hbytes(iYdim_2)
+BinArray(4) = lsb 'short lsb
+BinArray(5) = msb 'short msb
+
+Z Data
+sInt32_to_Hbytes(iZdim_3)
+BinArray(6) = lsb 'short lsb
+BinArray(7) = msb 'short msb
+
+I Data
+sInt32_to_Hbytes(iIdim_4)
+BinArray(8) = lsb 'short lsb
+BinArray(9) = msb 'short msb
+
+J Data
+sInt32_to_Hbytes(iJdim_5)
+BinArray(10) = lsb 'short lsb
+BinArray(11) = msb 'short msb
+
+BinArray(12) = FP_Char
+sInt32_to_Hbytes(iFP_Num)
+BinArray(13) = lsb 'short lsb
+
+BinArray(14) = bActiveFlags
+
+BinArray(15) = AscW("#")End of record filler
+
+Byte 14 is worth a few extra notes, this byte is used to define which of the axes are active, its used to get round the problem of say a line of code with no mention of z. This would be put into the file as z = 0 as the space for this data is reserved, if we did nothing, this would instruct the machine to go to z = 0. If we use the active flag to define the z axis as inactive the z = 0 is ignored and the value set to the last saved value of z, i.e it does not move. If the z data is actually set to z = 0 then the axis would be set to active and the move takes place.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+cStringIO
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+os
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+
sys
+

+ + + + + +
 
+Classes
       
+
Binary16ByteRepository +
Binary16ByteSkein +
+

+ + + + + + + +
 
+class Binary16ByteRepository
   A class to handle the export settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Convert to binary 16 byte button has been clicked.
+ +

+ + + + + + + +
 
+class Binary16ByteSkein
   A class to convert gcode into 16 byte binary segments.
 
 Methods defined here:
+
__init__(self)
+ +
getCraftedGcode(self, gcodeText, binary16ByteRepository)
Parse gcode text and store the gcode.
+ +
parseLine(self, line)
Parse a gcode line.
+ +

+ + + + + +
 
+Functions
       
getIntegerFlagFromCharacterSplitLine(character, splitLine)
Get the integer flag after the first occurence of the character in the split line.
+
getIntegerFromCharacterLengthLineOffset(character, offset, splitLine, stepLength)
Get the integer after the first occurence of the character in the split line.
+
getNewRepository()
Get new repository.
+
getOutput(gcodeText, binary16ByteRepository=None)
Get the exported version of a gcode file.
+
main()
Display the export dialog.
+
writeOutput(fileName, gcodeText='')
Write the exported version of a gcode file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalIsReplaceable = False

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.gcode_step.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.gcode_step.html new file mode 100644 index 0000000..18945bc --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.gcode_step.html @@ -0,0 +1,220 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.gcode_step + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.gcode_step ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/gcode_step.py
+

+Previous / Next / Contents +

+


+Gcode step is an export plugin to convert gcode from float position to number of steps.
+
+An export plugin is a script in the export_plugins folder which has the getOutput function, the globalIsReplaceable variable and if it's output is not replaceable, the writeOutput function. It is meant to be run from the export tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name.
+
+The getOutput function of this script takes a gcode text and returns it with the positions converted into number of steps. The writeOutput function of this script takes a gcode text and writes that with the positions converted into number of steps.
+
+
+Settings
+  Add Feed Rate Even When Unchanging
+  Add Space Between Words
+  Add Z Even When Unchanging
+  Feed Rate Step Length
+  Offset
+    X Offset
+    Y Offset
+    Z Offset
+  Step Length
+    E Step Length
+  Radius Rate Step Length
+    X Step Length
+    Y Step Length
+    Z Step Length
+
+

Settings

+
+ +

Add Feed Rate Even When Unchanging

+ +Default is on.
+
+When selected, the feed rate will be added even when it did not change from the previous line.
+
+

Add Space Between Words

+ +Default is on.
+
+When selected, a space will be added between each gcode word.
+
+

Add Z Even When Unchanging

+ +Default is on.
+
+When selected, the z word will be added even when it did not change.
+
+

Feed Rate Step Length

+ +Default is 0.1 millimeters/second.
+
+Defines the feed rate step length.
+
+

Offset

+ +

X Offset

+ +Default is zero.
+
+Defines the X Offset.
+
+

Y Offset

+ +Default is zero.
+
+Defines the Y Offset.
+
+

Z Offset

+ +Default is zero.
+
+Defines the Z Offset.
+
+

Step Length

+ +

E Step Length

+ +Default is 0.1 millimeters.
+
+Defines the E extrusion distance step length.
+
+

Radius Rate Step Length

+ +Default is 0.1 millimeters/second.
+
+Defines the radius step length.
+
+

X Step Length

+ +Default is 0.1 millimeters.
+
+Defines the X axis step length.
+
+

Y Step Length

+ +Default is 0.1 millimeters.
+
+Defines the Y axis step length.
+
+

Z Step Length

+ +Default is 0.01 millimeters.
+
+Defines the Z axis step length.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+cStringIO
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+os
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+
sys
+

+ + + + + +
 
+Classes
       
+
GcodeStepRepository +
GcodeStepSkein +
+

+ + + + + + + +
 
+class GcodeStepRepository
   A class to handle the export settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Convert to gcode step button has been clicked.
+ +

+ + + + + + + +
 
+class GcodeStepSkein
   A class to convert gcode into 16 byte binary segments.
 
 Methods defined here:
+
__init__(self)
+ +
addCharacterInteger(self, character, lineStringIO, offset, splitLine, stepLength)
Add a character and integer to line string.
+ +
addLine(self, line)
Add a line of text and a newline to the output.
+ +
addStringToLine(self, lineStringIO, wordString)
Add a character and integer to line string.
+ +
getCraftedGcode(self, repository, gcodeText)
Parse gcode text and store the gcode.
+ +
parseLine(self, line)
Parse a gcode line.
+ +

+ + + + + +
 
+Functions
       
getCharacterIntegerString(character, offset, splitLine, stepLength)
Get a character and integer string.
+
getFloatFromCharacterSplitLine(character, splitLine)
Get the float after the first occurence of the character in the split line.
+
getNewRepository()
Get new repository.
+
getOutput(gcodeText, repository=None)
Get the exported version of a gcode file.
+
main()
Display the export dialog.
+
writeOutput(fileName, gcodeText='')
Write the exported version of a gcode file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalIsReplaceable = True

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.gcode_time_segment.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.gcode_time_segment.html new file mode 100644 index 0000000..d3dc541 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.gcode_time_segment.html @@ -0,0 +1,199 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.gcode_time_segment + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.gcode_time_segment ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/gcode_time_segment.py
+

+Previous / Next / Contents +

+


+Gcode time segment is an export plugin to convert gcode from float position to number of steps.
+
+An export plugin is a script in the export_plugins folder which has the getOutput function, the globalIsReplaceable variable and if it's output is not replaceable, the writeOutput function. It is meant to be run from the export tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name.
+
+The getOutput function of this script takes a gcode text and returns it with the positions converted into number of steps and time. The writeOutput function of this script takes a gcode text and writes that with the positions converted into number of steps and time.
+
+
+Settings
+  Add Space Between Words
+  Offset
+    X Offset
+    Y Offset
+    Z Offset
+  Step
+  Extrusion Step
+  Time Step
+    X Step
+    Y Step
+    Z Step
+
+

Settings

+
+ +

Add Space Between Words

+ +Default is on.
+
+When selected, a space will be added between each gcode word.
+
+

Offset

+ +

X Offset

+ +Default is zero.
+
+Defines the X Offset.
+
+

Y Offset

+ +Default is zero.
+
+Defines the Y Offset.
+
+

Z Offset

+ +Default is zero.
+
+Defines the Z Offset.
+
+

Step

+ +

Extrusion Step

+ +Default is 0.01 mm.
+
+Defines the radius step length.
+
+

Time Step

+ +Default is 1 microsecond(mcs).
+
+Defines the time step duration.
+
+

X Step

+ +Default is 0.1 mm.
+
+Defines the X axis step length.
+
+

Y Step

+ +Default is 0.1 mm.
+
+Defines the Y axis step length.
+
+

Z Step

+ +Default is 0.01 mm.
+
+Defines the Z axis step length.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+cStringIO
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+os
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+
sys
+

+ + + + + +
 
+Classes
       
+
GcodeTimeSegmentRepository +
GcodeTimeSegmentSkein +
+

+ + + + + + + +
 
+class GcodeTimeSegmentRepository
   A class to handle the export settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Convert to gcode step button has been clicked.
+ +

+ + + + + + + +
 
+class GcodeTimeSegmentSkein
   A class to convert gcode into time segments.
 
 Methods defined here:
+
__init__(self)
Initialize.
+ +
addCharacterInteger(self, character, lineStringIO, offset, splitLine, step)
Add a character and integer to line string.
+ +
addLine(self, line)
Add a line of text and a newline to the output.
+ +
addStringToLine(self, lineStringIO, wordString)
Add a character and integer to line string.
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the gcode.
+ +
parseLine(self, line)
Parse a gcode line.
+ +

+ + + + + +
 
+Functions
       
getCharacterIntegerString(character, offset, splitLine, step)
Get a character and integer string.
+
getFloatFromCharacterSplitLine(character, splitLine)
Get the float after the first occurence of the character in the split line.
+
getNewRepository()
Get new repository.
+
getOutput(gcodeText, repository=None)
Get the exported version of a gcode file.
+
main()
Display the export dialog.
+
writeOutput(fileName, gcodeText='')
Write the exported version of a gcode file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalIsReplaceable = True

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.html new file mode 100644 index 0000000..d28df3b --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.html @@ -0,0 +1,34 @@ + + +Python: package skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
binary_16_byte
+
gcode_step
+
gcode_time_segment
+
static_plugins (package)
+

+ + + + + +
 
+Data
       level = 4
+numberOfLevelsDeepInPackageHierarchy = 4
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.static_plugins.gcode_small.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.static_plugins.gcode_small.html new file mode 100644 index 0000000..956d9ed --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.static_plugins.gcode_small.html @@ -0,0 +1,97 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.static_plugins.gcode_small + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.static_plugins.gcode_small ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/static_plugins/gcode_small.py
+

+Previous / Next / Contents +

+


+Gcode_small is an export plugin to remove the comments and the redundant z and feed rate parameters from a gcode file.
+
+An export plugin is a script in the export_plugins folder which has the getOutput function, the globalIsReplaceable variable and if it's output is not replaceable, the writeOutput function. It is meant to be run from the export tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name.
+
+The getOutput function of this script takes a gcode text and returns that text without comments and redundant z and feed rate parameters. The writeOutput function of this script takes a gcode text and writes that text without comments and redundant z and feed rate parameters to a file.
+
+Many of the functions in this script are copied from gcodec in skeinforge_utilities. They are copied rather than imported so developers making new plugins do not have to learn about gcodec, the code here is all they need to learn.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
cStringIO
+
os
+

+ + + + + +
 
+Classes
       
+
GcodeSmallSkein +
+

+ + + + + + + +
 
+class GcodeSmallSkein
   A class to remove redundant z and feed rate parameters from a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
getCraftedGcode(self, gcodeText)
Parse gcode text and store the gcode.
+ +
parseLine(self, line)
Parse a gcode line.
+ +

+ + + + + +
 
+Functions
       
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
+
getOutput(gcodeText)
Get the exported version of a gcode file.
+
getSplitLineBeforeBracketSemicolon(line)
Get the split line before a bracket or semicolon.
+
getStringFromCharacterSplitLine(character, splitLine)
Get the string after the first occurence of the character in the split line.
+
getSummarizedFileName(fileName)
Get the fileName basename if the file is in the current working directory, otherwise return the original full name.
+
getTextLines(text)
Get the all the lines of text of a text.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)
+globalIsReplaceable = True

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.static_plugins.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.static_plugins.html new file mode 100644 index 0000000..39cd69c --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.static_plugins.html @@ -0,0 +1,31 @@ + + +Python: package skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.static_plugins + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.static_plugins
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/static_plugins/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
gcode_small
+

+ + + + + +
 
+Data
       level = 5
+numberOfLevelsDeepInPackageHierarchy = 5
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.feed.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.feed.html new file mode 100644 index 0000000..a4f8698 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.feed.html @@ -0,0 +1,176 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.feed + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.feed ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/feed.py
+

+Previous / Next / Contents +

+


+The feed script sets the maximum feed rate, operating feed rate & travel feed rate.
+
+
+Operation
+Settings
+  Feed Rate
+  Maximum Z Drill Feed Rate
+  Maximum Z Feed Rate
+  Travel Feed Rate
+Examples
+
+

Operation

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

Settings

+
+ +

Feed Rate

+ +Default is 16 millimeters/second.
+
+Defines the feed rate for the shape.
+
+

Maximum Z Drill Feed Rate

+ +Default is 0.1 millimeters/second.
+
+If your firmware limits the z feed rate, you do not need to set this setting.
+
+Defines the maximum feed that the tool head will move in the z direction while the tool is on.
+
+

Maximum Z Feed Rate

+ +Default is one millimeter per second.
+
+Defines the maximum speed that the tool head will move in the z direction.
+
+

Travel Feed Rate

+ +Default is 16 millimeters/second.
+
+Defines the feed rate when the cutter is off. The travel feed rate could be set as high as the cutter can be moved, it does not have to be limited by the maximum cutter rate.
+
+

Examples

+
+ +The following examples feed the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and feed.py.
+
+> python feed.py
+This brings up the feed dialog.
+
+> python feed.py Screw Holder Bottom.stl
+The feed tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The feed tool has created the file:
+.. Screw Holder Bottom_feed.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+
math
+fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+
skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
FeedRepository +
FeedSkein +
+

+ + + + + + + +
 
+class FeedRepository
   A class to handle the feed settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Feed button has been clicked.
+ +

+ + + + + + + +
 
+class FeedSkein
   A class to feed a skein of cuttings.
 
 Methods defined here:
+
__init__(self)
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the feed gcode.
+ +
getFeededLine(self, line, splitLine)
Get gcode line with feed rate.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the feed skein.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, gcodeText='', repository=None)
Feed the file or text.
+
getCraftedTextFromText(gcodeText, repository=None)
Feed a gcode linear move text.
+
getNewRepository()
Get new repository.
+
main()
Display the feed dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Feed a gcode linear move file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.fill.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.fill.html new file mode 100644 index 0000000..1fe78e7 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.fill.html @@ -0,0 +1,476 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.fill + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.fill ($Date: 2008/28/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/fill.py
+

+Previous / Next / Contents +

+


+Fill is a script to fill the edges of a gcode file.
+
+The fill manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Fill
+
+Allan Ecker aka The Masked Retriever has written the "Skeinforge Quicktip: Fill" at:
+http://blog.thingiverse.com/2009/07/21/mysteries-of-skeinforge-fill/
+
+
+Operation
+Settings
+  Diaphragm
+    Diaphragm Period
+    Diaphragm Thickness
+  Extra Shells
+    Extra Shells on Alternating Solid Layers
+    Extra Shells on Base
+    Extra Shells on Sparse Layer
+  Grid
+    Grid Circle Separation over Perimeter Width
+    Grid Extra Overlap
+    Grid Junction Separation over Octogon Radius At End
+    Grid Junction Separation over Octogon Radius At Middle
+    Grid Junction Separation Band Height
+  Infill
+    Infill Pattern
+      Grid Circular
+      Grid Hexagonal
+      Grid Rectangular
+      Line
+    Infill Begin Rotation
+    Infill Odd Layer Extra Rotation
+    Infill Begin Rotation Repeat
+    Infill Perimeter Overlap
+    Infill Solidity
+    Infill Width over Thickness
+  Solid Surface Thickness
+  Start From Choice
+    Lower Left
+    Nearest
+  Surrounding Angle
+  Thread Sequence Choice
+    Infill > Loops > Perimeter
+    Infill > Perimeter > Loops
+    Loops > Infill > Perimeter
+    Loops > Perimeter > Infill
+    Perimeter > Infill > Loops
+    Perimeter > Loops > Infill
+Examples
+
+

Operation

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

Settings

+
+ +

Diaphragm

+ +The diaphragm is a solid group of layers, at regular intervals. It can be used with a sparse infill to give the object watertight, horizontal compartments and/or a higher shear strength.
+
+

Diaphragm Period

+ +Default is one hundred.
+
+Defines the number of layers between diaphrams.
+
+

Diaphragm Thickness

+ +Default is zero, because the diaphragm feature is rarely used.
+
+Defines the number of layers the diaphram is composed of.
+
+

Extra Shells

+ +The shells interior edge loops. Adding extra shells makes the object stronger & heavier.
+
+

Extra Shells on Alternating Solid Layers

+ +Default is two.
+
+Defines the number of extra shells, on the alternating solid layers.
+
+

Extra Shells on Base

+ +Default is one.
+
+Defines the number of extra shells on the bottom, base layer and every even solid layer after that. Setting this to a different value than the "Extra Shells on Alternating Solid Layers" means the infill pattern will alternate, creating a strong interleaved bond even if the edge loop shrinks.
+
+

Extra Shells on Sparse Layer

+ +Default is one.
+
+Defines the number of extra shells on the sparse layers. The solid layers are those at the top & bottom, and wherever the object has a plateau or overhang, the sparse layers are the layers in between.
+
+

Grid

+ +

Grid Circle Separation over Perimeter Width

+ +Default is 0.2.
+
+Defines the ratio of the amount the grid circle is inset over the edge width, the default is zero. With a value of zero the circles will touch, with a value of one two threads could be fitted between the circles.
+
+

Grid Extra Overlap

+ +Default is 0.1.
+
+Defines the amount of extra overlap added when extruding the grid to compensate for the fact that when the first thread going through a grid point is extruded, since there is nothing there yet for it to connect to it will shrink extra.
+
+

Grid Junction Separation over Octogon Radius At End

+ +Default is zero.
+
+Defines the ratio of the amount the grid square is increased in each direction over the extrusion width at the end. With a value of one or so the grid pattern will have large squares to go with the octogons.
+
+

Grid Junction Separation over Octogon Radius At Middle

+ +Default is zero.
+
+Defines the increase at the middle. If this value is different than the value at the end, the grid would have an accordion pattern, which would give it a higher shear strength.
+
+

Grid Junction Separation Band Height

+ +Default is ten.
+
+Defines the height of the bands of the accordion pattern.
+
+

Infill

+ +

Infill Pattern

+ +Default is 'Line', since it is quicker to generate and does not add extra movements for the extruder. The grid pattern has extra diagonal lines, so when choosing a grid option, set the infill solidity to 0.2 or less so that there is not too much plastic and the grid generation time, which increases with the third power of solidity, will be reasonable.
+
+
Grid Circular
+ +When selected, the infill will be a grid of separated circles. Because the circles are separated, the pattern is weak, it only provides support for the top layer threads and some strength in the z direction. The flip side is that this infill does not warp the object, the object will get warped only by the walls.
+
+Because this pattern turns the extruder on and off often, it is best to use a stepper motor extruder.
+
+
Grid Hexagonal
+ +When selected, the infill will be a hexagonal grid. Because the grid is made with threads rather than with molding or milling, only a partial hexagon is possible, so the rectangular grid pattern is stronger.
+
+
Grid Rectangular
+ +When selected, the infill will be a funky octogon square honeycomb like pattern which gives the object extra strength.
+
+
Line
+ +When selected, the infill will be made up of lines.
+
+

Infill Begin Rotation

+ +Default is forty five degrees, giving a diagonal infill.
+
+Defines the amount the infill direction of the base and every second layer thereafter is rotated.
+
+

Infill Odd Layer Extra Rotation

+ +Default is ninety degrees, making the odd layer infill perpendicular to the base layer.
+
+Defines the extra amount the infill direction of the odd layers is rotated compared to the base layer.
+
+

Infill Begin Rotation Repeat

+ +Default is one, giving alternating cross hatching.
+
+Defines the number of layers that the infill begin rotation will repeat. With a value higher than one, the infill will go in one direction more often, giving the object more strength in one direction and less in the other, this is useful for beams and cantilevers.
+
+

Infill Perimeter Overlap

+ +Default is 0.15.
+
+Defines the amount the infill overlaps the edge over the average of the edge and infill width. The higher the value the more the infill will overlap the edge, and the thicker join between the infill and the edge. If the value is too high, the join will be so thick that the nozzle will run plow through the join below making a mess, also when it is above 0.45 fill may not be able to create infill correctly. If you want to stretch the infill a lot, set 'Path Stretch over Perimeter Width' in stretch to a high value.
+
+

Infill Solidity

+ +Default is 0.2.
+
+Defines the solidity of the infill, this is the most important setting in fill. A value of one means the infill lines will be right beside each other, resulting in a solid, strong, heavy shape which takes a long time to extrude. A low value means the infill will be sparse, the interior will be mosty empty space, the object will be weak, light and quick to build.
+
+

Infill Width over Thickness

+ +Default is 1.5.
+
+Defines the ratio of the infill width over the layer height. The higher the value the wider apart the infill will be and therefore the sparser the infill will be.
+
+

Solid Surface Thickness

+ +Default is three.
+
+Defines the number of solid layers that are at the bottom, top, plateaus and overhang. With a value of zero, the entire object will be composed of a sparse infill, and water could flow right through it. With a value of one, water will leak slowly through the surface and with a value of three, the object could be watertight. The higher the solid surface thickness, the stronger and heavier the object will be.
+
+

Start From Choice

+ +Default is 'Lower Left'.
+
+Defines where each layer starts from.
+
+

Lower Left

+ +When selected the layer will start from the lower left corner. This is to extrude in round robin fashion so that the first extrusion will be deposited on the coolest part of the last layer. The reason for this is described at:
+http://hydraraptor.blogspot.com/2010/12/round-robin.html
+
+

Nearest

+ +When selected the layer will start from the closest point to the end of the last layer. This leads to less stringing, but the first extrusion will be deposited on the hottest part of the last layer which leads to melting problems. So this option is deprecated, eventually this option will be removed and the layers will always start from the lower left.
+
+

Surrounding Angle

+ +Default: 60 degrees
+
+Defines the angle that the surrounding layers around the infill are expanded.
+
+To decide whether or not the infill should be sparse or solid, fill looks at the 'Solid Surface Thickness' surrounding layers above and below the infill. If any of the expanded layers above or below the infill do not cover the infill, then the infill will be solid in that region. The layers are expanded by the height difference times the tangent of the surrounding angle, which is from the vertical. For example, if the model is a wedge with a wall angle less than the surrounding angle, the interior layers (those which are not on the bottom or top) will be sparse. If the wall angle is greater than the surrounding angle, the interior layers will be solid.
+
+The time required to examine the surrounding layers increases with the surrounding angle, so the surrounding angle is limited to eighty degrees, regardless of the input value.
+
+If you have an organic shape with gently sloping surfaces; if the surrounding angle is set too high, then too many layers will be sparse. If the surrounding angle is too low, then too many layers will be solid and the extruder may end up plowing through previous layers:
+http://hydraraptor.blogspot.com/2008/08/bearing-fruit.html
+
+

Thread Sequence Choice

+ +The 'Thread Sequence Choice' is the sequence in which the threads will be extruded on the second and higher layers. There are three kinds of thread, the edge threads on the outside of the object, the loop threads aka inner shell threads, and the interior infill threads. The first layer thread sequence is 'Perimeter > Loops > Infill'.
+
+The default choice is 'Perimeter > Loops > Infill', which the default stretch parameters are based on. If you change from the default sequence choice setting of edge, then loops, then infill, the optimal stretch thread parameters would also be different. In general, if the infill is extruded first, the infill would have to be stretched more so that even after the filament shrinkage, it would still be long enough to connect to the loop or edge. The six sequence combinations follow below.
+
+

Infill > Loops > Perimeter

+ +

Infill > Perimeter > Loops

+ +

Loops > Infill > Perimeter

+ +

Loops > Perimeter > Infill

+ +

Perimeter > Infill > Loops

+ +

Perimeter > Loops > Infill

+ +
+

Examples

+
+ +The following examples fill the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and fill.py.
+
+> python fill.py
+This brings up the fill dialog.
+
+> python fill.py Screw Holder Bottom.stl
+The fill tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The fill tool has created the file:
+.. Screw Holder Bottom_fill.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+
fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+math
+fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+
fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
FillRepository +
FillSkein +
RotatedLayer +
YIntersectionPath +
+

+ + + + + + + +
 
+class FillRepository
   A class to handle the fill settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Fill button has been clicked.
+ +

+ + + + + + + +
 
+class FillSkein
   A class to fill a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
Initialize.
+ +
addFill(self, layerIndex)
Add fill to the carve layer.
+ +
addGcodeFromThreadZ(self, thread, z)
Add a gcode thread to the output.
+ +
addGrid(self, arounds, fillLoops, gridPointInsetX, layerIndex, paths, pixelTable, reverseRotation, surroundingCarves, width)
Add the grid to the infill layer.
+ +
addGridCircle(self, center, infillPaths, layerRotation, pixelTable, rotatedLoops, startRotation, width)
Add circle to the grid.
+ +
addGridLinePoints(self, begin, end, gridPoints, gridRotationAngle, offset, y)
Add the segments of one line of a grid to the infill.
+ +
addRemainingGridPoints(self, arounds, gridPointInsetX, gridPointInsetY, gridPoints, isBothOrNone, paths, pixelTable, width)
Add the remaining grid points to the grid point list.
+ +
addRotatedCarve(self, currentLayer, layerDelta, reverseRotation, surroundingCarves)
Add a rotated carve to the surrounding carves.rotatedCarveDictionary
+ +
addThreadsBridgeLayer(self, layerIndex, nestedRings, rotatedLayer, testLoops=None)
Add the threads, add the bridge end & the layer end tag.
+ +
addToThread(self, location)
Add a location to thread.
+ +
getCraftedGcode(self, repository, gcodeText)
Parse gcode text and store the bevel gcode.
+ +
getGridPoints(self, fillLoops, reverseRotation)
Get the grid points.
+ +
getGridPointsByLoops(self, gridRotationAngle, loops)
Get the grid points by loops.
+ +
getLayerRotation(self, layerIndex)
Get the layer rotation.
+ +
getNextGripXStep(self, gridXStep)
Get the next grid x step, increment by an extra one every three if hexagonal grid is chosen.
+ +
isGridToBeExtruded(self)
Determine if the grid is to be extruded.
+ +
isPointInsideLineSegments(self, gridPoint)
Is the point inside the line segments of the loops.
+ +
linearMove(self, splitLine)
Add a linear move to the thread.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, lineIndex)
Parse a gcode line and add it to the fill skein.
+ +
setGridVariables(self, repository)
Set the grid variables.
+ +

+ + + + + + + +
 
+class RotatedLayer
   A rotated layer.
 
 Methods defined here:
+
__init__(self, z)
Initialize.
+ +
__repr__(self)
Get the string representation of this RotatedLayer.
+ +

+ + + + + + + +
 
+class YIntersectionPath
   A class to hold the y intersection position, the loop which it intersected and the point index of the loop which it intersected.
 
 Methods defined here:
+
__init__(self, pathIndex, pointIndex, y)
Initialize from the path, point index, and y.
+ +
__repr__(self)
Get the string representation of this y intersection.
+ +
getPath(self, paths)
Get the path from the paths and path index.
+ +
getPointIndexPlusOne(self)
Get the point index plus one.
+ +

+ + + + + +
 
+Functions
       
addAroundGridPoint(arounds, gridPoint, gridPointInsetX, gridPointInsetY, gridPoints, gridSearchRadius, isBothOrNone, isDoubleJunction, isJunctionWide, paths, pixelTable, width)
Add the path around the grid point.
+
addInfillBoundary(infillBoundary, nestedRings)
Add infill boundary to the nested ring that contains it.
+
addLoop(infillWidth, infillPaths, loop, rotationPlaneAngle)
Add simplified path to fill.
+
addPath(infillWidth, infillPaths, path, rotationPlaneAngle)
Add simplified path to fill.
+
addPathIndexFirstSegment(gridPixel, pathIndexTable, pixelTable, segmentFirstPixel)
Add the path index of the closest segment found toward the second segment.
+
addPathIndexSecondSegment(gridPixel, pathIndexTable, pixelTable, segmentSecondPixel)
Add the path index of the closest segment found toward the second segment.
+
addPointOnPath(path, pathIndex, pixelTable, point, pointIndex, width)
Add a point to a path and the pixel table.
+
addPointOnPathIfFree(path, pathIndex, pixelTable, point, pointIndex, width)
Add the closest point to a path, if the point added to a path is free.
+
addSparseEndpoints(doubleInfillWidth, endpoints, horizontalSegmentsDictionary, horizontalSegmentsDictionaryKey, infillSolidity, removedEndpoints, solidSurfaceThickness, surroundingXIntersections)
Add sparse endpoints.
+
addSparseEndpointsFromSegment(doubleInfillWidth, endpoints, horizontalSegmentsDictionary, horizontalSegmentsDictionaryKey, infillSolidity, removedEndpoints, segment, solidSurfaceThickness, surroundingXIntersections)
Add sparse endpoints from a segment.
+
addYIntersectionPathToList(pathIndex, pointIndex, y, yIntersection, yIntersectionPaths)
Add the y intersection path to the y intersection paths.
+
compareDistanceFromCenter(self, other)
Get comparison in order to sort y intersections in ascending order of distance from the center.
+
comparePointIndexDescending(self, other)
Get comparison in order to sort y intersections in descending order of point index.
+
createExtraFillLoops(nestedRing, radius, radiusAround, shouldExtraLoopsBeAdded)
Create extra fill loops.
+
createFillForSurroundings(nestedRings, radius, radiusAround, shouldExtraLoopsBeAdded)
Create extra fill loops for nested rings.
+
getAdditionalLength(path, point, pointIndex)
Get the additional length added by inserting a point into a path.
+
getClosestOppositeIntersectionPaths(yIntersectionPaths)
Get the close to center paths, starting with the first and an additional opposite if it exists.
+
getCraftedText(fileName, gcodeText='', repository=None)
Fill the inset file or gcode text.
+
getCraftedTextFromText(gcodeText, repository=None)
Fill the inset gcode text.
+
getKeyIsInPixelTableAddValue(key, pathIndexTable, pixelTable)
Determine if the key is in the pixel table, and if it is and if the value is not None add it to the path index table.
+
getLowerLeftCorner(nestedRings)
Get the lower left corner from the nestedRings.
+
getNewRepository()
Get new repository.
+
getNonIntersectingGridPointLine(gridPointInsetX, isJunctionWide, paths, pixelTable, yIntersectionPath, width)
Get the points around the grid point that is junction wide that do not intersect.
+
getPlusMinusSign(number)
Get one if the number is zero or positive else negative one.
+
getWithLeastLength(path, point)
Insert a point into a path, at the index at which the path would be shortest.
+
getYIntersectionInsideYSegment(segmentFirstY, segmentSecondY, beginComplex, endComplex, x)
Get the y intersection inside the y segment if it does, else none.
+
insertGridPointPair(gridPoint, gridPointInsetX, gridPoints, isJunctionWide, paths, pixelTable, yIntersectionPath, width)
Insert a pair of points around the grid point is is junction wide, otherwise inset one point.
+
insertGridPointPairWithLinePath(gridPoint, gridPointInsetX, gridPoints, isJunctionWide, linePath, paths, pixelTable, yIntersectionPath, width)
Insert a pair of points around the grid point is is junction wide, otherwise inset one point.
+
insertGridPointPairs(gridPoint, gridPointInsetX, gridPoints, intersectionPathFirst, intersectionPathSecond, isBothOrNone, isJunctionWide, paths, pixelTable, width)
Insert a pair of points around a pair of grid points.
+
isAddedPointOnPathFree(path, pixelTable, point, pointIndex, width)
Determine if the point added to a path is intersecting the pixel table or the path.
+
isAddedPointOnPathIntersectingPath(begin, path, point, pointIndex)
Determine if the point added to a path is intersecting the path by checking line intersection.
+
isIntersectingLoopsPaths(loops, paths, pointBegin, pointEnd)
Determine if the segment between the first and second point is intersecting the loop list.
+
isPointAddedAroundClosest(layerInfillWidth, paths, pixelTable, removedEndpointPoint, width)
Add the closest removed endpoint to the path, with minimal twisting.
+
isSegmentAround(aroundSegmentsDictionary, aroundSegmentsDictionaryKey, segment)
Determine if there is another segment around.
+
isSegmentCompletelyInAnIntersection(segment, xIntersections)
Add sparse endpoints from a segment.
+
isSegmentInX(segment, xFirst, xSecond)
Determine if the segment overlaps within x.
+
isSharpCorner(beginComplex, centerComplex, endComplex)
Determine if the three complex points form a sharp corner.
+
isSidePointAdded(pixelTable, closestPath, closestPathIndex, closestPointIndex, layerInfillWidth, removedEndpointPoint, width)
Add side point along with the closest removed endpoint to the path, with minimal twisting.
+
main()
Display the fill dialog.
+
removeEndpoints(layerInfillWidth, paths, pixelTable, removedEndpoints, aroundWidth)
Remove endpoints which are added to the path.
+
setIsOutside(yCloseToCenterPath, yIntersectionPaths)
Determine if the yCloseToCenterPath is outside.
+
writeOutput(fileName, shouldAnalyze=True)
Fill an inset gcode file.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__date__ = '$Date: 2008/28/04 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.fillet.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.fillet.html new file mode 100644 index 0000000..758dc70 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.fillet.html @@ -0,0 +1,351 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.fillet + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.fillet ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/fillet.py
+

+Previous / Next / Contents +

+


+Fillet rounds the corners slightly in a variety of ways. This is to reduce corner blobbing and sudden extruder acceleration.
+
+The fillet manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Fillet
+
+
+Operation
+Settings
+  Fillet Procedure Choice
+    Arc Point
+    Arc Radius
+    Arc Segment
+    Bevel
+  Corner Feed Rate Multiplier
+  Fillet Radius over Perimeter Width
+  Reversal Slowdown over Perimeter Width
+  Use Intermediate Feed Rate in Corners
+Examples
+
+

Operation

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

Settings

+
+ +

Fillet Procedure Choice

+ +Default is 'Bevel''.
+
+

Arc Point

+ +When selected, the corners will be filleted with an arc using the gcode point form.
+
+

Arc Radius

+ +When selected, the corners will be filleted with an arc using the gcode radius form.
+
+

Arc Segment

+ +When selected, the corners will be filleted with an arc composed of several segments.
+
+

Bevel

+ +When selected, the corners will be beveled.
+
+

Corner Feed Rate Multiplier

+ +Default: 1.0
+
+Defines the ratio of the feed rate in corners over the original feed rate. With a high value the extruder will move quickly in corners, accelerating quickly and leaving a thin extrusion. With a low value, the extruder will move slowly in corners, accelerating gently and leaving a thick extrusion.
+
+

Fillet Radius over Perimeter Width

+ +Default is 0.35.
+
+Defines the width of the fillet.
+
+

Reversal Slowdown over Perimeter Width

+ +Default is 0.5.
+
+Defines how far before a path reversal the extruder will slow down. Some tools, like nozzle wipe, double back the path of the extruder and this option will add a slowdown point in that path so there won't be a sudden jerk at the end of the path. If the value is less than 0.1 a slowdown will not be added.
+
+

Use Intermediate Feed Rate in Corners

+ +Default is on.
+
+When selected, the feed rate entering the corner will be the average of the old feed rate and the new feed rate.
+
+

Examples

+
+ +The following examples fillet the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and fillet.py.
+
+> python fillet.py
+This brings up the fillet dialog.
+
+> python fillet.py Screw Holder Bottom.stl
+The fillet tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The fillet tool has created the file:
+.. Screw Holder Bottom_fillet.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+math
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
BevelSkein +
+
+
ArcSegmentSkein +
+
+
ArcPointSkein +
+
+
ArcRadiusSkein +
+
+
+
+
+
+
FilletRepository +
+

+ + + + + + + +
 
+class ArcPointSkein(ArcSegmentSkein)
   A class to arc point a skein of extrusions.
 
 
Method resolution order:
+
ArcPointSkein
+
ArcSegmentSkein
+
BevelSkein
+
+
+Methods defined here:
+
addArc(self, afterCenterDifferenceAngle, afterPoint, beforeCenterSegment, beforePoint, center)
Add an arc point to the filleted skein.
+ +
getRelativeCenter(self, centerMinusBeforeComplex)
Get the relative center.
+ +
+Methods inherited from ArcSegmentSkein:
+
splitPointGetAfter(self, location, nextLocation)
Fillet a point into arc segments and return the end of the last segment.
+ +
+Methods inherited from BevelSkein:
+
__init__(self)
+ +
addLinearMovePoint(self, feedRateMinute, point)
Add a gcode linear move, feedRate and newline to the output.
+ +
getCornerFeedRate(self)
Get the corner feed rate, which may be based on the intermediate feed rate.
+ +
getCraftedGcode(self, repository, gcodeText)
Parse gcode text and store the bevel gcode.
+ +
getExtruderOffReversalPoint(self, afterSegment, afterSegmentComplex, beforeSegment, beforeSegmentComplex, location)
If the extruder is off and the path is reversing, add intermediate slow points.
+ +
getNextLocation(self)
Get the next linear move.  Return none is none is found.
+ +
linearMove(self, splitLine)
Bevel a linear move.
+ +
parseInitialization(self, repository)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the bevel gcode.
+ +

+ + + + + + + +
 
+class ArcRadiusSkein(ArcPointSkein)
   A class to arc radius a skein of extrusions.
 
 
Method resolution order:
+
ArcRadiusSkein
+
ArcPointSkein
+
ArcSegmentSkein
+
BevelSkein
+
+
+Methods defined here:
+
getRelativeCenter(self, centerMinusBeforeComplex)
Get the relative center.
+ +
+Methods inherited from ArcPointSkein:
+
addArc(self, afterCenterDifferenceAngle, afterPoint, beforeCenterSegment, beforePoint, center)
Add an arc point to the filleted skein.
+ +
+Methods inherited from ArcSegmentSkein:
+
splitPointGetAfter(self, location, nextLocation)
Fillet a point into arc segments and return the end of the last segment.
+ +
+Methods inherited from BevelSkein:
+
__init__(self)
+ +
addLinearMovePoint(self, feedRateMinute, point)
Add a gcode linear move, feedRate and newline to the output.
+ +
getCornerFeedRate(self)
Get the corner feed rate, which may be based on the intermediate feed rate.
+ +
getCraftedGcode(self, repository, gcodeText)
Parse gcode text and store the bevel gcode.
+ +
getExtruderOffReversalPoint(self, afterSegment, afterSegmentComplex, beforeSegment, beforeSegmentComplex, location)
If the extruder is off and the path is reversing, add intermediate slow points.
+ +
getNextLocation(self)
Get the next linear move.  Return none is none is found.
+ +
linearMove(self, splitLine)
Bevel a linear move.
+ +
parseInitialization(self, repository)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the bevel gcode.
+ +

+ + + + + + + +
 
+class ArcSegmentSkein(BevelSkein)
   A class to arc segment a skein of extrusions.
 
 Methods defined here:
+
addArc(self, afterCenterDifferenceAngle, afterPoint, beforeCenterSegment, beforePoint, center)
Add arc segments to the filleted skein.
+ +
splitPointGetAfter(self, location, nextLocation)
Fillet a point into arc segments and return the end of the last segment.
+ +
+Methods inherited from BevelSkein:
+
__init__(self)
+ +
addLinearMovePoint(self, feedRateMinute, point)
Add a gcode linear move, feedRate and newline to the output.
+ +
getCornerFeedRate(self)
Get the corner feed rate, which may be based on the intermediate feed rate.
+ +
getCraftedGcode(self, repository, gcodeText)
Parse gcode text and store the bevel gcode.
+ +
getExtruderOffReversalPoint(self, afterSegment, afterSegmentComplex, beforeSegment, beforeSegmentComplex, location)
If the extruder is off and the path is reversing, add intermediate slow points.
+ +
getNextLocation(self)
Get the next linear move.  Return none is none is found.
+ +
linearMove(self, splitLine)
Bevel a linear move.
+ +
parseInitialization(self, repository)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the bevel gcode.
+ +

+ + + + + + + +
 
+class BevelSkein
   A class to bevel a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
addLinearMovePoint(self, feedRateMinute, point)
Add a gcode linear move, feedRate and newline to the output.
+ +
getCornerFeedRate(self)
Get the corner feed rate, which may be based on the intermediate feed rate.
+ +
getCraftedGcode(self, repository, gcodeText)
Parse gcode text and store the bevel gcode.
+ +
getExtruderOffReversalPoint(self, afterSegment, afterSegmentComplex, beforeSegment, beforeSegmentComplex, location)
If the extruder is off and the path is reversing, add intermediate slow points.
+ +
getNextLocation(self)
Get the next linear move.  Return none is none is found.
+ +
linearMove(self, splitLine)
Bevel a linear move.
+ +
parseInitialization(self, repository)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the bevel gcode.
+ +
splitPointGetAfter(self, location, nextLocation)
Bevel a point and return the end of the bevel.   should get complex for radius
+ +

+ + + + + + + +
 
+class FilletRepository
   A class to handle the fillet settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Fillet button has been clicked.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, gcodeText, repository=None)
Fillet a gcode linear move file or text.
+
getCraftedTextFromText(gcodeText, repository=None)
Fillet a gcode linear move text.
+
getNewRepository()
Get new repository.
+
main()
Display the fillet dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Fillet a gcode linear move file. Depending on the settings, either arcPoint, arcRadius, arcSegment, bevel or do nothing.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.flow.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.flow.html new file mode 100644 index 0000000..8dc575f --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.flow.html @@ -0,0 +1,151 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.flow + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.flow ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/flow.py
+

+Previous / Next / Contents +

+


+The flow script sets the flow rate by writing the M108 gcode.
+
+
+Operation
+Settings
+  Flow Rate
+Examples
+
+

Operation

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

Settings

+
+ +

Flow Rate

+ +Default is 210.
+
+Defines the flow rate which will be written following the M108 command. The flow rate is usually a PWM setting, but could be anything, like the rpm of the tool or the duty cycle of the tool.
+
+

Examples

+
+ +The following examples flow the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and flow.py.
+
+> python flow.py
+This brings up the flow dialog.
+
+> python flow.py Screw Holder Bottom.stl
+The flow tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The flow tool has created the file:
+.. Screw Holder Bottom_flow.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+
sys
+

+ + + + + +
 
+Classes
       
+
FlowRepository +
FlowSkein +
+

+ + + + + + + +
 
+class FlowRepository
   A class to handle the flow settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Flow button has been clicked.
+ +

+ + + + + + + +
 
+class FlowSkein
   A class to flow a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
addFlowRateLine(self)
Add flow rate line.
+ +
getCraftedGcode(self, gcodeText, flowRepository)
Parse gcode text and store the flow gcode.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the flow skein.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text='', flowRepository=None)
Flow the file or text.
+
getCraftedTextFromText(gcodeText, flowRepository=None)
Flow a gcode linear move text.
+
getNewRepository()
Get new repository.
+
main()
Display the flow dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Flow a gcode linear move file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.home.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.home.html new file mode 100644 index 0000000..3024ea4 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.home.html @@ -0,0 +1,160 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.home + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.home ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/home.py
+

+Previous / Next / Contents +

+


+Plugin to home the tool at beginning of each layer.
+
+The home manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Home
+
+
+Operation
+Settings
+  Name of Home File
+Examples
+
+

Operation

+
+ +The default 'Activate Home' checkbox is on. When it is on, the functions described below will work, when it is off, nothing will be done.
+
+

Settings

+
+ +

Name of Home File

+ +Default: home.gcode
+
+At the beginning of a each layer, home will add the commands of a gcode script with the name of the "Name of Home File" setting, if one exists. Home does not care if the text file names are capitalized, but some file systems do not handle file name cases properly, so to be on the safe side you should give them lower case names. Home looks for those files in the alterations folder in the .skeinforge folder in the home directory. If it doesn't find the file it then looks in the alterations folder in the skeinforge_plugins folder.
+
+

Examples

+
+ +The following examples home the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and home.py.
+
+> python home.py
+This brings up the home dialog.
+
+> python home.py Screw Holder Bottom.stl
+The home tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The home tool has created the file:
+.. Screw Holder Bottom_home.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+math
+
os
+fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+
skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
HomeRepository +
HomeSkein +
+

+ + + + + + + +
 
+class HomeRepository
   A class to handle the home settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Home button has been clicked.
+ +

+ + + + + + + +
 
+class HomeSkein
   A class to home a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
addFloat(self, begin, end)
Add dive to the original height.
+ +
addHomeTravel(self, splitLine)
Add the home travel gcode.
+ +
addHopUp(self, location)
Add hop to highest point.
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the home gcode.
+ +
parseInitialization(self, repository)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the bevel gcode.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text, repository=None)
Home a gcode linear move file or text.
+
getCraftedTextFromText(gcodeText, repository=None)
Home a gcode linear move text.
+
getNewRepository()
Get new repository.
+
main()
Display the home dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Home a gcode linear move file.  Chain home the gcode if it is not already homed.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.hop.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.hop.html new file mode 100644 index 0000000..f100da7 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.hop.html @@ -0,0 +1,172 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.hop + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.hop ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/hop.py
+

+Previous / Next / Contents +

+


+Hop is a script to raise the extruder when it is not extruding.
+
+Note:
+
+Note: In some cases where you have thin overhang this plugin can help solve the problem object being knocked off by the head
+
+The hop manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Hop
+
+
+Operation
+Settings
+  Hop Over Layer Thickness
+  Minimum Hop Angle
+Examples
+
+

Operation

+
+ +The default 'Activate Hop' checkbox is off.
+
+It is off because Vik and Nophead found better results without hopping. Numerous users reported better output without this plugin hence it is off by default.
+
+When activated the extruder will hop when traveling. When it is off, nothing will be done.
+
+

Settings

+
+ +

Hop Over Layer Thickness

+ +Default is one.
+
+Defines the ratio of the hop height over the layer height, this is the most important hop setting.
+
+

Minimum Hop Angle

+ +Default is 20 degrees.
+
+Defines the minimum angle that the path of the extruder will be raised. An angle of ninety means that the extruder will go straight up as soon as it is not extruding and a low angle means the extruder path will gradually rise to the hop height.
+
+

Examples

+
+ +The following examples hop the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and hop.py.
+
+> python hop.py
+This brings up the hop dialog.
+
+> python hop.py Screw Holder Bottom.stl
+The hop tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The hop tool has created the file:
+.. Screw Holder Bottom_hop.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+math
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
HopRepository +
HopSkein +
+

+ + + + + + + +
 
+class HopRepository
   A class to handle the hop settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Hop button has been clicked.
+ +

+ + + + + + + +
 
+class HopSkein
   A class to hop a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
Initialize
+ +
getCraftedGcode(self, gcodeText, hopRepository)
Parse gcode text and store the hop gcode.
+ +
getHopLine(self, line)
Get hopped gcode line.
+ +
isNextTravel(self)
Determine if there is another linear travel before the thread ends.
+ +
parseInitialization(self, hopRepository)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the bevel gcode.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text, hopRepository=None)
Hop a gcode linear move text.
+
getCraftedTextFromText(gcodeText, hopRepository=None)
Hop a gcode linear move text.
+
getNewRepository()
Get new repository.
+
main()
Display the hop dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Hop a gcode linear move file.  Chain hop the gcode if it is not already hopped.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.html new file mode 100644 index 0000000..40ae545 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.html @@ -0,0 +1,74 @@ + + +Python: package skeinforge_application.skeinforge_plugins.craft_plugins + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
alteration
+bottom
+carve
+chamber
+chop
+cleave
+clip
+coil
+comb
+cool
+dimension
+
drill
+export
+export_plugins (package)
+feed
+fill
+fillet
+flow
+home
+hop
+inset
+jitter
+
lash
+lift
+limit
+mill
+multiply
+oozebane
+outset
+preface
+raft
+scale
+skin
+
skirt
+smooth
+speed
+splodge
+stretch
+temperature
+tower
+unpause
+whittle
+widen
+wipe
+

+ + + + + +
 
+Data
       level = 3
+numberOfLevelsDeepInPackageHierarchy = 3
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.inset.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.inset.html new file mode 100644 index 0000000..af2d507 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.inset.html @@ -0,0 +1,210 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.inset + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.inset ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/inset.py
+

+Previous / Next / Contents +

+


+Inset will inset the outside outlines by half the edge width, and outset the inside outlines by the same amount.
+
+The inset manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Inset
+
+
+Settings
+  Add Custom Code for Temperature Reading
+  Infill in Direction of Bridge
+  Loop Order Choice
+    Ascending Area
+    Descending Area
+  Overlap Removal Width over Perimeter Width
+  Turn Extruder Heater Off at Shut Down
+Examples
+
+

Settings

+
+ +

Add Custom Code for Temperature Reading

+ +Default is on.
+
+When selected, the M105 custom code for temperature reading will be added at the beginning of the file.
+
+

Infill in Direction of Bridge

+ +Default is on.
+
+When selected, the infill will be in the direction of any bridge across a gap, so that the fill will be able to span a bridge easier.
+
+

Loop Order Choice

+ +Default loop order choice is 'Ascending Area'.
+
+When overlap is to be removed, for each loop, the overlap is checked against the list of loops already extruded. If the latest loop overlaps an already extruded loop, the overlap is removed from the latest loop. The loops are ordered according to their areas.
+
+

Ascending Area

+ +When selected, the loops will be ordered in ascending area. With thin walled parts, if overlap is being removed the outside of the container will not be extruded. Holes will be the correct size.
+
+

Descending Area

+ +When selected, the loops will be ordered in descending area. With thin walled parts, if overlap is being removed the inside of the container will not be extruded. Holes will be missing the interior wall so they will be slightly wider than model size.
+
+

Overlap Removal Width over Perimeter Width

+ +Default is 0.6.
+
+Defines the ratio of the overlap removal width over the edge width. Any part of the extrusion that comes within the overlap removal width of another is removed. This is to prevent the extruder from depositing two extrusions right beside each other. If the 'Overlap Removal Width over Perimeter Width' is less than 0.2, the overlap will not be removed.
+
+

Turn Extruder Heater Off at Shut Down

+ +Default is on.
+
+When selected, the M104 S0 gcode line will be added to the end of the file to turn the extruder heater off by setting the extruder heater temperature to 0.
+
+

Examples

+
+ +The following examples inset the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and inset.py.
+
+> python inset.py
+This brings up the inset dialog.
+
+> python inset.py Screw Holder Bottom.stl
+The inset tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The inset tool has created the file:
+.. Screw Holder Bottom_inset.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+cmath
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+math
+
os
+fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
InsetRepository +
InsetSkein +
+

+ + + + + + + +
 
+class InsetRepository
   A class to handle the inset settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Inset button has been clicked.
+ +

+ + + + + + + +
 
+class InsetSkein
   A class to inset a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
Initialize.
+ +
addGcodeFromPerimeterPaths(self, isIntersectingSelf, loop, loopLayer, loopLists, radius)
Add the edge paths to the output.
+ +
addGcodeFromRemainingLoop(self, loop, loopLayer, loopLists, radius)
Add the remainder of the loop which does not overlap the alreadyFilledArounds loops.
+ +
addGcodePerimeterBlockFromRemainingLoop(self, loop, loopLayer, loopLists, radius)
Add the perimter block remainder of the loop which does not overlap the alreadyFilledArounds loops.
+ +
addInitializationToOutput(self)
Add initialization gcode to the output.
+ +
addInset(self, loopLayer)
Add inset to the layer.
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the bevel gcode.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the inset skein.
+ +

+ + + + + +
 
+Functions
       
addAlreadyFilledArounds(alreadyFilledArounds, loop, radius)
Add already filled loops around loop to alreadyFilledArounds.
+
addSegmentOutline(isThick, outlines, pointBegin, pointEnd, width)
Add a diamond or hexagonal outline for a line segment.
+
getBridgeDirection(belowLoops, layerLoops, radius)
Get span direction for the majority of the overhanging extrusion edge, if any.
+
getCraftedText(fileName, text='', repository=None)
Inset the preface file or text.
+
getCraftedTextFromText(gcodeText, repository=None)
Inset the preface gcode text.
+
getDoubledRoundZ(overhangingSegment, segmentRoundZ)
Get doubled plane angle around z of the overhanging segment.
+
getInteriorSegments(loops, segments)
Get segments inside the loops.
+
getIsIntersectingWithinList(loop, loopList)
Determine if the loop is intersecting or is within the loop list.
+
getNewRepository()
Get new repository.
+
getOverhangDirection(belowOutsetLoops, segmentBegin, segmentEnd)
Add to span direction from the endpoint segments which overhang the layer below.
+
getSegmentsFromLoopListsPoints(loopLists, pointBegin, pointEnd)
Get endpoint segments from the beginning and end of a line segment.
+
isCloseToLast(paths, point, radius)
Determine if the point is close to the last point of the last path.
+
isIntersectingItself(loop, width)
Determine if the loop is intersecting itself.
+
isIntersectingWithinLists(loop, loopLists)
Determine if the loop is intersecting or is within the loop lists.
+
main()
Display the inset dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Inset the carving of a gcode file.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__date__ = '$Date: 2008/02/05 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.jitter.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.jitter.html new file mode 100644 index 0000000..396b9bf --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.jitter.html @@ -0,0 +1,169 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.jitter + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.jitter ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/jitter.py
+

+Previous / Next / Contents +

+


+This craft tool jitters the loop end position to a different place on each layer to prevent a ridge from being created on the side of the object.
+
+The jitter manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Jitter
+
+
+Operation
+Settings
+  Jitter Over Perimeter Width
+Examples
+
+

Operation

+
+ +The default 'Activate Jitter' checkbox is on. When it is on, the functions described below will work, when it is off, nothing will be done.
+
+

Settings

+
+ +

Jitter Over Perimeter Width

+ +Default: 2
+
+Defines the amount the loop ends will be jittered over the edge width. A high value means the loops will start all over the place and a low value means loops will start at roughly the same place on each layer.
+
+For example if you turn jitter off and print a cube every outside shell on the cube will start from exactly the same point so you will have a visible "mark/line/seam" on the side of the cube. Using the jitter tool you move that start point around hence you avoid that visible seam.
+
+
+

Examples

+
+ +The following examples jitter the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and jitter.py.
+
+> python jitter.py
+This brings up the jitter dialog.
+
+> python jitter.py Screw Holder Bottom.stl
+The jitter tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The jitter tool has created the file:
+.. Screw Holder Bottom_jitter.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+
math
+fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+
skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
JitterRepository +
JitterSkein +
+

+ + + + + + + +
 
+class JitterRepository
   A class to handle the jitter settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Jitter button has been clicked.
+ +

+ + + + + + + +
 
+class JitterSkein
   A class to jitter a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
Initialize.
+ +
addGcodeFromThreadZ(self, thread, z)
Add a gcode thread to the output.
+ +
addGcodeMovementZ(self, feedRateMinute, point, z)
Add a movement to the output.
+ +
addGcodePathZ(self, feedRateMinute, path, z)
Add a gcode path, without modifying the extruder, to the output.
+ +
addTailoredLoopPath(self)
Add a clipped and jittered loop path.
+ +
getCraftedGcode(self, jitterRepository, gcodeText)
Parse gcode text and store the jitter gcode.
+ +
parseInitialization(self, jitterRepository)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line, jitter it and add it to the jitter skein.
+ +
setFeedRateLocationLoopPath(self, line, splitLine)
Set the feedRateMinute, oldLocation and loopPath.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text, jitterRepository=None)
Jitter a gcode linear move text.
+
getCraftedTextFromText(gcodeText, jitterRepository=None)
Jitter a gcode linear move text.
+
getJitteredLoop(jitterDistance, jitterLoop)
Get a jittered loop path.
+
getNewRepository()
Get new repository.
+
isLoopNumberEqual(betweenX, betweenXIndex, loopNumber)
Determine if the loop number is equal.
+
main()
Display the jitter dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Jitter a gcode linear move file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.lash.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.lash.html new file mode 100644 index 0000000..98f8e62 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.lash.html @@ -0,0 +1,165 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.lash + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.lash ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/lash.py
+

+Previous / Next / Contents +

+


+Lash is a script to partially compensate for the backlash of the tool head.
+
+The lash manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Lash
+
+The lash tool is ported from Erik de Bruijn's 3D-to-5D-Gcode php GPL'd script at:
+http://objects.reprap.org/wiki/3D-to-5D-Gcode.php
+
+The default values are from the settings in Erik's 3D-to-5D-Gcode, I believe the settings are used on his Darwin reprap.
+
+
+Operation
+Settings
+  X Backlash
+  Y Backlash
+Examples
+
+

Operation

+
+ +The default 'Activate Lash' checkbox is off. When it is on, the functions described below will work, when it is off, nothing will be done.
+
+

Settings

+
+ +

X Backlash

+ +Default is 0.2 millimeters.
+
+Defines the distance the tool head will be lashed in the X direction.
+
+

Y Backlash

+ +Default is 0.2 millimeters.
+
+Defines the distance the tool head will be lashed in the Y direction.
+
+

Examples

+
+ +The following examples lash the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and lash.py.
+
+> python lash.py
+This brings up the lash dialog.
+
+> python lash.py Screw Holder Bottom.stl
+The lash tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The lash tool has created the file:
+.. Screw Holder Bottom_lash.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+
fabmetheus_utilities.gcodec
+fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+
skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
LashRepository +
LashSkein +
+

+ + + + + + + +
 
+class LashRepository
   A class to handle the lash settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Lash button has been clicked.
+ +

+ + + + + + + +
 
+class LashSkein
   A class to lash a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
getCraftedGcode(self, gcodeText, lashRepository)
Parse gcode text and store the lash gcode.
+ +
getLashedLine(self, line, location, splitLine)
Get lashed gcode line.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLash(self, line)
Parse a gcode line and add it to the lash skein.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text, lashRepository=None)
Get a lashed gcode linear move text.
+
getCraftedTextFromText(gcodeText, lashRepository=None)
Get a lashed gcode linear move text from text.
+
getNewRepository()
Get new repository.
+
main()
Display the lash dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Lash a gcode linear move file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.lift.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.lift.html new file mode 100644 index 0000000..5d1a4cd --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.lift.html @@ -0,0 +1,161 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.lift + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.lift ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/lift.py
+

+Previous / Next / Contents +

+


+Lift will change the altitude of the cutting tool when it is on so that it will cut through the slab at the correct altitude. It will also lift the gcode when the tool is off so that the cutting tool will clear the top of the slab.
+
+
+Operation
+Settings
+  Cutting Lift over Layer Step
+  Clearance above Top
+Examples
+
+

Operation

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

Settings

+
+ +

Cutting Lift over Layer Step

+ +Default is minus 0.5, because the end mill is the more common tool.
+
+Defines the ratio of the amount the cutting tool will be lifted over the layer step. If whittle is off the layer step will be the layer height, if it is on, it will be the layer step from the whittle gcode. If the cutting tool is like an end mill, where the cutting happens until the end of the tool, then the 'Cutting Lift over Layer Step' should be minus 0.5, so that the end mill cuts to the bottom of the slab. If the cutting tool is like a laser, where the cutting happens around the focal point. the 'Cutting Lift over Layer Step' should be zero, so that the cutting action will be focused in the middle of the slab.
+
+

Clearance above Top

+ +Default is 5 millimeters.
+
+Defines the distance above the top of the slab the cutting tool will be lifted when will tool is off so that the cutting tool will clear the top of the slab.
+
+

Examples

+
+ +The following examples lift the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and lift.py.
+
+> python lift.py
+This brings up the lift dialog.
+
+> python lift.py Screw Holder Bottom.stl
+The lift tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The lift tool has created the file:
+.. Screw Holder Bottom_lift.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+
fabmetheus_utilities.gcodec
+fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+
skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
LiftRepository +
LiftSkein +
+

+ + + + + + + +
 
+class LiftRepository
   A class to handle the lift settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Lift button has been clicked.
+ +

+ + + + + + + +
 
+class LiftSkein
   A class to lift a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
addPreviousInactiveMovementLineIfNecessary(self)
Add the previous inactive movement line if necessary.
+ +
getCraftedGcode(self, liftRepository, gcodeText)
Parse gcode text and store the lift gcode.
+ +
getLinearMove(self, line, location, splitLine)
Get the linear move.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the lift skein.
+ +
setMaximumZ(self)
Set maximum  z.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text='', liftRepository=None)
Lift the preface file or text.
+
getCraftedTextFromText(gcodeText, liftRepository=None)
Lift the preface gcode text.
+
getNewRepository()
Get new repository.
+
main()
Display the lift dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Lift the carving of a gcode file.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__date__ = '$Date: 2008/02/05 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.limit.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.limit.html new file mode 100644 index 0000000..4463e10 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.limit.html @@ -0,0 +1,164 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.limit + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.limit ($Date: 2008/28/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/limit.py
+

+Previous / Next / Contents +

+


+This plugin limits the feed rate of the tool head, so that the stepper motors are not driven too fast and skip steps.
+
+The limit manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Limit
+
+The maximum z feed rate is defined in speed.
+
+
+Operation
+Settings
+  Maximum Initial Feed Rate
+Examples
+
+

Operation

+
+ +The default 'Activate Limit' checkbox is on. When it is on, the functions described below will work, when it is off, nothing will be done.
+
+

Settings

+
+ +

Maximum Initial Feed Rate

+ +Default is one millimeter per second.
+
+Defines the maximum speed of the inital tool head move.
+
+

Examples

+
+ +The following examples limit the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and limit.py.
+
+> python limit.py
+This brings up the limit dialog.
+
+> python limit.py Screw Holder Bottom.stl
+The limit tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The limit tool has created the file:
+.. Screw Holder Bottom_limit.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
skeinforge_application.skeinforge_plugins.craft_plugins.__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+
fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+math
+os
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+
sys
+

+ + + + + +
 
+Classes
       
+
LimitRepository +
LimitSkein +
+

+ + + + + + + +
 
+class LimitRepository
   A class to handle the limit settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Limit button has been clicked.
+ +

+ + + + + + + +
 
+class LimitSkein
   A class to limit a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the limit gcode.
+ +
getLimitedInitialMovement(self, line, splitLine)
Get a limited linear movement.
+ +
getZLimitedLine(self, deltaZ, distance, line, splitLine)
Get a replaced z limited gcode movement line.
+ +
getZLimitedLineArc(self, line, splitLine)
Get a replaced z limited gcode arc movement line.
+ +
getZLimitedLineLinear(self, line, location, splitLine)
Get a replaced z limited gcode linear movement line.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, lineIndex)
Parse a gcode line and add it to the limit skein.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, gcodeText='', repository=None)
Limit a gcode file or text.
+
getCraftedTextFromText(gcodeText, repository=None)
Limit a gcode text.
+
getNewRepository()
Get new repository.
+
main()
Display the limit dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Limit a gcode file.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__date__ = '$Date: 2008/28/04 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.mill.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.mill.html new file mode 100644 index 0000000..c3c16cf --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.mill.html @@ -0,0 +1,231 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.mill + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.mill ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/mill.py
+

+Previous / Next / Contents +

+


+Mill is a script to mill the outlines.
+
+
+Operation
+Settings
+  Add Loops
+    Add Inner Loops
+    Add Outer Loops
+  Cross Hatch
+  Loop Outset
+    Loop Inner Outset over Perimeter Width
+    Loop Outer Outset over Perimeter Width
+  Mill Width over Perimeter Width
+Examples
+
+

Operation

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

Settings

+
+ +

Add Loops

+ +

Add Inner Loops

+ +Default is on.
+
+When selected, the inner milling loops will be added.
+
+

Add Outer Loops

+ +Default is on.
+
+When selected, the outer milling loops will be added.
+
+

Cross Hatch

+ +Default is on.
+
+When selected, there will be alternating horizontal and vertical milling paths, if it is off there will only be horizontal milling paths.
+
+

Loop Outset

+ +

Loop Inner Outset over Perimeter Width

+ +Default is 0.5.
+
+Defines the ratio of the amount the inner milling loop will be outset over the edge width.
+
+

Loop Outer Outset over Perimeter Width

+ +Default is one.
+
+Defines the ratio of the amount the outer milling loop will be outset over the edge width. The 'Loop Outer Outset over Perimeter Width' ratio should be greater than the 'Loop Inner Outset over Perimeter Width' ratio.
+
+

Mill Width over Perimeter Width

+ +Default is one.
+
+Defines the ratio of the mill line width over the edge width. If the ratio is one, all the material will be milled. The greater the 'Mill Width over Perimeter Width' the farther apart the mill lines will be and so less of the material will be directly milled, the remaining material might still be removed in chips if the ratio is not much greater than one.
+
+

Examples

+
+ +The following examples mill the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and mill.py.
+
+> python mill.py
+This brings up the mill dialog.
+
+> python mill.py Screw Holder Bottom.stl
+The mill tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The mill tool has created the file:
+Screw Holder Bottom_mill.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+
fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+math
+os
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+
sys
+fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
Average +
MillRepository +
MillSkein +
+

+ + + + + + + +
 
+class Average
   A class to hold values and get the average.
 
 Methods defined here:
+
__init__(self)
+ +
addValue(self, value)
Add a value to the total and the number of values.
+ +
getAverage(self)
Get the average.
+ +
reset(self)
Set the number of values and the total to the default.
+ +

+ + + + + + + +
 
+class MillRepository
   A class to handle the mill settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Mill button has been clicked.
+ +

+ + + + + + + +
 
+class MillSkein
   A class to mill a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
addGcodeFromLoops(self, loops, z)
Add gcode from loops.
+ +
addGcodeFromThreadZ(self, thread, z)
Add a thread to the output.
+ +
addMillThreads(self)
Add the mill threads to the skein.
+ +
addSegmentTableLoops(self, boundaryLayerIndex)
Add the segment tables and loops to the boundary.
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the mill gcode.
+ +
getHorizontalSegmentTableForXIntersectionsTable(self, xIntersectionsTable)
Get the horizontal segment table from the xIntersectionsTable.
+ +
getHorizontalXIntersectionsTable(self, loops)
Get the horizontal x intersections table from the loops.
+ +
getVerticalSegmentTableForXIntersectionsTable(self, xIntersectionsTable)
Get the vertical segment table from the xIntersectionsTable which has the x and y swapped.
+ +
parseBoundaries(self)
Parse the boundaries and add them to the boundary layers.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the mill skein.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, gcodeText='', repository=None)
Mill the file or gcodeText.
+
getCraftedTextFromText(gcodeText, repository=None)
Mill a gcode linear move gcodeText.
+
getNewRepository()
Get new repository.
+
getPointsFromSegmentTable(segmentTable)
Get the points from the segment table.
+
isPointOfTableInLoop(loop, pointTable)
Determine if a point in the point table is in the loop.
+
main()
Display the mill dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Mill a gcode linear move file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.multiply.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.multiply.html new file mode 100644 index 0000000..07b2f50 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.multiply.html @@ -0,0 +1,206 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.multiply + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.multiply ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/multiply.py
+

+Previous / Next / Contents +

+


+The multiply plugin will take a single object and create an array of objects. It is used when you want to print single object multiple times in a single pass.
+
+You can also position any object using this plugin by setting the center X and center Y to the desired coordinates (0,0 for the center of the print_bed) and setting the number of rows and columns to 1 (effectively setting a 1x1 matrix - printing only a single object).
+
+The multiply manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Multiply
+
+Besides using the multiply tool, another way of printing many copies of the model is to duplicate the model in Art of Illusion, however many times you want, with the appropriate offsets. Then you can either use the Join Objects script in the scripts submenu to create a combined shape or you can export the whole scene as an xml file, which skeinforge can then slice.
+
+
+Operation
+Settings
+  Center
+    Center X
+    Center Y
+  Number of Cells
+    Number of Columns
+    Number of Rows
+  Reverse Sequence every Odd Layer
+  Separation over Perimeter Width
+Examples
+
+

Operation

+
+ +The default 'Activate Multiply' checkbox is on. When it is on, the functions described below will work, when it is off, nothing will be done.
+
+

Settings

+
+ +

Center

+ +Default is the origin.
+
+The center of the shape will be moved to the "Center X" and "Center Y" coordinates.
+
+

Center X

+ +

Center Y

+ +
+

Number of Cells

+ +

Number of Columns

+ +Default is one.
+
+Defines the number of columns in the array table.
+
+

Number of Rows

+ +Default is one.
+
+Defines the number of rows in the table.
+
+

Reverse Sequence every Odd Layer

+ +Default is off.
+
+When selected the build sequence will be reversed on every odd layer so that the tool will travel less. The problem is that the builds would be made with different amount of time to cool, so some would be too hot and some too cold, which is why the default is off.
+
+

Separation over Perimeter Width

+ +Default is fifteen.
+
+Defines the ratio of separation between the shape copies over the edge width.
+
+

Examples

+
+ +The following examples multiply the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and multiply.py.
+
+> python multiply.py
+This brings up the multiply dialog.
+
+> python multiply.py Screw Holder Bottom.stl
+The multiply tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The multiply tool has created the file:
+.. Screw Holder Bottom_multiply.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+
math
+fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+
skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
MultiplyRepository +
MultiplySkein +
+

+ + + + + + + +
 
+class MultiplyRepository
   A class to handle the multiply settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Multiply button has been clicked.
+ +

+ + + + + + + +
 
+class MultiplySkein
   A class to multiply a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
addElement(self, offset)
Add moved element to the output.
+ +
addLayer(self)
Add multiplied layer to the output.
+ +
addRemoveThroughLayer(self)
Parse gcode initialization and store the parameters.
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the multiply gcode.
+ +
getMovedLocationSetOldLocation(self, offset, splitLine)
Get the moved location and set the old location.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the multiply skein.
+ +
setCorners(self)
Set maximum and minimum corners and z.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text='', repository=None)
Multiply the fill file or text.
+
getCraftedTextFromText(gcodeText, repository=None)
Multiply the fill text.
+
getNewRepository()
Get new repository.
+
main()
Display the multiply dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Multiply a gcode linear move file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.oozebane.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.oozebane.html new file mode 100644 index 0000000..ebc8a80 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.oozebane.html @@ -0,0 +1,254 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.oozebane + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.oozebane ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/oozebane.py
+

+Previous / Next / Contents +

+


+Oozebane is a script to turn off the extruder before the end of a thread and turn it on before the beginning.
+
+The oozebane manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Oozebane
+
+After oozebane turns the extruder on, it slows the feed rate down where the thread starts. Then it speeds it up in steps so in theory the thread will remain at roughly the same thickness from the beginning.
+
+
+Operation
+Settings
+  After Startup Distance
+  Early Shutdown Distance
+  Early Startup Maximum Distance
+  Early Startup Distance Constant
+  First Early Startup Distance
+  Minimum Distance for Early Shutdown
+  Minimum Distance for Early Startup
+  Slowdown Startup Steps
+Examples
+
+

Operation

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

Settings

+
+ +

After Startup Distance

+ +Default is 1.2.
+
+When oozebane reaches the point where the extruder would of turned on, it slows down so that the thread will be thick at that point. Afterwards it speeds the extruder back up to operating speed. The speed up distance is the "After Startup Distance".
+
+

Early Shutdown Distance

+ +Default is 1.2.
+
+Defines the distance before the end of the thread that the extruder will be turned off. It is the most important oozebane setting. A higher distance means the extruder will turn off sooner and the end of the line will be thinner.
+
+

Early Startup Maximum Distance

+ +Default is 1.2.
+
+Defines the maximum distance before the thread starts that the extruder will be turned on
+
+

Early Startup Distance Constant

+ +Default is twenty.
+
+The longer the extruder has been off, the earlier the extruder will turn back on, the ratio is one minus one over e to the power of the distance the extruder has been off over the "Early Startup Distance Constant".
+
+

First Early Startup Distance

+ +Default is twenty five.
+
+Defines the distance before the first thread starts that the extruder will be turned off. This value should be high because, according to Marius, the extruder takes a second or two to extrude when starting for the first time.
+
+

Minimum Distance for Early Shutdown

+ +Default is zero.
+
+Defines the minimum distance that the extruder has to be off after the thread end for the early shutdown feature to activate.
+
+

Minimum Distance for Early Startup

+ +Default is zero.
+
+Defines the minimum distance that the extruder has to be off before the thread begins for the early start up feature to activate.
+
+

Slowdown Startup Steps

+ +Default is three.
+
+When oozebane turns the extruder off, it slows the feed rate down in steps so in theory the thread will remain at roughly the same thickness until the end. The "Slowdown Startup Steps" setting is the number of steps, the more steps the smaller the size of the step that the feed rate will be decreased and the larger the size of the resulting gcode file.
+
+

Examples

+
+ +The following examples oozebane the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and oozebane.py.
+
+> python oozebane.py
+This brings up the oozebane dialog.
+
+> python oozebane.py Screw Holder Bottom.stl
+The oozebane tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The oozebane tool has created the file:
+.. Screw Holder Bottom_oozebane.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+math
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
OozebaneRepository +
OozebaneSkein +
+

+ + + + + + + +
 
+class OozebaneRepository
   A class to handle the oozebane settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Oozebane button has been clicked.
+ +

+ + + + + + + +
 
+class OozebaneSkein
   A class to oozebane a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
addAfterStartupLine(self, splitLine)
Add the after startup lines.
+ +
addLineSetShutdowns(self, line)
Add a line and set the shutdown variables.
+ +
getActiveFeedRateRatio(self)
Get the feed rate of the first active move over the operating feed rate.
+ +
getAddAfterStartupLines(self, line)
Get and / or add after the startup lines.
+ +
getAddBeforeStartupLines(self, line)
Get and / or add before the startup lines.
+ +
getAddShutSlowDownLine(self, line)
Add the shutdown and slowdown lines.
+ +
getAddShutSlowDownLines(self, line)
Get and / or add the shutdown and slowdown lines.
+ +
getCraftedGcode(self, gcodeText, oozebaneRepository)
Parse gcode text and store the oozebane gcode.
+ +
getDistanceAfterThreadBeginning(self)
Get the distance after the beginning of the thread.
+ +
getDistanceToExtruderOffCommand(self, remainingDistance)
Get the distance to the word.
+ +
getDistanceToThreadBeginning(self)
Get the distance to the beginning of the thread.
+ +
getDistanceToThreadBeginningAfterThreadEnd(self, remainingDistance)
Get the distance to the thread beginning after the end of this thread.
+ +
getDistanceToThreadEnd(self)
Get the distance to the end of the thread.
+ +
getLinearMoveWithFeedRate(self, feedRate, location)
Get a linear move line with the feed rate.
+ +
getLinearMoveWithFeedRateSplitLine(self, feedRate, splitLine)
Get a linear move line with the feed rate and split line.
+ +
getOozebaneLine(self, line)
Get oozebaned gcode line.
+ +
getShutdownFlowRateMultiplier(self, along, numberOfDistances)
Get the shut down flow rate multipler.
+ +
getStartupFlowRateMultiplier(self, along, numberOfDistances)
Get the startup flow rate multipler.
+ +
isClose(self, location, otherLocation)
Determine if the location is close to the other location.
+ +
isCloseToEither(self, location, otherLocationFirst, otherLocationSecond)
Determine if the location is close to the other locations.
+ +
isDistanceAfterThreadBeginningGreater(self)
Determine if the distance after the thread beginning is greater than the step index after startup distance.
+ +
parseInitialization(self, oozebaneRepository)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the bevel gcode.
+ +
setAfterStartupFlowRates(self, afterStartupRatio)
Set the after startup flow rates.
+ +
setEarlyShutdown(self)
Set the early shutdown variables.
+ +
setEarlyShutdownFlowRates(self, earlyShutdownRatio)
Set the extrusion width.
+ +
setEarlyStartupDistance(self, splitLine)
Set the early startup distance.
+ +
setExtrusionWidth(self, oozebaneRepository)
Set the extrusion width.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text, oozebaneRepository=None)
Oozebane a gcode linear move file or text.
+
getCraftedTextFromText(gcodeText, oozebaneRepository=None)
Oozebane a gcode linear move text.
+
getNewRepository()
Get new repository.
+
main()
Display the oozebane dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Oozebane a gcode linear move file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.outset.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.outset.html new file mode 100644 index 0000000..80b55f7 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.outset.html @@ -0,0 +1,144 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.outset + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.outset ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/outset.py
+

+Previous / Next / Contents +

+


+Outset outsets the edges of the slices of a gcode file. The outside edges will be outset by half the edge width, and the inside edges will be inset by half the edge width. Outset is needed for subtractive machining, like cutting or milling.
+
+
+Operation
+Examples
+
+

Operation

+
+ +The default 'Activate Outset' checkbox is on. When it is on, the gcode will be outset, when it is off, the gcode will not be changed.
+
+

Examples

+
+ +The following examples outset the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and outset.py.
+
+> python outset.py
+This brings up the outset dialog.
+
+> python outset.py Screw Holder Bottom.stl
+The outset tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The outset tool has created the file:
+.. Screw Holder Bottom_outset.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
OutsetRepository +
OutsetSkein +
+

+ + + + + + + +
 
+class OutsetRepository
   A class to handle the outset settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Outset button has been clicked.
+ +

+ + + + + + + +
 
+class OutsetSkein
   A class to outset a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
addGcodeFromRemainingLoop(self, loop, radius, z)
Add the remainder of the loop.
+ +
addOutset(self, loopLayer)
Add outset to the layer.
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the bevel gcode.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, lineIndex)
Parse a gcode line and add it to the outset skein.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text='', repository=None)
Outset the preface file or text.
+
getCraftedTextFromText(gcodeText, repository=None)
Outset the preface gcode text.
+
getNewRepository()
Get new repository.
+
main()
Display the outset dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Outset the carving of a gcode file.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__date__ = '$Date: 2008/02/05 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.preface.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.preface.html new file mode 100644 index 0000000..a8e24e0 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.preface.html @@ -0,0 +1,196 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.preface + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.preface ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/preface.py
+

+Previous / Next / Contents +

+


+Preface converts the svg slices into gcode extrusion layers, optionally with home, positioning, turn off, and unit commands.
+
+The preface manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Preface
+
+
+Settings
+  Meta
+  Set Positioning to Absolute
+  Set Units to Millimeters
+  Start at Home
+  Turn Extruder Off
+    Turn Extruder Off at Shut Down
+    Turn Extruder Off at Start Up
+Examples
+
+

Settings

+
+ +

Meta

+ +Default is empty.
+
+The 'Meta' field is to add meta tags or a note to all your files. Whatever is in that field will be added in a meta tagged line to the output.
+
+

Set Positioning to Absolute

+ +Default is on.
+
+When selected, preface will add the G90 command to set positioning to absolute.
+
+

Set Units to Millimeters

+ +Default is on.
+
+When selected, preface will add the G21 command to set the units to millimeters.
+
+

Start at Home

+ +Default is off.
+
+When selected, the G28 go to home gcode will be added at the beginning of the file.
+
+

Turn Extruder Off

+ +

Turn Extruder Off at Shut Down

+ +Default is on.
+
+When selected, the M103 turn extruder off gcode will be added at the end of the file.
+
+

Turn Extruder Off at Start Up

+ +Default is on.
+
+When selected, the M103 turn extruder off gcode will be added at the beginning of the file.
+
+

Examples

+
+ +The following examples preface the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and preface.py.
+
+> python preface.py
+This brings up the preface dialog.
+
+> python preface.py Screw Holder Bottom.stl
+The preface tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The preface tool has created the file:
+.. Screw Holder Bottom_preface.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+
fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+os
+fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+fabmetheus_utilities.svg_writer
+
sys
+

+ + + + + +
 
+Classes
       
+
PrefaceRepository +
PrefaceSkein +
+

+ + + + + + + +
 
+class PrefaceRepository
   A class to handle the preface settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Preface button has been clicked.
+ +

+ + + + + + + +
 
+class PrefaceSkein
   A class to preface a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
addInitializationToOutput(self)
Add initialization gcode to the output.
+ +
addPreface(self, loopLayer)
Add preface to the carve layer.
+ +
addShutdownToOutput(self)
Add shutdown gcode to the output.
+ +
addToolSettingLines(self, pluginName)
Add tool setting lines.
+ +
getCraftedGcode(self, repository, gcodeText)
Parse gcode text and store the bevel gcode.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text='', repository=None)
Preface and convert an svg file or text.
+
getCraftedTextFromText(text, repository=None)
Preface and convert an svg text.
+
getNewRepository()
Get new repository.
+
main()
Display the preface dialog.
+
strftime(...)
strftime(format[, tuple]) -> string

+Convert a time tuple to a string according to a format specification.
+See the library reference manual for formatting codes. When the time tuple
+is not present, current time as returned by localtime() is used.
+
writeOutput(fileName, shouldAnalyze=True)
Preface the carving of a gcode file.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__date__ = '$Date: 2008/02/05 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.raft.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.raft.html new file mode 100644 index 0000000..5bd139c --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.raft.html @@ -0,0 +1,470 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.raft + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.raft ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/raft.py
+

+Previous / Next / Contents +

+


+Raft is a plugin to create a raft, elevate the nozzle and set the temperature. A raft is a flat base structure on top of which your object is being build and has a few different purposes. It fills irregularities like scratches and pits in your printbed and gives you a nice base parallel to the printheads movement. It also glues your object to the bed so to prevent warping in bigger object. The rafts base layer performs these tricks while the sparser interface layer(s) help you removing the object from the raft after printing. It is based on the Nophead's reusable raft, which has a base layer running one way, and a couple of perpendicular layers above. Each set of layers can be set to a different temperature. There is the option of having the extruder orbit the raft for a while, so the heater barrel has time to reach a different temperature, without ooze accumulating around the nozzle.
+
+The raft manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Raft
+
+The important values for the raft settings are the temperatures of the raft, the first layer and the next layers. These will be different for each material. The default settings for ABS, HDPE, PCL & PLA are extrapolated from Nophead's experiments.
+
+You don't necessarily need a raft and especially small object will print fine on a flat bed without one, sometimes its even better when you need a water tight base to print directly on the bed. If you want to only set the temperature or only create support material or only elevate the nozzle without creating a raft, set the Base Layers and Interface Layers to zero.
+
+<gallery perRow="1">
+Image:Raft.jpg|Raft
+</gallery>
+
+Example of a raft on the left with the interface layers partially removed exposing the base layer. Notice that the first line of the base is rarely printed well because of the startup time of the extruder. On the right you see an object with its raft still attached.
+
+The Raft panel has some extra settings, it probably made sense to have them there but they have not that much to do with the actual Raft. First are the Support material settings. Since close to all RepRap style printers have no second extruder for support material Skeinforge offers the option to print support structures with the same material set at a different speed and temperature. The idea is that the support sticks less to the actual object when it is extruded around the minimum possible working temperature. This results in a temperature change EVERY layer so build time will increase seriously.
+
+Allan Ecker aka The Masked Retriever's has written two quicktips for raft which follow below.
+"Skeinforge Quicktip: The Raft, Part 1" at:
+http://blog.thingiverse.com/2009/07/14/skeinforge-quicktip-the-raft-part-1/
+"Skeinforge Quicktip: The Raft, Part II" at:
+http://blog.thingiverse.com/2009/08/04/skeinforge-quicktip-the-raft-part-ii/
+
+Nophead has written about rafts on his blog:
+http://hydraraptor.blogspot.com/2009/07/thoughts-on-rafts.html
+
+More pictures of rafting in action are available from the Metalab blog at:
+http://reprap.soup.io/?search=rafting
+
+
+Operation
+Settings
+  Add Raft, Elevate Nozzle, Orbit
+  Base
+    Base Feed Rate Multiplier
+    Base Flow Rate Multiplier
+    Base Infill Density
+    Base Layer Height over Layer Thickness
+    Base Layers
+    Base Nozzle Lift over Base Layer Thickness
+  Initial Circling
+  Infill Overhang over Extrusion Width
+  Interface
+    Interface Feed Rate Multiplier
+    Interface Flow Rate Multiplier
+    Interface Infill Density
+    Interface Layer Thickness over Extrusion Height
+    Interface Layers
+    Interface Nozzle Lift over Interface Layer Thickness
+  Name of Alteration Files
+    Name of Support End File
+    Name of Support Start File
+  Operating Nozzle Lift over Layer Thickness
+  Raft Size
+    Raft Additional Margin over Length
+    Raft Margin
+  Support
+    Support Cross Hatch
+    Support Flow Rate over Operating Flow Rate
+    Support Gap over Perimeter Extrusion Width
+    Support Material Choice
+      Empty Layers Only
+      Everywhere
+      Exterior Only
+      None
+    Support Minimum Angle
+Examples
+
+

Operation

+
+ +Default: On
+
+When it is on, the functions described below will work, when it is off, nothing will be done, so no temperatures will be set, nozzle will not be lifted..
+
+

Settings

+
+ +

Add Raft, Elevate Nozzle, Orbit

+ +Default: On
+
+When selected, the script will also create a raft, elevate the nozzle, orbit and set the altitude of the bottom of the raft. It also turns on support generation.
+
+

Base

+ +Base layer is the part of the raft that touches the bed.
+
+

Base Feed Rate Multiplier

+ +Default is one.
+
+Defines the base feed rate multiplier. The greater the 'Base Feed Rate Multiplier', the thinner the base, the lower the 'Base Feed Rate Multiplier', the thicker the base.
+
+

Base Flow Rate Multiplier

+ +Default is one.
+
+Defines the base flow rate multiplier. The greater the 'Base Flow Rate Multiplier', the thicker the base, the lower the 'Base Flow Rate Multiplier', the thinner the base.
+
+

Base Infill Density

+ +Default is 0.5.
+
+Defines the infill density ratio of the base of the raft.
+
+

Base Layer Height over Layer Thickness

+ +Default is two.
+
+Defines the ratio of the height & width of the base layer compared to the height and width of the object infill. The feed rate will be slower for raft layers which have thicker extrusions than the object infill.
+
+

Base Layers

+ +Default is one.
+
+Defines the number of base layers.
+
+

Base Nozzle Lift over Base Layer Thickness

+ +Default is 0.4.
+
+Defines the amount the nozzle is above the center of the base extrusion divided by the base layer thickness.
+
+

Initial Circling

+ +Default is off.
+
+When selected, the extruder will initially circle around until it reaches operating temperature.
+
+

Infill Overhang over Extrusion Width

+ +Default is 0.05.
+
+Defines the ratio of the infill overhang over the the extrusion width of the raft.
+
+

Interface

+ +

Interface Feed Rate Multiplier

+ +Default is one.
+
+Defines the interface feed rate multiplier. The greater the 'Interface Feed Rate Multiplier', the thinner the interface, the lower the 'Interface Feed Rate Multiplier', the thicker the interface.
+
+

Interface Flow Rate Multiplier

+ +Default is one.
+
+Defines the interface flow rate multiplier. The greater the 'Interface Flow Rate Multiplier', the thicker the interface, the lower the 'Interface Flow Rate Multiplier', the thinner the interface.
+
+

Interface Infill Density

+ +Default is 0.5.
+
+Defines the infill density ratio of the interface of the raft.
+
+

Interface Layer Thickness over Extrusion Height

+ +Default is one.
+
+Defines the ratio of the height & width of the interface layer compared to the height and width of the object infill. The feed rate will be slower for raft layers which have thicker extrusions than the object infill.
+
+

Interface Layers

+ +Default is two.
+
+Defines the number of interface layers to print.
+
+

Interface Nozzle Lift over Interface Layer Thickness

+ +Default is 0.45.
+
+Defines the amount the nozzle is above the center of the interface extrusion divided by the interface layer thickness.
+
+

Name of Alteration Files

+ +If support material is generated, raft looks for alteration files in the alterations folder in the .skeinforge folder in the home directory. Raft does not care if the text file names are capitalized, but some file systems do not handle file name cases properly, so to be on the safe side you should give them lower case names. If it doesn't find the file it then looks in the alterations folder in the skeinforge_plugins folder.
+
+

Name of Support End File

+ +Default is support_end.gcode.
+
+If support material is generated and if there is a file with the name of the "Name of Support End File" setting, it will be added to the end of the support gcode.
+
+

Name of Support Start File

+ +If support material is generated and if there is a file with the name of the "Name of Support Start File" setting, it will be added to the start of the support gcode.
+
+

Operating Nozzle Lift over Layer Thickness

+ +Default is 0.5.
+
+Defines the amount the nozzle is above the center of the operating extrusion divided by the layer height.
+
+

Raft Size

+ +The raft fills a rectangle whose base size is the rectangle around the bottom layer of the object expanded on each side by the 'Raft Margin' plus the 'Raft Additional Margin over Length (%)' percentage times the length of the side.
+
+

Raft Additional Margin over Length

+ +Default is 1 percent.
+
+

Raft Margin

+ +Default is three millimeters.
+
+

Support

+ +Good articles on support material are at:
+http://davedurant.wordpress.com/2010/07/31/skeinforge-support-part-1/
+http://davedurant.wordpress.com/2010/07/31/skeinforge-support-part-2/
+
+

Support Cross Hatch

+ +Default is off.
+
+When selected, the support material will cross hatched. Cross hatching the support makes it stronger and harder to remove, which is why the default is off.
+
+

Support Flow Rate over Operating Flow Rate

+ +Default: 0.9.
+
+Defines the ratio of the flow rate when the support is extruded over the operating flow rate. With a number less than one, the support flow rate will be smaller so the support will be thinner and easier to remove.
+
+

Support Gap over Perimeter Extrusion Width

+ +Default: 0.5.
+
+Defines the gap between the support material and the object over the edge extrusion width.
+
+

Support Material Choice

+ +Default is 'None' because the raft takes time to generate.
+
+
Empty Layers Only
+ +When selected, support material will be only on the empty layers. This is useful when making identical objects in a stack.
+
+
Everywhere
+ +When selected, support material will be added wherever there are overhangs, even inside the object. Because support material inside objects is hard or impossible to remove, this option should only be chosen if the object has a cavity that needs support and there is some way to extract the support material.
+
+
Exterior Only
+ +When selected, support material will be added only the exterior of the object. This is the best option for most objects which require support material.
+
+
None
+ +When selected, raft will not add support material.
+
+

Support Minimum Angle

+ +Default is sixty degrees.
+
+Defines the minimum angle that a surface overhangs before support material is added. If angle is lower then this value the support will be generated. This angle is defined from the vertical, so zero is a vertical wall, ten is a wall with a bit of overhang, thirty is the typical safe angle for filament extrusion, sixty is a really high angle for extrusion and ninety is an unsupported horizontal ceiling.
+
+

Examples

+
+ +The following examples raft the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and raft.py.
+
+> python raft.py
+This brings up the raft dialog.
+
+> python raft.py Screw Holder Bottom.stl
+The raft tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The raft tool has created the file:
+Screw Holder Bottom_raft.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+
fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+math
+os
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+
sys
+fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
RaftRepository +
RaftSkein +
SupportLayer +
+

+ + + + + + + +
 
+class RaftRepository
   A class to handle the raft settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Raft button has been clicked.
+ +

+ + + + + + + +
 
+class RaftSkein
   A class to raft a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
addBaseLayer(self)
Add a base layer.
+ +
addBaseSegments(self, baseExtrusionWidth)
Add the base segments.
+ +
addEmptyLayerSupport(self, boundaryLayerIndex)
Add support material to a layer if it is empty.
+ +
addFlowRate(self, flowRate)
Add a flow rate value if different.
+ +
addInterfaceLayer(self)
Add an interface layer.
+ +
addInterfaceTables(self, interfaceExtrusionWidth)
Add interface tables.
+ +
addLayerFromEndpoints(self, endpoints, feedRateMultiplier, flowRateMultiplier, layerLayerThickness, layerThicknessRatio, step, z)
Add a layer from endpoints and raise the extrusion top.
+ +
addLayerLine(self, z)
Add the layer gcode line and close the last layer gcode block.
+ +
addOperatingOrbits(self, boundaryLoops, pointComplex, temperatureChangeTime, z)
Add the orbits before the operating layers.
+ +
addRaft(self)
Add the raft.
+ +
addRaftPerimeters(self)
Add raft edges if there is a raft.
+ +
addRaftPerimetersByLoops(self, loops, outset)
Add raft edges to the gcode for loops.
+ +
addRaftedLine(self, splitLine)
Add elevated gcode line with operating feed rate.
+ +
addSegmentTablesToSupportLayers(self)
Add segment tables to the support layers.
+ +
addSupportLayerTemperature(self, endpoints, z)
Add support layer and temperature before the object layer.
+ +
addSupportSegmentTable(self, layerIndex)
Add support segments from the boundary layers.
+ +
addTemperatureLineIfDifferent(self, temperature)
Add a line of temperature if different.
+ +
addTemperatureOrbits(self, endpoints, temperature, z)
Add the temperature and orbits around the support layer.
+ +
addToFillXIntersectionIndexTables(self, supportLayer)
Add fill segments from the boundary layers.
+ +
extendXIntersections(self, loops, radius, xIntersectionsTable)
Extend the support segments.
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the raft gcode.
+ +
getElevatedBoundaryLine(self, splitLine)
Get elevated boundary gcode line.
+ +
getInsetLoops(self, boundaryLayerIndex)
Inset the support loops if they are not already inset.
+ +
getInsetLoopsAbove(self, boundaryLayerIndex)
Get the inset loops above the boundary layer index.
+ +
getInsetLoopsBelow(self, boundaryLayerIndex)
Get the inset loops below the boundary layer index.
+ +
getStepsUntilEnd(self, begin, end, stepSize)
Get steps from the beginning until the end.
+ +
getSupportEndpoints(self)
Get the support layer segments.
+ +
getTemperatureChangeTime(self, temperature)
Get the temperature change time.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the raft skein.
+ +
setBoundaryLayers(self)
Set the boundary layers.
+ +
setCornersZ(self)
Set maximum and minimum corners and z.
+ +
subtractJoinedFill(self, supportLayerIndex)
Join the fill then subtract it from the support layer table.
+ +
truncateSupportSegmentTables(self)
Truncate the support segments after the last support segment which contains elements.
+ +

+ + + + + + + +
 
+class SupportLayer
   Support loops with segment tables.
 
 Methods defined here:
+
__init__(self, supportLoops)
+ +
__repr__(self)
Get the string representation of this loop layer.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text='', repository=None)
Raft the file or text.
+
getCraftedTextFromText(gcodeText, repository=None)
Raft a gcode linear move text.
+
getCrossHatchPointLine(crossHatchPointLineTable, y)
Get the cross hatch point line.
+
getEndpointsFromYIntersections(x, yIntersections)
Get endpoints from the y intersections.
+
getExtendedLineSegment(extensionDistance, lineSegment, loopXIntersections)
Get extended line segment.
+
getLoopsBySegmentsDictionary(segmentsDictionary, width)
Get loops from a horizontal segments dictionary.
+
getNewRepository()
Get new repository.
+
getVerticalEndpoints(horizontalSegmentsTable, horizontalStep, verticalOverhang, verticalStep)
Get vertical endpoints.
+
main()
Display the raft dialog.
+
setExtendedPoint(lineSegmentEnd, pointOriginal, x)
Set the point in the extended line segment.
+
writeOutput(fileName, shouldAnalyze=True)
Raft a gcode linear move file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.scale.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.scale.html new file mode 100644 index 0000000..28413a2 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.scale.html @@ -0,0 +1,169 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.scale + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.scale ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/scale.py
+

+Previous / Next / Contents +

+


+Scale scales the carving to compensate for shrinkage after the extrusion has cooled.
+
+The scale manual page is at:
+
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Scale
+
+It is best to only change the XY Plane Scale, because that does not affect other variables. If you choose to change the Z Axis Scale, that increases the layer height so you must increase the feed rate in speed by the same amount and maybe some other variables which depend on layer height.
+
+
+Operation
+Settings
+  XY Plane Scale
+  Z Axis Scale
+  SVG Viewer
+Examples
+
+

Operation

+
+ +The default 'Activate Scale' checkbox is off. When it is on, the functions described below will work, when it is off, nothing will be done.
+
+

Settings

+
+ +

XY Plane Scale

+ +Default is 1.01.
+
+Defines the amount the xy plane of the carving will be scaled. The xy coordinates will be scaled, but the edge width is not changed, so this can be changed without affecting other variables.
+
+

Z Axis Scale

+ +Default is one.
+
+Defines the amount the z axis of the carving will be scaled. The default is one because changing this changes many variables related to the layer height. For example, the feedRate should be multiplied by the Z Axis Scale because the layers would be farther apart.
+
+

SVG Viewer

+ +Default is webbrowser.
+
+If the 'SVG Viewer' is set to the default 'webbrowser', the scalable vector graphics file will be sent to the default browser to be opened. If the 'SVG Viewer' is set to a program name, the scalable vector graphics file will be sent to that program to be opened.
+
+

Examples

+
+ +The following examples scale the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and scale.py.
+
+> python scale.py
+This brings up the scale dialog.
+
+> python scale.py Screw Holder Bottom.stl
+The scale tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The scale tool has created the file:
+.. Screw Holder Bottom_scale.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+cStringIO
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+os
+fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+fabmetheus_utilities.svg_writer
+
sys
+time
+fabmetheus_utilities.xml_simple_writer
+

+ + + + + +
 
+Classes
       
+
ScaleRepository +
ScaleSkein +
+

+ + + + + + + +
 
+class ScaleRepository
   A class to handle the scale settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Scale button has been clicked.
+ +

+ + + + + + + +
 
+class ScaleSkein
   A class to scale a skein of extrusions.
 
 Methods defined here:
+
getCraftedGcode(self, fileName, repository, svgText)
Parse svgText and store the scale svgText.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, svgText='', repository=None)
Scale and convert an svg file or svgText.
+
getCraftedTextFromText(fileName, svgText, repository=None)
Scale and convert an svgText.
+
getNewRepository()
Get new repository.
+
main()
Display the scale dialog.
+
setLoopLayerScale(loopLayer, xyPlaneScale, zAxisScale)
Set the slice element scale.
+
writeOutput(fileName, shouldAnalyze=True)
Scale the carving.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__date__ = '$Date: 2008/02/05 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.skin.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.skin.html new file mode 100644 index 0000000..902469e --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.skin.html @@ -0,0 +1,212 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.skin + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.skin ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/skin.py
+

+Previous / Next / Contents +

+


+Skin is a plugin to smooth the surface skin of an object by replacing the edge surface with a surface printed at a fraction of the carve
+height. This gives the impression that the object was carved at a much thinner height giving a high-quality finish, but still prints
+in a relatively short time. The latest process has some similarities with a description at:
+
+http://adventuresin3-dprinting.blogspot.com/2011/05/skinning.html
+
+The skin manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Skin
+
+
+Operation
+Settings
+  Division
+    Horizontal Infill Divisions
+    Horizontal Perimeter Divisions
+    Vertical Divisions
+  Hop When Extruding Infill
+  Layers From
+Tips
+Examples
+
+

Operation

+
+ +The default 'Activate Skin' checkbox is off. When it is on, the functions described below will work, when it is off, nothing will be done.
+
+

Settings

+
+ +

Division

+ +

Horizontal Infill Divisions

+ +Default: 2
+
+Defines the number of times the skinned infill is divided horizontally.
+
+

Horizontal Perimeter Divisions

+ +Default: 1
+
+Defines the number of times the skinned edges are divided horizontally.
+
+

Vertical Divisions

+ +Default: 2
+
+Defines the number of times the skinned infill and edges are divided vertically.
+
+

Hop When Extruding Infill

+ +Default is off.
+
+When selected, the extruder will hop before and after extruding the lower infill in order to avoid the regular thickness threads.
+
+

Layers From

+ +Default: 1
+
+Defines which layer of the print the skinning process starts from. It is not wise to set this to zero, skinning the bottom layer is likely to cause the bottom edge not to adhere well to the print surface.
+
+

Tips

+
+ +Due to the very small Z-axis moves skinning can generate as it prints the edge, it can cause the Z-axis speed to be limited by the Limit plug-in, if you have it enabled. This can cause some printers to pause excessively during each layer change. To overcome this, ensure that the Z-axis max speed in the Limit tool is set to an appropriate value for your printer, e.g. 10mm/s
+
+Since Skin prints a number of fractional-height edge layers for each layer, printing the edge last causes the print head to travel down from the current print height. Depending on the shape of your extruder nozzle, you may get higher quality prints if you print the edges first, so the print head always travels up. This is set via the Thread Sequence Choice setting in the Fill tool.
+
+

Examples

+
+ +The following examples skin the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and skin.py.
+
+> python skin.py
+This brings up the skin dialog.
+
+> python skin.py Screw Holder Bottom.stl
+The skin tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The skin tool has created the file:
+.. Screw Holder Bottom_skin.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
SkinRepository +
SkinSkein +
+

+ + + + + + + +
 
+class SkinRepository
   A class to handle the skin settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Skin button has been clicked.
+ +

+ + + + + + + +
 
+class SkinSkein
   A class to skin a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
Initialize.
+ +
addFlowRateLine(self, flowRate)
Add a flow rate line.
+ +
addPerimeterLoop(self, thread, z)
Add the edge loop to the gcode.
+ +
addSkinnedInfill(self)
Add skinned infill.
+ +
addSkinnedInfillBoundary(self, infillBoundaries, offsetY, upperZ, z)
Add skinned infill boundary.
+ +
addSkinnedPerimeter(self)
Add skinned edge.
+ +
getClippedSimplifiedLoopPathByLoop(self, loop)
Get clipped and simplified loop path from a loop.
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the skin gcode.
+ +
parseBoundaries(self)
Parse the boundaries and add them to the boundary layers.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the skin skein.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, gcodeText, repository=None)
Skin a gcode linear move text.
+
getCraftedTextFromText(gcodeText, repository=None)
Skin a gcode linear move text.
+
getIsMinimumSides(loops, sides=3)
Determine if all the loops have at least the given number of sides.
+
getNewRepository()
Get new repository.
+
main()
Display the skin dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Skin a gcode linear move file.  Chain skin the gcode if it is not already skinned.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique aht yahoo.com) & James Blackwell (jim_blag ahht hotmail.com)'
+__date__ = '$Date: 2008/21/04 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique aht yahoo.com) & James Blackwell (jim_blag ahht hotmail.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.skirt.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.skirt.html new file mode 100644 index 0000000..dbad981 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.skirt.html @@ -0,0 +1,219 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.skirt + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.skirt ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/skirt.py
+

+Previous / Next / Contents +

+


+Skirt is a plugin to give the extruder some extra time to begin extruding properly before beginning the object, and to put a baffle around the model in order to keep the extrusion warm.
+
+The skirt manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Skirt
+
+It is loosely based on Lenbook's outline plugin:
+
+http://www.thingiverse.com/thing:4918
+
+it is also loosely based on the outline that Nophead sometimes uses:
+
+http://hydraraptor.blogspot.com/2010/01/hot-metal-and-serendipity.html
+
+and also loosely based on the baffles that Nophead made to keep corners warm:
+
+http://hydraraptor.blogspot.com/2010/09/some-corners-like-it-hot.html
+
+If you want only an outline, set 'Layers To' to one. This gives the extruder some extra time to begin extruding properly before beginning your object, and gives you an early verification of where your object will be extruded.
+
+If you also want an insulating skirt around the entire object, set 'Layers To' to a huge number, like 912345678. This will additionally make an insulating baffle around the object; to prevent moving air from cooling the object, which increases warping, especially in corners.
+
+
+Operation
+Settings
+  Convex
+  Gap over Perimeter Width
+  Layers To
+Examples
+
+

Operation

+
+ +The default 'Activate Skirt' checkbox is off. When it is on, the functions described below will work, when it is off, nothing will be done.
+
+

Settings

+
+ +

Convex

+ +Default is on.
+
+When selected, the skirt will be convex, going around the model with only convex angles. If convex is not selected, the skirt will hug the model, going into every nook and cranny.
+
+

Gap over Perimeter Width

+ +Default is three.
+
+Defines the ratio of the gap between the object and the skirt over the edge width. If the ratio is too low, the skirt will connect to the object, if the ratio is too high, the skirt willl not provide much insulation for the object.
+
+

Layers To

+ +Default is a one.
+
+Defines the number of layers of the skirt. If you want only an outline, set 'Layers To' to one. If you want an insulating skirt around the entire object, set 'Layers To' to a huge number, like 912345678.
+
+

Examples

+
+ +The following examples skirt the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and skirt.py.
+
+> python skirt.py
+This brings up the skirt dialog.
+
+> python skirt.py Screw Holder Bottom.stl
+The skirt tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The skirt tool has created the file:
+.. Screw Holder Bottom_skirt.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+
fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+math
+fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+
fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
LoopCrossDictionary +
SkirtRepository +
SkirtSkein +
+

+ + + + + + + +
 
+class LoopCrossDictionary
   Loop with a horizontal and vertical dictionary.
 
 Methods defined here:
+
__init__(self)
Initialize LoopCrossDictionary.
+ +
__repr__(self)
Get the string representation of this LoopCrossDictionary.
+ +

+ + + + + + + +
 
+class SkirtRepository
   A class to handle the skirt settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Skirt button has been clicked.
+ +

+ + + + + + + +
 
+class SkirtSkein
   A class to skirt a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
Initialize variables.
+ +
addFlowRate(self, flowRate)
Add a line of temperature if different.
+ +
addSkirt(self, z)
At skirt at z to gcode output.
+ +
addTemperatureLineIfDifferent(self, temperature)
Add a line of temperature if different.
+ +
createSegmentDictionaries(self, loopCrossDictionary)
Create horizontal and vertical segment dictionaries.
+ +
createSkirtLoops(self)
Create the skirt loops.
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the skirt gcode.
+ +
getHorizontalXIntersectionsTable(self, loop)
Get the horizontal x intersections table from the loop.
+ +
parseBoundaries(self)
Parse the boundaries and union them.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the skirt skein.
+ +
setSkirtFeedFlowTemperature(self)
Set the skirt feed rate, flow rate and temperature to that of the next extrusion.
+ +
unifyLayer(self, loopCrossDictionary)
Union the loopCrossDictionary with the unifiedLoop.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text='', repository=None)
Skirt the fill file or text.
+
getCraftedTextFromText(gcodeText, repository=None)
Skirt the fill text.
+
getNewRepository()
Get new repository.
+
getOuterLoops(loops)
Get widdershins outer loops.
+
main()
Display the skirt dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Skirt a gcode linear move file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.smooth.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.smooth.html new file mode 100644 index 0000000..56b938a --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.smooth.html @@ -0,0 +1,167 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.smooth + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.smooth ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/smooth.py
+

+Previous / Next / Contents +

+


+This plugin smooths jagged extruder paths. It takes shortcuts through jagged paths and decreases the feed rate to compensate.
+
+Smooth is based on ideas in Nophead's frequency limit post:
+
+http://hydraraptor.blogspot.com/2010/12/frequency-limit.html
+
+The smooth manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Smooth
+
+
+Operation
+Settings
+  Layers From
+  Maximum Shortening over Width
+Examples
+
+

Operation

+
+ +The default 'Activate Smooth' checkbox is off. When it is on, the functions described below will work, when it is off, nothing will be done.
+
+

Settings

+
+ +

Layers From

+ +Default: 1
+
+Defines which layer of the print the smoothing process starts from. If this is set this to zero, that might cause the smoothed parts of the bottom edge not to adhere well to the print surface. However, this is just a potential problem in theory, no bottom adhesion problem has been reported.
+
+

Maximum Shortening over Width

+ +Default: 1.2
+
+Defines the maximum shortening of the shortcut compared to the original path. Smooth goes over the path and if the shortcut between the midpoint of one line and the midpoint of the second line after is not too short compared to the original and the shortcut is not too long, it replaces the jagged original with the shortcut. If the maximum shortening is too much, smooth will shorten paths which should not of been shortened and will leave blobs and holes in the model. If the maximum shortening is too little, even jagged paths that could be shortened safely won't be smoothed.
+
+

Examples

+
+ +The following examples smooth the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and smooth.py.
+
+> python smooth.py
+This brings up the smooth dialog.
+
+> python smooth.py Screw Holder Bottom.stl
+The smooth tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The smooth tool has created the file:
+.. Screw Holder Bottom_smooth.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+
sys
+

+ + + + + +
 
+Classes
       
+
SmoothRepository +
SmoothSkein +
+

+ + + + + + + +
 
+class SmoothRepository
   A class to handle the smooth settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Smooth button has been clicked.
+ +

+ + + + + + + +
 
+class SmoothSkein
   A class to smooth a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
Initialize.
+ +
addSmoothedInfill(self)
Add smoothed infill.
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the smooth gcode.
+ +
getIsParallelToRotation(self, segment)
Determine if the segment is parallel to the rotation.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the smooth skein.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, gcodeText, repository=None)
Smooth a gcode linear move text.
+
getCraftedTextFromText(gcodeText, repository=None)
Smooth a gcode linear move text.
+
getNewRepository()
Get new repository.
+
main()
Display the smooth dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Smooth a gcode linear move file.  Chain smooth the gcode if it is not already smoothed.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique aht yahoo.com) & James Blackwell (jim_blag ahht hotmail.com)'
+__date__ = '$Date: 2008/21/04 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique aht yahoo.com) & James Blackwell (jim_blag ahht hotmail.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.speed.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.speed.html new file mode 100644 index 0000000..1cd20ea --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.speed.html @@ -0,0 +1,285 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.speed + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.speed ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/speed.py
+

+Previous / Next / Contents +

+


+Speed is a plugin to set the feed rate and flow rate.
+
+The speed manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Speed
+
+
+Operation
+Settings
+  Add Flow Rate
+  Bridge
+    Bridge Feed Rate Multiplier
+    Bridge Flow Rate Multiplier
+  Duty Cyle
+    Duty Cyle at Beginning
+    Duty Cyle at Ending
+  Feed Rate
+  Flow Rate Setting
+  Maximum Z Feed Rate
+  Object First Layer
+    Object First Layer Feed Rate Infill Multiplier
+    Object First Layer Feed Rate Perimeter Multiplier
+    Object First Layer Flow Rate Infill Multiplier
+    Object First Layer Flow Rate Perimeter Multiplier
+  Orbital Feed Rate over Operating Feed Rate
+  Perimeter
+    Perimeter Feed Rate Multiplier
+    Perimeter Flow Rate Multiplier
+  Travel Feed Rate
+Examples
+
+

Operation

+
+ +The default 'Activate Speed' checkbox is on. When it is on, the functions described below will work, when it is off, nothing will be done.
+
+

Settings

+
+ +

Add Flow Rate

+ +Default is on.
+
+When selected, the flow rate will be added to the gcode.
+
+

Bridge

+ +

Bridge Feed Rate Multiplier

+ +Default is one.
+
+Defines the ratio of the feed rate (head speed) on the bridge layers over the feed rate of the typical non bridge layers.
+
+

Bridge Flow Rate Multiplier

+ +Default is one.
+
+Defines the ratio of the flow rate (extruder speed) on the bridge layers over the flow rate of the typical non bridge layers.
+
+

Duty Cyle

+ +

Duty Cyle at Beginning

+ +Default is one, which will set the extruder motor to full current.
+
+Defines the duty cycle of the stepper motor pulse width modulation by adding an M113 command toward the beginning of the gcode text. If the hardware has the option of using a potentiometer to set the duty cycle, to select the potentiometer option set 'Duty Cyle at Beginning' to an empty string. To turn off the extruder, set the 'Duty Cyle at Beginning' to zero.
+
+

Duty Cyle at Ending

+ +Default is zero, which will turn off the extruder motor.
+
+Defines the duty cycle of the stepper motor pulse width modulation by adding an M113 command toward the ending of the gcode text. If the hardware has the option of using a potentiometer to set the duty cycle, to select the potentiometer option set 'Duty Cyle at Beginning' to an empty string. To turn off the extruder, set the 'Duty Cyle at Ending' to zero.
+
+

Feed Rate

+ +Default is sixteen millimeters per second.
+
+Defines the operating feed rate, the speed your printing head moves in XY plane, before any modifiers.
+
+

Flow Rate Setting

+ +Default is 210.
+
+Defines the operating flow rate.
+
+RapMan uses this parameter to define the RPM of the extruder motor. The extruder motor RPM is flow rate / 10 so if your flow rate is 150.0 that will set the extruder stepper to run at 15 RPM, different printers might read this value differently.
+
+

Maximum Z Feed Rate

+ +Default is one millimeter per second.
+
+Defines the speed of a vertical hop, like the infill hop in skin. Also, if the Limit plugin is activated, it will limit the maximum speed of the tool head in the z direction to this value.
+
+

Object First Layer

+ +
+

Object First Layer Feed Rate Infill Multiplier

+ +Default is 0.4.
+
+Defines the object first layer infill feed rate multiplier. The greater the 'Object First Layer Feed Rate Infill Multiplier, the thinner the infill, the lower the 'Object First Layer Feed Rate Infill Multiplier', the thicker the infill.
+
+

Object First Layer Feed Rate Perimeter Multiplier

+ +Default is 0.4.
+
+Defines the object first layer edge feed rate multiplier. The greater the 'Object First Layer Feed Rate Perimeter Multiplier, the thinner the edge, the lower the 'Object First Layer Feed Rate Perimeter Multiplier', the thicker the edge.
+
+

Object First Layer Flow Rate Infill Multiplier

+ +Default is 0.4.
+
+Defines the object first layer infill flow rate multiplier. The greater the 'Object First Layer Flow Rate Infill Multiplier', the thicker the infill, the lower the 'Object First Layer Flow Rate Infill Multiplier, the thinner the infill.
+
+

Object First Layer Flow Rate Perimeter Multiplier

+ +Default is 0.4.
+
+Defines the object first layer edge flow rate multiplier. The greater the 'Object First Layer Flow Rate Perimeter Multiplier', the thicker the edge, the lower the 'Object First Layer Flow Rate Perimeter Multiplier, the thinner the edge.
+
+

Orbital Feed Rate over Operating Feed Rate

+ +Default is 0.5.
+
+Defines the speed when the head is orbiting compared to the operating extruder speed. If you want the orbit to be very short, set the "Orbital Feed Rate over Operating Feed Rate" setting to a low value like 0.1.
+
+

Perimeter

+ +To have higher build quality on the outside at the expense of slower build speed, a typical setting for the 'Perimeter Feed Rate over Operating Feed Rate' would be 0.5. To go along with that, if you are using a speed controlled extruder like a stepper extruder, the 'Perimeter Flow Rate over Operating Flow Rate' should also be 0.5.
+
+A stepper motor is the best way of driving the extruder; however, if you are stuck with a DC motor extruder using Pulse Width Modulation to control the speed, then you'll probably need a slightly higher ratio because there is a minimum voltage 'Flow Rate PWM Setting' required for the extruder motor to turn. The flow rate PWM ratio would be determined by trial and error, with the first trial being:
+Perimeter Flow Rate over Operating Flow Rate ~ Perimeter Feed Rate over Operating Feed Rate * (Flow Rate PWM Setting - Minimum Flow Rate PWM Setting) + Minimum Flow Rate PWM Setting
+
+

Perimeter Feed Rate Multiplier

+ +Default: 1.0
+
+Defines the ratio of the feed rate of the edge (outside shell) over the feed rate of the infill. If you for example set this to 0.8 you will have a "stronger" outside edge than inside extrusion as the outside edge will be printed slower hence better lamination will occur and more filament will be placed there.
+
+

Perimeter Flow Rate Multiplier

+ +Default: 1.0
+
+Defines the ratio of the flow rate of the edge (outside shell) over the flow rate of the infill. If you want the same thickness of the edge but better lamination you need to compensate for the slower feed rate by slowing down the flow rate, but all combinations are possible for different results.
+
+

Travel Feed Rate

+ +Default is sixteen millimeters per second.
+
+Defines the feed rate when the extruder is off (not printing). The 'Travel Feed Rate' could be set as high as the extruder can be moved, it is not limited by the maximum extrusion rate.
+
+

Examples

+
+ +The following examples speed the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and speed.py.
+
+> python speed.py
+This brings up the speed dialog.
+
+> python speed.py Screw Holder Bottom.stl
+The speed tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The speed tool has created the file:
+.. Screw Holder Bottom_speed.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+
math
+fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+
skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
SpeedRepository +
SpeedSkein +
+

+ + + + + + + +
 
+class SpeedRepository
   A class to handle the speed settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Speed button has been clicked.
+ +

+ + + + + + + +
 
+class SpeedSkein
   A class to speed a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
Initialize.
+ +
addFlowRateLine(self)
Add flow rate line.
+ +
addParameterString(self, firstWord, parameterWord)
Add parameter string.
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the speed gcode.
+ +
getSpeededLine(self, line, splitLine)
Get gcode line with feed rate.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the speed skein.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text='', repository=None)
Speed the file or text.
+
getCraftedTextFromText(gcodeText, repository=None)
Speed a gcode linear move text.
+
getNewRepository()
Get new repository.
+
main()
Display the speed dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Speed a gcode linear move file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.splodge.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.splodge.html new file mode 100644 index 0000000..483c189 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.splodge.html @@ -0,0 +1,214 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.splodge + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.splodge ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/splodge.py
+

+Previous / Next / Contents +

+


+Splodge turns the extruder on just before the start of a thread. This is to give the extrusion a bit anchoring at the beginning.
+
+The splodge manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Splodge
+
+
+Operation
+Settings
+  Initial
+    Initial Lift over Extra Thickness
+    Initial Splodge Feed Rate
+    Initial Splodge Quantity Length
+  Operating
+    Operating Lift over Extra Thickness
+    Operating Splodge Feed Rate
+    Operating Splodge Quantity Length
+Examples
+
+

Operation

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

Settings

+
+ +

Initial

+ +

Initial Lift over Extra Thickness

+ +Default is one.
+
+Defines the amount the extruder will be lifted over the extra thickness of the initial splodge thread. The higher the ratio, the more the extruder will be lifted over the splodge, if the ratio is too low the extruder might plow through the splodge extrusion.
+
+

Initial Splodge Feed Rate

+ +Default is one millimeter per second.
+
+Defines the feed rate at which the initial extra extrusion will be added. With the default feed rate, the splodge will be added slower so it will be thicker than the regular extrusion.
+
+

Initial Splodge Quantity Length

+ +Default is thirty millimeters.
+
+Defines the quantity length of extra extrusion at the operating feed rate that will be added to the initial thread. If a splodge quantity length is smaller than 0.1 times the edge width, no splodge of that type will be added.
+
+

Operating

+ +

Operating Lift over Extra Thickness

+ +Default is one.
+
+Defines the amount the extruder will be lifted over the extra thickness of the operating splodge thread.
+
+

Operating Splodge Feed Rate

+ +Default is one millimeter per second.
+
+Defines the feed rate at which the next extra extrusions will be added.
+
+

Operating Splodge Quantity Length

+ +Default is thirty millimeters.
+
+Defines the quantity length of extra extrusion at the operating feed rate that will be added for the next threads.
+
+

Examples

+
+ +The following examples splodge the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and splodge.py.
+
+> python splodge.py
+This brings up the splodge dialog.
+
+> python splodge.py Screw Holder Bottom.stl
+The splodge tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The splodge tool has created the file:
+.. Screw Holder Bottom_splodge.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+math
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
SplodgeRepository +
SplodgeSkein +
+

+ + + + + + + +
 
+class SplodgeRepository
   A class to handle the splodge settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Splodge button has been clicked.
+ +

+ + + + + + + +
 
+class SplodgeSkein
   A class to splodge a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
addLineUnlessIdentical(self, line)
Add a line, unless it is identical to the last line.
+ +
addLineUnlessIdenticalReactivate(self, line)
Add a line, unless it is identical to the last line or another M101.
+ +
getCraftedGcode(self, gcodeText, splodgeRepository)
Parse gcode text and store the splodge gcode.
+ +
getInitialSplodgeLine(self, line, location)
Add the initial splodge line.
+ +
getNextActiveLocationComplex(self)
Get the next active line.
+ +
getOperatingSplodgeLine(self, line, location)
Get the operating splodge line.
+ +
getSplodgeLine(self, line, location, splitLine)
Get splodged gcode line.
+ +
getSplodgeLineGivenDistance(self, feedRateMinute, line, liftOverExtraThickness, location, startupDistance)
Add the splodge line.
+ +
getStartInsideBoundingRectangle(self, locationComplex, relativeStartComplex)
Get a start inside the bounding rectangle.
+ +
isJustBeforeExtrusion(self)
Determine if activate command is before linear move command.
+ +
parseInitialization(self, splodgeRepository)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the bevel gcode.
+ +
setRotations(self)
Set the rotations.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text, splodgeRepository=None)
Splodge a gcode linear move file or text.
+
getCraftedTextFromText(gcodeText, splodgeRepository=None)
Splodge a gcode linear move text.
+
getNewRepository()
Get new repository.
+
main()
Display the splodge dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Splodge a gcode linear move file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.stretch.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.stretch.html new file mode 100644 index 0000000..414e938 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.stretch.html @@ -0,0 +1,248 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.stretch + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.stretch ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/stretch.py
+

+Previous / Next / Contents +

+


+Stretch is very important Skeinforge plugin that allows you to partially compensate for the fact that extruded holes are smaller then they should be. It stretches the threads to partially compensate for filament shrinkage when extruded.
+
+The stretch manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Stretch
+
+Extruded holes are smaller than the model because while printing an arc the head is depositing filament on both sides of the arc but in the inside of the arc you actually need less material then on the outside of the arc. You can read more about this on the RepRap ArcCompensation page:
+http://reprap.org/bin/view/Main/ArcCompensation
+
+In general, stretch will widen holes and push corners out. In practice the filament contraction will not be identical to the algorithm, so even once the optimal parameters are determined, the stretch script will not be able to eliminate the inaccuracies caused by contraction, but it should reduce them.
+
+All the defaults assume that the thread sequence choice setting in fill is the edge being extruded first, then the loops, then the infill. If the thread sequence choice is different, the optimal thread parameters will also be different. In general, if the infill is extruded first, the infill would have to be stretched more so that even after the filament shrinkage, it would still be long enough to connect to the loop or edge.
+
+Holes should be made with the correct area for their radius. In other words, for example if your modeling program approximates a hole of radius one (area = pi) by making a square with the points at [(1,0), (0,1), (-1,0), (0,-1)] (area = 2), the radius should be increased by sqrt(pi/2). This can be done in fabmetheus xml by writing:
+radiusAreal='True'
+
+in the attributes of the object or any parent of that object. In other modeling programs, you'll have to this manually or make a script. If area compensation is not done, then changing the stretch parameters to over compensate for too small hole areas will lead to incorrect compensation in other shapes.
+
+
+Operation
+Settings
+  Loop Stretch Over Perimeter Width
+  Path Stretch Over Perimeter Width
+  Perimeter
+    Perimeter Inside Stretch Over Perimeter Width
+    Perimeter Outside Stretch Over Perimeter Width
+  Stretch from Distance over Perimeter Width
+Examples
+
+

Operation

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

Settings

+
+ +

Loop Stretch Over Perimeter Width

+ +Default is 0.1.
+
+Defines the ratio of the maximum amount the loop aka inner shell threads will be stretched compared to the edge width, in general this value should be the same as the 'Perimeter Outside Stretch Over Perimeter Width' setting.
+
+

Path Stretch Over Perimeter Width

+ +Default is zero.
+
+Defines the ratio of the maximum amount the threads which are not loops, like the infill threads, will be stretched compared to the edge width.
+
+

Perimeter

+ +

Perimeter Inside Stretch Over Perimeter Width

+ +Default is 0.32.
+
+Defines the ratio of the maximum amount the inside edge thread will be stretched compared to the edge width, this is the most important setting in stretch. The higher the value the more it will stretch the edge and the wider holes will be. If the value is too small, the holes could be drilled out after fabrication, if the value is too high, the holes would be too wide and the part would have to junked.
+
+

Perimeter Outside Stretch Over Perimeter Width

+ +Default is 0.1.
+
+Defines the ratio of the maximum amount the outside edge thread will be stretched compared to the edge width, in general this value should be around a third of the 'Perimeter Inside Stretch Over Perimeter Width' setting.
+
+

Stretch from Distance over Perimeter Width

+ +Default is two.
+
+The stretch algorithm works by checking at each turning point on the extrusion path what the direction of the thread is at a distance of 'Stretch from Distance over Perimeter Width' times the edge width, on both sides, and moves the thread in the opposite direction. So it takes the current turning-point, goes "Stretch from Distance over Perimeter Width" * "Perimeter Width" ahead, reads the direction at that point. Then it goes the same distance in back in time, reads the direction at that other point. It then moves the thread in the opposite direction, away from the center of the arc formed by these 2 points+directions.
+
+The magnitude of the stretch increases with:
+the amount that the direction of the two threads is similar and
+by the '..Stretch Over Perimeter Width' ratio.
+
+

Examples

+
+ +The following examples stretch the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and stretch.py.
+
+> python stretch.py
+This brings up the stretch dialog.
+
+> python stretch.py Screw Holder Bottom.stl
+The stretch tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The stretch tool has created the file:
+.. Screw Holder Bottom_stretch.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
LineIteratorBackward +
LineIteratorForward +
StretchRepository +
StretchSkein +
+

+ + + + + + + +
 
+class LineIteratorBackward
   Backward line iterator class.
 
 Methods defined here:
+
__init__(self, isLoop, lineIndex, lines)
+ +
getIndexBeforeNextDeactivate(self)
Get index two lines before the deactivate command.
+ +
getNext(self)
Get next line going backward or raise exception.
+ +
isBeforeExtrusion(self)
Determine if index is two or more before activate command.
+ +

+ + + + + + + +
 
+class LineIteratorForward
   Forward line iterator class.
 
 Methods defined here:
+
__init__(self, isLoop, lineIndex, lines)
+ +
getIndexJustAfterActivate(self)
Get index just after the activate command.
+ +
getNext(self)
Get next line or raise exception.
+ +

+ + + + + + + +
 
+class StretchRepository
   A class to handle the stretch settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Stretch button has been clicked.
+ +

+ + + + + + + +
 
+class StretchSkein
   A class to stretch a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
getCraftedGcode(self, gcodeText, stretchRepository)
Parse gcode text and store the stretch gcode.
+ +
getCrossLimitedStretch(self, crossLimitedStretch, crossLineIterator, locationComplex)
Get cross limited relative stretch for a location.
+ +
getRelativeStretch(self, locationComplex, lineIterator)
Get relative stretch for a location.
+ +
getStretchedLine(self, splitLine)
Get stretched gcode line.
+ +
getStretchedLineFromIndexLocation(self, indexPreviousStart, indexNextStart, location)
Get stretched gcode line from line index and location.
+ +
isJustBeforeExtrusion(self)
Determine if activate command is before linear move command.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseStretch(self, line)
Parse a gcode line and add it to the stretch skein.
+ +
setStretchToPath(self)
Set the thread stretch to path stretch and is loop false.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, gcodeText, stretchRepository=None)
Stretch a gcode linear move text.
+
getCraftedTextFromText(gcodeText, stretchRepository=None)
Stretch a gcode linear move text.
+
getNewRepository()
Get new repository.
+
main()
Display the stretch dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Stretch a gcode linear move file.  Chain stretch the gcode if it is not already stretched.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.temperature.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.temperature.html new file mode 100644 index 0000000..90e66f3 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.temperature.html @@ -0,0 +1,217 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.temperature + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.temperature ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/temperature.py
+

+Previous / Next / Contents +

+


+Temperature is a plugin to set the temperature for the entire extrusion.
+
+The temperature manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Temperature
+
+
+Operation
+Settings
+  Rate
+    Cooling Rate
+    Heating Rate
+  Temperature
+    Base Temperature
+    Interface Temperature
+    Object First Layer Infill Temperature
+    Object First Layer Perimeter Temperature
+    Object Next Layers Temperature
+    Support Layers Temperature
+    Supported Layers Temperature
+Examples
+
+

Operation

+
+ +The default 'Activate Temperature' checkbox is on. When it is on, the functions described below will work, when it is off, nothing will be done.
+
+

Settings

+
+ +

Rate

+ +The default cooling rate and heating rate for the extruder were both been derived from bothacker's graph at:
+http://bothacker.com/wp-content/uploads/2009/09/18h5m53s9.29.2009.png
+
+

Cooling Rate

+ +Default is three degrees Celcius per second.
+
+Defines the cooling rate of the extruder.
+
+

Heating Rate

+ +Default is ten degrees Celcius per second.
+
+Defines the heating rate of the extruder.
+
+

Temperature

+ +

Base Temperature

+ +Default for ABS is two hundred degrees Celcius.
+
+Defines the raft base temperature.
+
+

Interface Temperature

+ +Default for ABS is two hundred degrees Celcius.
+
+Defines the raft interface temperature.
+
+

Object First Layer Infill Temperature

+ +Default for ABS is 195 degrees Celcius.
+
+Defines the infill temperature of the first layer of the object.
+
+

Object First Layer Perimeter Temperature

+ +Default for ABS is two hundred and twenty degrees Celcius.
+
+Defines the edge temperature of the first layer of the object.
+
+

Object Next Layers Temperature

+ +Default for ABS is two hundred and thirty degrees Celcius.
+
+Defines the temperature of the next layers of the object.
+
+

Support Layers Temperature

+ +Default for ABS is two hundred degrees Celcius.
+
+Defines the support layers temperature.
+
+

Supported Layers Temperature

+ +Default for ABS is two hundred and thirty degrees Celcius.
+
+Defines the temperature of the supported layers of the object, those layers which are right above a support layer.
+
+

Examples

+
+ +The following examples add temperature information to the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and temperature.py.
+
+> python temperature.py
+This brings up the temperature dialog.
+
+> python temperature.py Screw Holder Bottom.stl
+The temperature tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The temperature tool has created the file:
+.. Screw Holder Bottom_temperature.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+
math
+fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+
skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
TemperatureRepository +
TemperatureSkein +
+

+ + + + + + + +
 
+class TemperatureRepository
   A class to handle the temperature settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Temperature button has been clicked.
+ +

+ + + + + + + +
 
+class TemperatureSkein
   A class to temperature a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the temperature gcode.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text='', repository=None)
Temperature the file or text.
+
getCraftedTextFromText(gcodeText, repository=None)
Temperature a gcode linear move text.
+
getNewRepository()
Get new repository.
+
main()
Display the temperature dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Temperature a gcode linear move file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.tower.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.tower.html new file mode 100644 index 0000000..de1a16e --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.tower.html @@ -0,0 +1,220 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.tower + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.tower ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/tower.py
+

+Previous / Next / Contents +

+


+Tower commands the fabricator to extrude a disconnected region for a few layers, then go to another disconnected region and extrude there. Its purpose is to reduce the number of stringers between a shape and reduce extruder travel.
+
+The tower manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Tower
+
+
+Operation
+Settings
+  Maximum Tower Height
+  Extruder Possible Collision Cone Angle
+  Tower Start Layer
+Examples
+
+

Operation

+
+ +The default 'Activate Tower' checkbox is off. The default is off because tower could result in the extruder colliding with an already extruded part of the shape and because extruding in one region for more than one layer could result in the shape melting. When it is on, the functions described below will work, when it is off, nothing will be done.
+
+

Settings

+
+ +

Maximum Tower Height

+ +Default: 5
+
+Defines the maximum number of layers that the extruder will extrude in one region before going to another. This is the most important value for tower.
+
+

Extruder Possible Collision Cone Angle

+ +Default: 60 degrees
+
+Tower works by looking for islands in each layer and if it finds another island in the layer above, it goes to the next layer above instead of going across to other regions on the original layer. It checks for collision with shapes already extruded within a cone from the nozzle tip. The 'Extruder Possible Collision Cone Angle' setting is the angle of that cone. Realistic values for the cone angle range between zero and ninety. The higher the angle, the less likely a collision with the rest of the shape is, generally the extruder will stay in the region for only a few layers before a collision is detected with the wide cone.
+
+

Tower Start Layer

+ +Default: 1
+
+Defines the layer index which the script starts extruding towers, after the last raft layer which does not have support material. It is best to not tower at least the first layer because the temperature of the first layer is sometimes different than that of the other layers.
+
+

Examples

+
+ +The following examples tower the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and tower.py.
+
+> python tower.py
+This brings up the tower dialog.
+
+> python tower.py Screw Holder Bottom.stl
+The tower tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The tower tool has created the file:
+.. Screw Holder Bottom_tower.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+
math
+fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+
skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
Island +
ThreadLayer +
TowerRepository +
TowerSkein +
+

+ + + + + + + +
 
+class Island
   A class to hold the boundary and lines.
 
 Methods defined here:
+
__init__(self)
+ +
addToBoundary(self, splitLine)
Add to the boundary if it is not complete.
+ +
createBoundingLoop(self)
Create the bounding loop if it is not already created.
+ +

+ + + + + + + +
 
+class ThreadLayer
   A layer of loops and paths.
 
 Methods defined here:
+
__init__(self)
Thread layer constructor.
+ +
__repr__(self)
Get the string representation of this thread layer.
+ +

+ + + + + + + +
 
+class TowerRepository
   A class to handle the tower settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Tower button has been clicked.
+ +

+ + + + + + + +
 
+class TowerSkein
   A class to tower a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
addEntireLayer(self, threadLayer)
Add entire thread layer.
+ +
addHighThread(self, location)
Add thread with a high move if necessary to clear the previous extrusion.
+ +
addThreadLayerIfNone(self)
Add a thread layer if it is none.
+ +
addTowers(self)
Add towers.
+ +
climbTower(self, removedIsland)
Climb up the island to any islands directly above.
+ +
getBottomLayerIndex(self)
Get the index of the first island layer which has islands.
+ +
getCraftedGcode(self, gcodeText, towerRepository)
Parse gcode text and store the tower gcode.
+ +
getRemovedIslandAddLayerLinesIfDifferent(self, islands, layerIndex)
Add gcode lines for the layer if it is different than the old bottom layer index.
+ +
getTransferClosestNestedRingLines(self, oldOrderedLocation, remainingNestedRings)
Get and transfer the closest remaining nested ring.
+ +
isInsideRemovedOutsideCone(self, island, removedBoundingLoop, untilLayerIndex)
Determine if the island is entirely inside the removed bounding loop and outside the collision cone of the remaining islands.
+ +
parseIfWordUntilWord(self, word)
Parse gcode if there is a word until the word is reached.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, lineIndex)
Parse a gcode line.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text, towerRepository=None)
Tower a gcode linear move file or text.
+
getCraftedTextFromText(gcodeText, towerRepository=None)
Tower a gcode linear move text.
+
getNewRepository()
Get new repository.
+
main()
Display the tower dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Tower a gcode linear move file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.unpause.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.unpause.html new file mode 100644 index 0000000..566583b --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.unpause.html @@ -0,0 +1,169 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.unpause + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.unpause ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/unpause.py
+

+Previous / Next / Contents +

+


+The unpause plugin is based on the Shane Hathaway's patch to speed up a line segment to compensate for the delay of the microprocessor. The description is at:
+http://shane.willowrise.com/archives/delay-compensation-in-firmware/
+
+The unpause manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Unpause
+
+
+Operation
+Settings
+  Delay
+  Maximum Speed
+Examples
+
+

Operation

+
+ +The default 'Activate Unpause' checkbox is off. When it is on, the functions described below will work, when it is off, nothing will be done.
+
+

Settings

+
+ +

Delay

+ +Default is 28 milliseconds, which Shane found for the Arduino.
+
+Defines the delay on the microprocessor that will be at least partially compensated for.
+
+

Maximum Speed

+ +Default is 1.3.
+
+Defines the maximum amount that the feed rate will be sped up to, compared to the original feed rate.
+
+

Examples

+
+ +The following examples unpause the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and unpause.py.
+
+> python unpause.py
+This brings up the unpause dialog.
+
+> python unpause.py Screw Holder Bottom.stl
+The unpause tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The unpause tool has created the file:
+.. Screw Holder Bottom_unpause.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+
math
+fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+
skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
UnpauseRepository +
UnpauseSkein +
+

+ + + + + + + +
 
+class UnpauseRepository
   A class to handle the unpause settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Unpause button has been clicked.
+ +

+ + + + + + + +
 
+class UnpauseSkein
   A class to unpause a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the unpause gcode.
+ +
getUnpausedArcMovement(self, line, splitLine)
Get an unpaused arc movement.
+ +
getUnpausedLinearMovement(self, line, splitLine)
Get an unpaused linear movement.
+ +
getUnpausedMovement(self, distance, line, splitLine)
Get an unpaused movement.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, gcodeText, repository=None)
Unpause a gcode linear move file or text.
+
getCraftedTextFromText(gcodeText, repository=None)
Unpause a gcode linear move text.
+
getNewRepository()
Get new repository.
+
getSelectedPlugin(repository)
Get the selected plugin.
+
main()
Display the unpause dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Unpause a gcode linear move file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.whittle.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.whittle.html new file mode 100644 index 0000000..19b0c68 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.whittle.html @@ -0,0 +1,155 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.whittle + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.whittle ($Date: 2008/02/05 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/whittle.py
+

+Previous / Next / Contents +

+


+Whittle will convert each polygon of a gcode file into a helix which has a vertical step down on each rotation.
+
+
+Operation
+Settings
+  Maximum Vertical Step'
+Examples
+
+

Operation

+
+ +The default 'Activate Whittle' checkbox is on. When it is on, the functions described below will work, when it is off, the functions will not be called. If the cutting tool can cut the slab in one cut, the 'Activate Whittle' checkbox should be off, the default is off.
+
+

Settings

+
+ +

Maximum Vertical Step'

+ +Default is 0.1 mm.
+
+Defines the maximum distance that the helix will step down on each rotation. The number of steps in the helix will be the layer height divided by the 'Maximum Vertical Step', rounded up. The amount the helix will step down is the layer height divided by the number of steps. The thinner the 'Maximum Vertical Step', the more times the cutting tool will circle around on its way to the bottom of the slab.
+
+

Examples

+
+ +The following examples whittle the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and whittle.py.
+
+> python whittle.py
+This brings up the whittle dialog.
+
+> python whittle.py Screw Holder Bottom.stl
+The whittle tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The whittle tool has created the file:
+.. Screw Holder Bottom_whittle.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+
fabmetheus_utilities.gcodec
+math
+fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+
sys
+

+ + + + + +
 
+Classes
       
+
WhittleRepository +
WhittleSkein +
+

+ + + + + + + +
 
+class WhittleRepository
   A class to handle the whittle settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Whittle button has been clicked.
+ +

+ + + + + + + +
 
+class WhittleSkein
   A class to whittle a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
getCraftedGcode(self, whittleRepository, gcodeText)
Parse gcode text and store the whittle gcode.
+ +
getLinearMove(self, line, splitLine)
Get the linear move.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the whittle skein.
+ +
repeatLines(self)
Repeat the lines at decreasing altitude.
+ +
setLayerThinknessVerticalDeltas(self, splitLine)
Set the layer height and the vertical deltas.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text='', whittleRepository=None)
Whittle the preface file or text.
+
getCraftedTextFromText(gcodeText, whittleRepository=None)
Whittle the preface gcode text.
+
getNewRepository()
Get new repository.
+
main()
Display the whittle dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Whittle the carving of a gcode file.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__date__ = '$Date: 2008/02/05 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.widen.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.widen.html new file mode 100644 index 0000000..859403a --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.widen.html @@ -0,0 +1,155 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.widen + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.widen ($Date: 2008/28/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/widen.py
+

+Previous / Next / Contents +

+


+Widen will widen the outside edges away from the inside edges, so that the outsides will be at least two edge widths away from the insides and therefore the outside filaments will not overlap the inside filaments.
+
+For example, if a mug has a very thin wall, widen would widen the outside of the mug so that the wall of the mug would be two edge widths wide, and the outside wall filament would not overlap the inside filament.
+
+For another example, if the outside of the object runs right next to a hole, widen would widen the wall around the hole so that the wall would bulge out around the hole, and the outside filament would not overlap the hole filament.
+
+The widen manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Widen
+
+
+Operation
+Examples
+
+

Operation

+
+ +The default 'Activate Widen' checkbox is off. When it is on, widen will work, when it is off, nothing will be done.
+
+

Examples

+
+ +The following examples widen the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and widen.py.
+
+> python widen.py
+This brings up the widen dialog.
+
+> python widen.py Screw Holder Bottom.stl
+The widen tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The widen tool has created the file:
+.. Screw Holder Bottom_widen.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.geometry.geometry_utilities.boolean_solid
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+fabmetheus_utilities.intercircle
+os
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+
sys
+fabmetheus_utilities.geometry.solids.triangle_mesh
+

+ + + + + +
 
+Classes
       
+
WidenRepository +
WidenSkein +
+

+ + + + + + + +
 
+class WidenRepository
   A class to handle the widen settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Widen button has been clicked.
+ +

+ + + + + + + +
 
+class WidenSkein
   A class to widen a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
addWiden(self, loopLayer)
Add widen to the layer.
+ +
getCraftedGcode(self, gcodeText, repository)
Parse gcode text and store the widen gcode.
+ +
parseInitialization(self)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the widen skein.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text='', repository=None)
Widen the preface file or text.
+
getCraftedTextFromText(gcodeText, repository=None)
Widen the preface gcode text.
+
getIntersectingWithinLoops(loop, loopList, outsetLoop)
Get the loops which are intersecting or which it is within.
+
getIsIntersectingWithinLoop(loop, otherLoop, outsetLoop)
Determine if the loop is intersecting or is within the other loop.
+
getIsPointInsideALoop(loops, point)
Determine if a point is inside a loop of a loop list.
+
getNewRepository()
Get new repository.
+
getWidenedLoop(loop, loopList, outsetLoop, radius)
Get the widened loop.
+
main()
Display the widen dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Widen the carving of a gcode file.
+

+ + + + + +
 
+Data
       __author__ = 'Enrique Perez (perez_enrique@yahoo.com)'
+__date__ = '$Date: 2008/28/04 $'
+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.wipe.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.wipe.html new file mode 100644 index 0000000..465c9ec --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.craft_plugins.wipe.html @@ -0,0 +1,233 @@ + + +Python: module skeinforge_application.skeinforge_plugins.craft_plugins.wipe + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.craft_plugins.wipe ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/wipe.py
+

+Previous / Next / Contents +

+


+At the beginning of a layer, depending on the settings, wipe will move the nozzle with the extruder off to the arrival point, then to the wipe point, then to the departure point, then back to the layer.
+
+The wipe path is machine specific, so you'll probably have to change all the default locations.
+
+The wipe manual page is at:
+http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Wipe
+
+
+Operation
+Settings
+  Arrival Location
+    Arrival X
+    Arrival Y
+    Arrival Z
+  Departure Location
+    Departure X
+    Departure Y
+    Departure Z
+  Wipe Location
+    Wipe X
+    Wipe Y
+    Wipe Z
+  Wipe Period
+Examples
+
+

Operation

+
+ +The default 'Activate Wipe' checkbox is off. When it is on, the functions described below will work, when it is off, nothing will be done.
+
+

Settings

+
+ +

Arrival Location

+ +

Arrival X

+ +Default is minus seventy millimeters.
+
+Defines the x coordinate of the arrival location.
+
+

Arrival Y

+ +Default is minus fifty millimeters.
+
+Defines the y coordinate of the arrival location.
+
+

Arrival Z

+ +Default is fifty millimeters.
+
+Defines the z coordinate of the arrival location.
+
+

Departure Location

+ +

Departure X

+ +Default is minus seventy millimeters.
+
+Defines the x coordinate of the departure location.
+
+

Departure Y

+ +Default is minus forty millimeters.
+
+Defines the y coordinate of the departure location.
+
+

Departure Z

+ +Default is fifty millimeters.
+
+Defines the z coordinate of the departure location.
+
+

Wipe Location

+ +

Wipe X

+ +Default is minus seventy millimeters.
+
+Defines the x coordinate of the wipe location.
+
+

Wipe Y

+ +Default is minus seventy millimeters.
+
+Defines the y coordinate of the wipe location.
+
+

Wipe Z

+ +Default is fifty millimeters.
+
+Defines the z coordinate of the wipe location.
+
+

Wipe Period

+ +Default is three.
+
+Defines the number of layers between wipes. Wipe will always wipe just before layer zero, afterwards it will wipe every "Wipe Period" layers. With the default of three, wipe will wipe just before layer zero, layer three, layer six and so on.
+
+

Examples

+
+ +The following examples wipe the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and wipe.py.
+
+> python wipe.py
+This brings up the wipe dialog.
+
+> python wipe.py Screw Holder Bottom.stl
+The wipe tool is parsing the file:
+Screw Holder Bottom.stl
+..
+The wipe tool has created the file:
+.. Screw Holder Bottom_wipe.gcode
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+math
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_craft
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+

+ + + + + +
 
+Classes
       
+
WipeRepository +
WipeSkein +
+

+ + + + + + + +
 
+class WipeRepository
   A class to handle the wipe settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Wipe button has been clicked.
+ +

+ + + + + + + +
 
+class WipeSkein
   A class to wipe a skein of extrusions.
 
 Methods defined here:
+
__init__(self)
+ +
addHop(self, begin, end)
Add hop to highest point.
+ +
addWipeTravel(self, splitLine)
Add the wipe travel gcode.
+ +
getCraftedGcode(self, gcodeText, wipeRepository)
Parse gcode text and store the wipe gcode.
+ +
getLinearMoveWithFeedRate(self, feedRate, location)
Get a linear move line with the feedRate.
+ +
parseInitialization(self, wipeRepository)
Parse gcode initialization and store the parameters.
+ +
parseLine(self, line)
Parse a gcode line and add it to the bevel gcode.
+ +

+ + + + + +
 
+Functions
       
getCraftedText(fileName, text, wipeRepository=None)
Wipe a gcode linear move text.
+
getCraftedTextFromText(gcodeText, wipeRepository=None)
Wipe a gcode linear move text.
+
getNewRepository()
Get new repository.
+
main()
Display the wipe dialog.
+
writeOutput(fileName, shouldAnalyze=True)
Wipe a gcode linear move file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.help.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.help.html new file mode 100644 index 0000000..382571c --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.help.html @@ -0,0 +1,130 @@ + + +Python: module skeinforge_application.skeinforge_plugins.help + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.help ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/help.py
+

+Previous / Next / Contents +

+


+Help has buttons and menu items to open help, blog and forum pages in your primary browser.
+
+
+
+Link Buttons
+  Announcements
+    Fabmetheus Blog
+  Documentation
+    Index of Local Documentation
+    Wiki Manual
+    Skeinforge Overview
+  Forums
+    Bits from Bytes Printing Board
+    Bits from Bytes Software Board
+    Skeinforge Contributions Thread
+    Skeinforge Settings Thread
+Settings
+  Wiki Manual Primary
+
+

Link Buttons

+
+ +

Announcements

+ +

Fabmetheus Blog

+ +The skeinforge announcements blog and the place to post questions, bugs and skeinforge requests.
+
+

Documentation

+ +

Index of Local Documentation

+ +The list of the pages in the documentation folder.
+
+

Wiki Manual

+ +The skeinforge wiki with pictures and charts. It is the best and most readable source of skeinforge information and you are welcome to contribute.
+
+

Skeinforge Overview

+ +A general description of skeinforge, has answers to frequently asked questions and has many links to skeinforge, fabrication and python pages. It is also the help page of the skeinforge tool.
+
+

Forums

+ +

Bits from Bytes Printing Board

+ +Board about printing questions, problems and solutions. Most of the people on that forum use the rapman, but many of the solutions apply to any reprap.
+
+

Bits from Bytes Software Board

+ +Board about software, and has some skeinforge threads.
+
+

Skeinforge Contributions Thread

+ +Forum thread about how to contribute to skeinforge development.
+
+

Skeinforge Settings Thread

+ +Forum thread for people to post, download and discuss skeinforge settings.
+
+

Settings

+
+ +

Wiki Manual Primary

+ +Default is on.
+
+The help menu has an item for each button on the help page. Also, at the very top, it has a link to the local documentation and if there is a separate page for that tool in the wiki manual, a link to that page on the manual. If the 'Wiki Manual Primary' checkbutton is selected and there is a separate wiki manual page, the wiki page will be the primary document page, otherwise the local page will be primary. The help button (? symbol button) on the tool page will open the primary page, as will pressing <F1>. For example, if you click the the help button from the chamber tool, which has a separate page in the wiki, and 'Wiki Manual Primary' is selected, the wiki manual chamber page will be opened. Clicking F1 will also open the wiki manual chamber page.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+
os
+
fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_help
+

+ + + + + +
 
+Functions
       
addToMenu(master, menu, repository, window)
Add a tool plugin menu.
+
getNewRepository()
Get new repository.
+
main()
Display the help dialog.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.html new file mode 100644 index 0000000..0ca2022 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.html @@ -0,0 +1,39 @@ + + +Python: package skeinforge_application.skeinforge_plugins + + + + +
 
+ 
skeinforge_application.skeinforge_plugins
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
analyze
+analyze_plugins (package)
+craft
+
craft_plugins (package)
+help
+meta
+
meta_plugins (package)
+profile
+profile_plugins (package)
+

+ + + + + +
 
+Data
       level = 2
+numberOfLevelsDeepInPackageHierarchy = 2
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.meta.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.meta.html new file mode 100644 index 0000000..b6f3670 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.meta.html @@ -0,0 +1,63 @@ + + +Python: module skeinforge_application.skeinforge_plugins.meta + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.meta ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/meta.py
+

+Previous / Next / Contents +

+


+Meta is a script to access the plugins which handle meta information.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.archive
+
fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_meta
+

+ + + + + +
 
+Functions
       
addToMenu(master, menu, repository, window)
Add a tool plugin menu.
+
getNewRepository()
Get new repository.
+
main()
Display the meta dialog.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.meta_plugins.description.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.meta_plugins.description.html new file mode 100644 index 0000000..8a89ecf --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.meta_plugins.description.html @@ -0,0 +1,105 @@ + + +Python: module skeinforge_application.skeinforge_plugins.meta_plugins.description + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.meta_plugins.description ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/meta_plugins/description.py
+

+Previous / Next / Contents +

+


+Description is a script to store a description of the profile.
+
+
+Settings
+  Description Text
+Example
+
+

Settings

+
+ +

Description Text

+ +Default is 'Write your profile description here.'
+
+The suggested format is a description, followed by a link to a profile post or web page.
+
+

Example

+
+ +Example of using description follows below.
+
+> python description.py
+This brings up the description dialog.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+

+ + + + + +
 
+Classes
       
+
DescriptionRepository +
+

+ + + + + + + +
 
+class DescriptionRepository
   A class to handle the description settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +

+ + + + + +
 
+Functions
       
getNewRepository()
Get new repository.
+
main()
Display the file or directory dialog.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.meta_plugins.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.meta_plugins.html new file mode 100644 index 0000000..6e5a432 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.meta_plugins.html @@ -0,0 +1,32 @@ + + +Python: package skeinforge_application.skeinforge_plugins.meta_plugins + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.meta_plugins
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/meta_plugins/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
description
+
polyfile
+

+ + + + + +
 
+Data
       level = 3
+numberOfLevelsDeepInPackageHierarchy = 3
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.meta_plugins.polyfile.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.meta_plugins.polyfile.html new file mode 100644 index 0000000..dc35b18 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.meta_plugins.polyfile.html @@ -0,0 +1,90 @@ + + +Python: module skeinforge_application.skeinforge_plugins.meta_plugins.polyfile + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.meta_plugins.polyfile ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/meta_plugins/polyfile.py
+

+Previous / Next / Contents +

+


+Polyfile is a script to choose whether the skeinforge toolchain will operate on one file or all the files in a directory.
+
+
+Settings
+  Polyfile Choice
+    Execute File
+    Execute All Unmodified Files in a Directory'
+Example
+
+

Settings

+
+ +

Polyfile Choice

+ +Default is 'Execute File',
+
+

Execute File

+ +When selected, the toolchain will operate on only the chosen file.
+
+

Execute All Unmodified Files in a Directory'

+ +When selected, the toolchain will operate on all the unmodifed files in the directory that the chosen file is in.
+
+

Example

+
+ +Example of using polyfile follows below.
+
+> python polyfile.py
+This brings up the polyfile dialog.
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+
skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+

+ + + + + +
 
+Functions
       
getNewRepository()
Get new repository.
+
main()
Display the file or directory dialog.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.profile.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.profile.html new file mode 100644 index 0000000..53156f9 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.profile.html @@ -0,0 +1,113 @@ + + +Python: module skeinforge_application.skeinforge_plugins.profile + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.profile ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/profile.py
+

+Previous / Next / Contents +

+


+Profile is a script to set the craft types setting for the skeinforge chain.
+
+Profile presents the user with a choice of the craft types in the profile_plugins folder. The chosen craft type is used to determine the craft type profile for the skeinforge chain. The default craft type is extrusion.
+
+The setting is the selection. If you hit 'Save and Close' the selection will be saved, if you hit 'Cancel' the selection will not be saved.
+
+To change the profile setting, in a shell in the profile folder type:
+> python profile.py
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+
fabmetheus_utilities.euclidean
+os
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+

+ + + + + +
 
+Classes
       
+
ProfileMenuRadio +
ProfileMenuSaveListener +
+

+ + + + + + + +
 
+class ProfileMenuRadio
   A class to display a profile menu radio button.
 
 Methods defined here:
+
__init__(self, profilePluginFileName, menu, name, radioVar, value)
Create a profile menu radio.
+ +
clickRadio(self)
Workaround for Tkinter bug, invoke and set the value when clicked.
+ +

+ + + + + + + +
 
+class ProfileMenuSaveListener
   A class to update a profile menu.
 
 Methods defined here:
+
__init__(self, menu, window)
Set the menu.
+ +
save(self)
Profile has been saved and profile menu should be updated.
+ +

+ + + + + +
 
+Functions
       
addSubmenus(craftTypeName, menu, pluginFileName, pluginPath, profileRadioVar)
Add a tool plugin menu.
+
addToMenu(master, menu, repository, window)
Add a tool plugin menu.
+
addToProfileMenu(menu)
Add a profile menu.
+
getNewRepository()
Get new repository.
+
main()
Display the profile dialog.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.profile_plugins.cutting.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.profile_plugins.cutting.html new file mode 100644 index 0000000..97d36ed --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.profile_plugins.cutting.html @@ -0,0 +1,94 @@ + + +Python: module skeinforge_application.skeinforge_plugins.profile_plugins.cutting + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.profile_plugins.cutting ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/profile_plugins/cutting.py
+

+Previous / Next / Contents +

+


+Cutting is a script to set the cutting profile for the skeinforge chain.
+
+The displayed craft sequence is the sequence in which the tools craft the model and export the output.
+
+On the cutting dialog, clicking the 'Add Profile' button will duplicate the selected profile and give it the name in the input field. For example, if laser is selected and the name laser_10mm is in the input field, clicking the 'Add Profile' button will duplicate laser and save it as laser_10mm. The 'Delete Profile' button deletes the selected profile.
+
+The profile selection is the setting. If you hit 'Save and Close' the selection will be saved, if you hit 'Cancel' the selection will not be saved. However; adding and deleting a profile is a permanent action, for example 'Cancel' will not bring back any deleted profiles.
+
+To change the cutting profile, in a shell in the profile_plugins folder type:
+> python cutting.py
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+
sys
+

+ + + + + +
 
+Classes
       
+
CuttingRepository +
+

+ + + + + + + +
 
+class CuttingRepository
   A class to handle the cutting settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +

+ + + + + +
 
+Functions
       
getCraftSequence()
Get the cutting craft sequence.
+
getNewRepository()
Get new repository.
+
main()
Display the export dialog.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.profile_plugins.extrusion.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.profile_plugins.extrusion.html new file mode 100644 index 0000000..b042aec --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.profile_plugins.extrusion.html @@ -0,0 +1,94 @@ + + +Python: module skeinforge_application.skeinforge_plugins.profile_plugins.extrusion + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.profile_plugins.extrusion ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/profile_plugins/extrusion.py
+

+Previous / Next / Contents +

+


+Extrusion is a script to set the extrusion profile for the skeinforge chain.
+
+The displayed craft sequence is the sequence in which the tools craft the model and export the output.
+
+On the extrusion dialog, clicking the 'Add Profile' button will duplicate the selected profile and give it the name in the input field. For example, if ABS is selected and the name ABS_black is in the input field, clicking the 'Add Profile' button will duplicate ABS and save it as ABS_black. The 'Delete Profile' button deletes the selected profile.
+
+The profile selection is the setting. If you hit 'Save and Close' the selection will be saved, if you hit 'Cancel' the selection will not be saved. However; adding and deleting a profile is a permanent action, for example 'Cancel' will not bring back any deleted profiles.
+
+To change the extrusion profile, in a shell in the profile_plugins folder type:
+> python extrusion.py
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+
sys
+

+ + + + + +
 
+Classes
       
+
ExtrusionRepository +
+

+ + + + + + + +
 
+class ExtrusionRepository
   A class to handle the export settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +

+ + + + + +
 
+Functions
       
getCraftSequence()
Get the extrusion craft sequence.
+
getNewRepository()
Get new repository.
+
main()
Display the export dialog.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.profile_plugins.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.profile_plugins.html new file mode 100644 index 0000000..f826e1a --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.profile_plugins.html @@ -0,0 +1,34 @@ + + +Python: package skeinforge_application.skeinforge_plugins.profile_plugins + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.profile_plugins
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/profile_plugins/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
cutting
+
extrusion
+
milling
+
winding
+

+ + + + + +
 
+Data
       level = 3
+numberOfLevelsDeepInPackageHierarchy = 3
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.profile_plugins.milling.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.profile_plugins.milling.html new file mode 100644 index 0000000..e026650 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.profile_plugins.milling.html @@ -0,0 +1,94 @@ + + +Python: module skeinforge_application.skeinforge_plugins.profile_plugins.milling + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.profile_plugins.milling ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/profile_plugins/milling.py
+

+Previous / Next / Contents +

+


+Milling is a script to set the milling profile for the skeinforge chain.
+
+The displayed craft sequence is the sequence in which the tools craft the model and export the output.
+
+On the milling dialog, clicking the 'Add Profile' button will duplicate the selected profile and give it the name in the input field. For example, if laser is selected and the name laser_10mm is in the input field, clicking the 'Add Profile' button will duplicate laser and save it as laser_10mm. The 'Delete Profile' button deletes the selected profile.
+
+The profile selection is the setting. If you hit 'Save and Close' the selection will be saved, if you hit 'Cancel' the selection will not be saved. However; adding and deleting a profile is a permanent action, for example 'Cancel' will not bring back any deleted profiles.
+
+To change the milling profile, in a shell in the profile_plugins folder type:
+> python milling.py
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+
sys
+

+ + + + + +
 
+Classes
       
+
MillingRepository +
+

+ + + + + + + +
 
+class MillingRepository
   A class to handle the milling settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +

+ + + + + +
 
+Functions
       
getCraftSequence()
Get the milling craft sequence.
+
getNewRepository()
Get new repository.
+
main()
Display the export dialog.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.profile_plugins.winding.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.profile_plugins.winding.html new file mode 100644 index 0000000..d21baad --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_plugins.profile_plugins.winding.html @@ -0,0 +1,94 @@ + + +Python: module skeinforge_application.skeinforge_plugins.profile_plugins.winding + + + + +
 
+ 
skeinforge_application.skeinforge_plugins.profile_plugins.winding ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/profile_plugins/winding.py
+

+Previous / Next / Contents +

+


+Winding is a script to set the winding profile for the skeinforge chain.
+
+The displayed craft sequence is the sequence in which the tools craft the model and export the output.
+
+On the winding dialog, clicking the 'Add Profile' button will duplicate the selected profile and give it the name in the input field. For example, if laser is selected and the name laser_10mm is in the input field, clicking the 'Add Profile' button will duplicate laser and save it as laser_10mm. The 'Delete Profile' button deletes the selected profile.
+
+The profile selection is the setting. If you hit 'Save and Close' the selection will be saved, if you hit 'Cancel' the selection will not be saved. However; adding and deleting a profile is a permanent action, for example 'Cancel' will not bring back any deleted profiles.
+
+To change the winding profile, in a shell in the profile_plugins folder type:
+> python winding.py
+
+
+

+Previous / Next / Contents +

+
+

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+
sys
+

+ + + + + +
 
+Classes
       
+
WindingRepository +
+

+ + + + + + + +
 
+class WindingRepository
   A class to handle the winding settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +

+ + + + + +
 
+Functions
       
getCraftSequence()
Get the winding craft sequence.
+
getNewRepository()
Get new repository.
+
main()
Display the export dialog.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_utilities.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_utilities.html new file mode 100644 index 0000000..cd0cac2 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_utilities.html @@ -0,0 +1,36 @@ + + +Python: package skeinforge_application.skeinforge_utilities + + + + +
 
+ 
skeinforge_application.skeinforge_utilities
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_utilities/__init__.py
+

#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module.

+

+ + + + + +
 
+Package Contents
       
skeinforge_analyze
+skeinforge_craft
+
skeinforge_help
+skeinforge_meta
+
skeinforge_polyfile
+skeinforge_profile
+

+ + + + + +
 
+Data
       level = 2
+numberOfLevelsDeepInPackageHierarchy = 2
+packageFilePath = '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus'
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_utilities.skeinforge_analyze.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_utilities.skeinforge_analyze.html new file mode 100644 index 0000000..6d7d03b --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_utilities.skeinforge_analyze.html @@ -0,0 +1,83 @@ + + +Python: module skeinforge_application.skeinforge_utilities.skeinforge_analyze + + + + +
 
+ 
skeinforge_application.skeinforge_utilities.skeinforge_analyze ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_utilities/skeinforge_analyze.py
+

Analyze is a script to access the plugins which analyze a gcode file.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.gcodec
+
os
+fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+traceback
+

+ + + + + +
 
+Classes
       
+
AnalyzeRepository +
+

+ + + + + + + +
 
+class AnalyzeRepository
   A class to handle the analyze settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Analyze button has been clicked.
+ +

+ + + + + +
 
+Functions
       
getNewRepository()
Get new repository.
+
getPluginFileNames()
Get analyze plugin fileNames.
+
getPluginsDirectoryPath()
Get the plugins directory path.
+
main()
Write analyze output.
+
writeOutput(fileName, fileNamePenultimate, fileNameSuffix, filePenultimateWritten, gcodeText='')
Analyze a gcode file.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_utilities.skeinforge_craft.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_utilities.skeinforge_craft.html new file mode 100644 index 0000000..881b12c --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_utilities.skeinforge_craft.html @@ -0,0 +1,119 @@ + + +Python: module skeinforge_application.skeinforge_utilities.skeinforge_craft + + + + +
 
+ 
skeinforge_application.skeinforge_utilities.skeinforge_craft ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_utilities/skeinforge_craft.py
+

Craft is a script to access the plugins which craft a gcode file.
+
+The plugin buttons which are commonly used are bolded and the ones which are rarely used have normal font weight.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+fabmetheus_utilities.euclidean
+
fabmetheus_utilities.fabmetheus_tools.fabmetheus_interpret
+fabmetheus_utilities.gcodec
+os
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_analyze
+skeinforge_application.skeinforge_utilities.skeinforge_polyfile
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+sys
+time
+

+ + + + + +
 
+Classes
       
+
CraftRadioButtonsSaveListener +
CraftRepository +
+

+ + + + + + + +
 
+class CraftRadioButtonsSaveListener
   A class to update the craft radio buttons.
 
 Methods defined here:
+
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
getFromRadioPlugins(self, radioPlugins, repository)
Initialize.
+ +
save(self)
Profile has been saved and craft radio plugins should be updated.
+ +
setRadioButtons(self)
Profile has been saved and craft radio plugins should be updated.
+ +

+ + + + + + + +
 
+class CraftRepository
   A class to handle the craft settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
execute(self)
Craft button has been clicked.
+ +

+ + + + + +
 
+Functions
       
getChainText(fileName, procedure)
Get a crafted shape file.
+
getChainTextFromProcedures(fileName, procedures, text)
Get a crafted shape file from a list of procedures.
+
getCraftModule(pluginName)
Get craft module.
+
getCraftPreferences(pluginName)
Get craft preferences.
+
getCraftValue(preferenceName, preferences)
Get craft preferences value.
+
getLastModule()
Get the last tool.
+
getNewRepository()
Get new repository.
+
getPluginFileNames()
Get craft plugin fileNames.
+
getPluginsDirectoryPath()
Get the plugins directory path.
+
getProcedures(procedure, text)
Get the procedures up to and including the given procedure.
+
getReadCraftSequence()
Get profile sequence.
+
getSequenceIndexFromProcedure(procedure)
Get the profile sequence index of the procedure.  Return None if the procedure is not in the sequence
+
getSequenceIndexPlusOneFromText(fileText)
Get the profile sequence index of the file plus one.  Return zero if the procedure is not in the file
+
main()
Write craft output.
+
writeChainTextWithNounMessage(fileName, procedure, shouldAnalyze=True)
Get and write a crafted shape file.
+
writeOutput(fileName, shouldAnalyze=True)
Craft a gcode file with the last module.
+
writeSVGTextWithNounMessage(fileName, repository, shouldAnalyze=True)
Get and write an svg text and print messages.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_utilities.skeinforge_help.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_utilities.skeinforge_help.html new file mode 100644 index 0000000..be0b5a1 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_utilities.skeinforge_help.html @@ -0,0 +1,74 @@ + + +Python: module skeinforge_application.skeinforge_utilities.skeinforge_help + + + + +
 
+ 
skeinforge_application.skeinforge_utilities.skeinforge_help ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_utilities/skeinforge_help.py
+

Help has buttons and menu items to open help, blog and forum pages in your primary browser.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.archive
+
fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+

+ + + + + +
 
+Classes
       
+
HelpRepository +
+

+ + + + + + + +
 
+class HelpRepository
   A class to handle the help settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +
save(self)
Write the entities.
+ +

+ + + + + +
 
+Functions
       
getNewRepository()
Get new repository.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_utilities.skeinforge_meta.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_utilities.skeinforge_meta.html new file mode 100644 index 0000000..001f653 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_utilities.skeinforge_meta.html @@ -0,0 +1,76 @@ + + +Python: module skeinforge_application.skeinforge_utilities.skeinforge_meta + + + + +
 
+ 
skeinforge_application.skeinforge_utilities.skeinforge_meta ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_utilities/skeinforge_meta.py
+

Meta is a script to access the plugins which handle meta information.

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+
fabmetheus_utilities.gcodec
+os
+
fabmetheus_utilities.settings
+skeinforge_application.skeinforge_utilities.skeinforge_profile
+

+ + + + + +
 
+Classes
       
+
MetaRepository +
+

+ + + + + + + +
 
+class MetaRepository
   A class to handle the meta settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +

+ + + + + +
 
+Functions
       
getNewRepository()
Get new repository.
+
getPluginFileNames()
Get meta plugin file names.
+
getPluginsDirectoryPath()
Get the plugins directory path.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_utilities.skeinforge_polyfile.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_utilities.skeinforge_polyfile.html new file mode 100644 index 0000000..9f65698 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_utilities.skeinforge_polyfile.html @@ -0,0 +1,77 @@ + + +Python: module skeinforge_application.skeinforge_utilities.skeinforge_polyfile + + + + +
 
+ 
skeinforge_application.skeinforge_utilities.skeinforge_polyfile ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_utilities/skeinforge_polyfile.py
+

Polyfile is a script to choose whether the skeinforge toolchain will operate on one file or all the files in a directory.

+

+ + + + + +
 
+Modules
       
__init__
+
fabmetheus_utilities.archive
+
fabmetheus_utilities.settings
+
skeinforge_application.skeinforge_utilities.skeinforge_profile
+

+ + + + + +
 
+Classes
       
+
PolyfileRepository +
+

+ + + + + + + +
 
+class PolyfileRepository
   A class to handle the polyfile settings.
 
 Methods defined here:
+
__init__(self)
Set the default settings, execute title & settings fileName.
+ +

+ + + + + +
 
+Functions
       
getFileOrDirectoryTypes(fileName, fileTypes, wasCancelled)
Get the gcode files in the directory the file is in if directory setting is true.  Otherwise, return the file in a list.
+
getFileOrDirectoryTypesUnmodifiedGcode(fileName, fileTypes, wasCancelled)
Get the gcode files in the directory the file is in if directory setting is true.  Otherwise, return the file in a list.
+
getFileOrGcodeDirectory(fileName, wasCancelled, words=[])
Get the gcode files in the directory the file is in if directory setting is true.  Otherwise, return the file in a list.
+
getNewRepository()
Get new repository.
+
isDirectorySetting()
Determine if the directory setting is true.
+
isEmptyOrCancelled(fileName, wasCancelled)
Determine if the fileName is empty or the dialog was cancelled.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/documentation/skeinforge_application.skeinforge_utilities.skeinforge_profile.html b/SkeinPyPy/documentation/skeinforge_application.skeinforge_utilities.skeinforge_profile.html new file mode 100644 index 0000000..fde0022 --- /dev/null +++ b/SkeinPyPy/documentation/skeinforge_application.skeinforge_utilities.skeinforge_profile.html @@ -0,0 +1,300 @@ + + +Python: module skeinforge_application.skeinforge_utilities.skeinforge_profile + + + + +
 
+ 
skeinforge_application.skeinforge_utilities.skeinforge_profile ($Date: 2008/21/04 $)
index
/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_utilities/skeinforge_profile.py
+

Profile is a script to set the craft types setting for the skeinforge chain.
+
+Profile presents the user with a choice of the craft types in the profile_plugins folder. The chosen craft type is used to determine the craft type profile for the skeinforge chain. The default craft type is extrusion.
+
+The setting is the selection. If you hit 'Save and Close' the selection will be saved, if you hit 'Cancel' the selection will not be saved.
+
+To change the profile setting, in a shell in the profile folder type:
+> python profile.py

+

+ + + + + +
 
+Modules
       
__init__
+fabmetheus_utilities.archive
+
fabmetheus_utilities.euclidean
+fabmetheus_utilities.gcodec
+
os
+fabmetheus_utilities.settings
+
shutil
+

+ + + + + +
 
+Classes
       
+
fabmetheus_utilities.settings.StringSetting +
+
+
ProfileListboxSetting +
+
+
AddProfile +
+
+
DeleteProfile +
+
+
DeleteProfileDialog +
ProfileList +
ProfilePluginRadioButtonsSaveListener +
ProfileRepository +
ProfileSelectionMenuRadio +
+
+
ProfileTypeMenuRadio +
+
+
+

+ + + + + + + +
 
+class AddProfile
   A class to add a profile.
 
 Methods defined here:
+
addSelection(self)
Add the selection of a listbox setting.
+ +
addSelectionWithEvent(self, event)
Add the selection of a listbox setting, given an event.
+ +
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
getFromProfileListboxSettingRepository(self, profileListboxSetting, repository)
Initialize.
+ +

+ + + + + + + +
 
+class DeleteProfile(AddProfile)
   A class to delete the selection of a listbox profile.
 
 Methods defined here:
+
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
deleteSelection(self)
Delete the selection of a listbox setting.
+ +
+Methods inherited from AddProfile:
+
addSelection(self)
Add the selection of a listbox setting.
+ +
addSelectionWithEvent(self, event)
Add the selection of a listbox setting, given an event.
+ +
getFromProfileListboxSettingRepository(self, profileListboxSetting, repository)
Initialize.
+ +

+ + + + + + + +
 
+class DeleteProfileDialog
   A dialog to delete a profile.
 
 Methods defined here:
+
__init__(self, profileListboxSetting, root)
Display a delete dialog.
+ +
delete(self)
Delete the selection of a listbox setting.
+ +
no(self)
The dialog was closed.
+ +

+ + + + + + + +
 
+class ProfileList
   A class to list the profiles.
 
 Methods defined here:
+
getFromName(self, name, repository)
Initialize.
+ +
setValueToFolders(self)
Set the value to the folders in the profiles directories.
+ +

+ + + + + + + +
 
+class ProfileListboxSetting(fabmetheus_utilities.settings.StringSetting)
   A class to handle the profile listbox.
 
 Methods defined here:
+
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
buttonReleaseOne(self, event)
Button one released.
+ +
focusIn(self, event)
The root has gained focus.
+ +
getFromListSetting(self, listSetting, name, repository, value)
Initialize.
+ +
getSelectedFolder(self)
Get the selected folder.
+ +
setStateToValue(self)
Set the listbox items to the list setting.
+ +
setToDisplay(self)
Set the selection value to the listbox selection.
+ +
setValueToIndex(self, index)
Set the selection value to the index.
+ +
setValueToString(self, valueString)
Set the value to the value string.
+ +
+Methods inherited from fabmetheus_utilities.settings.StringSetting:
+
__init__(self)
Set the update function to none.
+ +
__repr__(self)
Get the string representation of this StringSetting.
+ +
addToMenu(self, repositoryMenu)
Do nothing because this should only be added to a frameable repository menu.
+ +
addToMenuFrameable(self, repositoryMenu)
Add this to the frameable repository menu.
+ +
addToWindow(self)
Add this to the repository frame list.
+ +
bindEntry(self)
Bind the entry to the update function.
+ +
createEntry(self, root)
Create the entry.
+ +
getFromValue(self, name, repository, value)
Initialize.
+ +
getFromValueOnly(self, name, repository, value)
Initialize.
+ +
getFromValueOnlyAddToRepository(self, name, repository, value)
Initialize.
+ +
removeFromWindow(self)
Remove this from the repository frame list.
+ +
setUpdateFunction(self, updateFunction)
Set the update function.
+ +
setValueToSplitLine(self, lineIndex, lines, splitLine)
Set the value to the second word of a split line.
+ +
updateSaveListeners(self)
Update save listeners if any.
+ +
writeToRepositoryWriter(self, repositoryWriter)
Write tab separated name and value to the repository writer.
+ +

+ + + + + + + +
 
+class ProfilePluginRadioButtonsSaveListener
   A class to update the profile radio buttons.
 
 Methods defined here:
+
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
getFromRadioPlugins(self, radioPlugins, repository)
Initialize.
+ +
save(self)
Profile has been saved and profile radio plugins should be updated.
+ +

+ + + + + + + +
 
+class ProfileRepository
   A class to handle the profile entities.
 
 Methods defined here:
+
__init__(self)
Set the default entities, execute title & repository fileName.
+ +
updateRelay(self)
Update the plugin frame then the ProfileSaveListeners.
+ +

+ + + + + + + +
 
+class ProfileSelectionMenuRadio
   A class to display a profile selection menu radio button.
 
 Methods defined here:
+
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
clickRadio(self)
Workaround for Tkinter bug, invoke and set the value when clicked.
+ +
getFromMenuButtonDisplay(self, menuButtonDisplay, name, repository, value)
Initialize.
+ +
setToMenuButtonDisplay(self, menuButtonDisplay, name, repository, value)
Initialize.
+ +

+ + + + + + + +
 
+class ProfileTypeMenuRadio(ProfileSelectionMenuRadio)
   A class to display a profile type menu radio button.
 
 Methods defined here:
+
clickRadio(self)
Workaround for Tkinter bug, invoke and set the value when clicked.
+ +
getFromMenuButtonDisplay(self, menuButtonDisplay, name, repository, value)
Initialize.
+ +
+Methods inherited from ProfileSelectionMenuRadio:
+
addToDialog(self, gridPosition)
Add this to the dialog.
+ +
setToMenuButtonDisplay(self, menuButtonDisplay, name, repository, value)
Initialize.
+ +

+ + + + + +
 
+Functions
       
addListsSetCraftProfile(craftSequence, defaultProfile, repository, fileNameHelp)
Set the craft profile repository.
+
addListsToCraftTypeRepository(fileNameHelp, repository)
Add the value to the lists.
+
cancelAll()
Cancel all the dialogs.
+
getCraftTypeName(subName='')
Get the craft type from the profile.
+
getCraftTypePluginModule(craftTypeName='')
Get the craft type plugin module.
+
getNewRepository()
Get new repository.
+
getPluginFileNames()
Get analyze plugin fileNames.
+
getPluginsDirectoryPath()
Get the plugins directory path.
+
getProfileDirectory()
Get the profile directory.
+
getProfileName(craftTypeName)
Get the profile name from the craft type name.
+
getReadProfileRepository()
Get the read profile repository.
+
updateProfileSaveListeners()
Call the save function of all the update profile save listeners.
+

+ + + + + +
 
+Data
       __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'
+absolute_import = _Feature((2, 5, 0, 'alpha', 1), (2, 7, 0, 'alpha', 0), 16384)

+ + + + + +
 
+Author
       Enrique Perez (perez_enrique@yahoo.com)
+ \ No newline at end of file diff --git a/SkeinPyPy/fabmetheus_utilities/__init__.py b/SkeinPyPy/fabmetheus_utilities/__init__.py new file mode 100644 index 0000000..1638033 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/__init__.py @@ -0,0 +1,13 @@ +""" +This page is in the table of contents. +This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +""" +import os +import sys + +numberOfLevelsDeepInPackageHierarchy = 1 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/archive.py b/SkeinPyPy/fabmetheus_utilities/archive.py new file mode 100644 index 0000000..8b6d93f --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/archive.py @@ -0,0 +1,380 @@ +""" +Boolean geometry utilities. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +import os +import sys +import traceback + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalTemporarySettingsPath = os.path.join(os.path.expanduser('~'), '.skeinforge_pypy') + + +def addToNamePathDictionary(directoryPath, namePathDictionary): + 'Add to the name path dictionary.' + pluginFileNames = getPluginFileNamesFromDirectoryPath(directoryPath) + for pluginFileName in pluginFileNames: + namePathDictionary[pluginFileName.replace('_', '')] = os.path.join(directoryPath, pluginFileName) + +def getAbsoluteFolderPath(filePath, folderName=''): + 'Get the absolute folder path.' + absoluteFolderPath = os.path.dirname(os.path.abspath(filePath)) + if folderName == '': + return absoluteFolderPath + return os.path.join(absoluteFolderPath, folderName) + +def getAbsoluteFrozenFolderPath(filePath, folderName=''): + 'Get the absolute frozen folder path.' + if hasattr(sys, 'frozen'): + if '.py' in filePath: + filePath = ''.join(filePath.rpartition('\\')[: 2]) + filePath = os.path.join(filePath, 'skeinforge_application') + return getAbsoluteFolderPath(filePath, folderName) + +def getAnalyzePluginsDirectoryPath(subName=''): + 'Get the analyze plugins directory path.' + return getJoinedPath(getSkeinforgePluginsPath('analyze_plugins'), subName) + +def getCraftPluginsDirectoryPath(subName=''): + 'Get the craft plugins directory path.' + return getJoinedPath(getSkeinforgePluginsPath('craft_plugins'), subName) + +def getDocumentationPath(subName=''): + 'Get the documentation file path.' + return getJoinedPath(getFabmetheusPath('documentation'), subName) + +def getElementsPath(subName=''): + 'Get the evaluate_elements directory path.' + return getJoinedPath(getGeometryUtilitiesPath('evaluate_elements'), subName) + +def getEndsWithList(word, wordEndings): + 'Determine if the word ends with a list.' + for wordEnding in wordEndings: + if word.endswith(wordEnding): + return True + return False + +def getFabmetheusPath(subName=''): + 'Get the fabmetheus directory path.' + fabmetheusFile = None + if hasattr(sys, 'frozen'): + fabmetheusFile = unicode(sys.executable, sys.getfilesystemencoding()) + else: + fabmetheusFile = os.path.dirname(os.path.abspath(__file__)) + return getJoinedPath(os.path.dirname(fabmetheusFile), subName) + +def getFabmetheusToolsPath(subName=''): + 'Get the fabmetheus tools directory path.' + return getJoinedPath(getFabmetheusUtilitiesPath('fabmetheus_tools'), subName) + +def getFabmetheusUtilitiesPath(subName=''): + 'Get the fabmetheus utilities directory path.' + return getJoinedPath(getFabmetheusPath('fabmetheus_utilities'), subName) + +def getFileNamesByFilePaths(pluginFilePaths): + 'Get the file names of the plugins by the file paths.' + fileNames = [] + for pluginFilePath in pluginFilePaths: + pluginBasename = os.path.basename(pluginFilePath) + pluginBasename = getUntilDot(pluginBasename) + fileNames.append(pluginBasename) + return fileNames + +def getFilePaths(fileInDirectory=''): + 'Get the file paths in the directory of the file in directory.' + directoryName = os.getcwd() + if fileInDirectory != '': + directoryName = os.path.dirname(fileInDirectory) + return getFilePathsByDirectory(directoryName) + +def getFilePathsByDirectory(directoryName): + 'Get the file paths in the directory of the file in directory.' + absoluteDirectoryPath = os.path.abspath(directoryName) + directory = os.listdir(directoryName) + filePaths = [] + for fileName in directory: + filePaths.append(os.path.join(absoluteDirectoryPath, fileName)) + return filePaths + +def getFilePathsRecursively(fileInDirectory=''): + 'Get the file paths in the directory of the file in directory.' + filePaths = getFilePaths(fileInDirectory) + filePathsRecursively = filePaths[:] + for filePath in filePaths: + if os.path.isdir(filePath): + directory = os.listdir(filePath) + if len(directory) > 0: + filePathsRecursively += getFilePathsRecursively(os.path.join(filePath, directory[0])) + return filePathsRecursively + +def getFilePathWithUnderscoredBasename(fileName, suffix): + 'Get the file path with all spaces in the basename replaced with underscores.' + suffixFileName = getUntilDot(fileName) + suffix + suffixDirectoryName = os.path.dirname(suffixFileName) + suffixReplacedBaseName = os.path.basename(suffixFileName).replace(' ', '_') + return os.path.join(suffixDirectoryName, suffixReplacedBaseName) + +def getFilesWithFileTypesWithoutWords(fileTypes, words = [], fileInDirectory=''): + 'Get files which have a given file type, but with do not contain a word in a list.' + filesWithFileTypes = [] + for filePath in getFilePaths(fileInDirectory): + for fileType in fileTypes: + if isFileWithFileTypeWithoutWords(fileType, filePath, words): + filesWithFileTypes.append(filePath) + filesWithFileTypes.sort() + return filesWithFileTypes + +def getFilesWithFileTypesWithoutWordsRecursively(fileTypes, words = [], fileInDirectory=''): + 'Get files recursively which have a given file type, but with do not contain a word in a list.' + filesWithFileTypesRecursively = [] + for filePath in getFilePathsRecursively(fileInDirectory): + for fileType in fileTypes: + if isFileWithFileTypeWithoutWords(fileType, filePath, words): + filesWithFileTypesRecursively.append(filePath) + filesWithFileTypesRecursively.sort() + return filesWithFileTypesRecursively + +def getFilesWithFileTypeWithoutWords(fileType, words = [], fileInDirectory=''): + 'Get files which have a given file type, but with do not contain a word in a list.' + filesWithFileType = [] + for filePath in getFilePaths(fileInDirectory): + if isFileWithFileTypeWithoutWords(fileType, filePath, words): + filesWithFileType.append(filePath) + filesWithFileType.sort() + return filesWithFileType + +def getFileText(fileName, printWarning=True, readMode='r'): + 'Get the entire text of a file.' + try: + file = open(fileName, readMode) + fileText = file.read() + file.close() + return fileText + except IOError: + if printWarning: + print('The file ' + fileName + ' does not exist.') + return '' + +def getFileTextInFileDirectory(fileInDirectory, fileName, readMode='r'): + 'Get the entire text of a file in the directory of the file in directory.' + absoluteFilePathInFileDirectory = os.path.join(os.path.dirname(fileInDirectory), fileName) + return getFileText(absoluteFilePathInFileDirectory, True, readMode) + +def getFundamentalsPath(subName=''): + 'Get the evaluate_fundamentals directory path.' + return getJoinedPath(getGeometryUtilitiesPath('evaluate_fundamentals'), subName) + +def getGeometryDictionary(folderName): + 'Get to the geometry name path dictionary.' + geometryDictionary={} + geometryDirectory = getGeometryPath() + addToNamePathDictionary(os.path.join(geometryDirectory, folderName), geometryDictionary) + geometryPluginsDirectory = getFabmetheusUtilitiesPath('geometry_plugins') + addToNamePathDictionary(os.path.join(geometryPluginsDirectory, folderName), geometryDictionary) + return geometryDictionary + +def getGeometryPath(subName=''): + 'Get the geometry directory path.' + return getJoinedPath(getFabmetheusUtilitiesPath('geometry'), subName) + +def getGeometryToolsPath(subName=''): + 'Get the geometry tools directory path.' + return getJoinedPath(getGeometryPath('geometry_tools'), subName) + +def getGeometryUtilitiesPath(subName=''): + 'Get the geometry_utilities directory path.' + return getJoinedPath(getGeometryPath('geometry_utilities'), subName) + +def getInterpretPluginsPath(subName=''): + 'Get the interpret plugins directory path.' + return getJoinedPath(getFabmetheusToolsPath('interpret_plugins'), subName) + +def getJoinedPath(path, subName=''): + 'Get the joined file path.' + if subName == '': + return path + return os.path.join(path, subName) + +def getModuleWithDirectoryPath(directoryPath, fileName): + 'Get the module from the fileName and folder name.' + if fileName == '': + print('The file name in getModule in archive was empty.') + return None + originalSystemPath = sys.path[:] + try: + sys.path.insert(0, directoryPath) + folderPluginsModule = __import__(fileName) + sys.path = originalSystemPath + return folderPluginsModule + except: + sys.path = originalSystemPath + print('') + print('Exception traceback in getModuleWithDirectoryPath in archive:') + traceback.print_exc(file=sys.stdout) + print('') + print('That error means; could not import a module with the fileName ' + fileName) + print('and an absolute directory name of ' + directoryPath) + print('') + return None + +def getModuleWithPath(path): + 'Get the module from the path.' + return getModuleWithDirectoryPath(os.path.dirname(path), os.path.basename(path)) + +def getPluginFileNamesFromDirectoryPath(directoryPath): + 'Get the file names of the python plugins in the directory path.' + fileInDirectory = os.path.join(directoryPath, '__init__.py') + return getFileNamesByFilePaths(getPythonFileNamesExceptInit(fileInDirectory)) + +def getProfilesPath(subName=''): + 'Get the profiles directory path, which is the settings directory joined with profiles.' + return getJoinedPath(getSettingsPath('profiles'), subName) + +def getPythonDirectoryNames(directoryName): + 'Get the python directories.' + pythonDirectoryNames = [] + directory = os.listdir(directoryName) + for fileName in directory: + subdirectoryName = os.path.join(directoryName, fileName) + if os.path.isdir(subdirectoryName): + if os.path.isfile(os.path.join(subdirectoryName, '__init__.py')): + pythonDirectoryNames.append(subdirectoryName) + return pythonDirectoryNames + +def getPythonDirectoryNamesRecursively(directoryName=''): + 'Get the python directories recursively.' + recursivePythonDirectoryNames = [] + if directoryName == '': + directoryName = os.getcwd() + if os.path.isfile(os.path.join(directoryName, '__init__.py')): + recursivePythonDirectoryNames.append(directoryName) + pythonDirectoryNames = getPythonDirectoryNames(directoryName) + for pythonDirectoryName in pythonDirectoryNames: + recursivePythonDirectoryNames += getPythonDirectoryNamesRecursively(pythonDirectoryName) + else: + return [] + return recursivePythonDirectoryNames + +def getPythonFileNamesExceptInit(fileInDirectory=''): + 'Get the python fileNames of the directory which the fileInDirectory is in, except for the __init__.py file.' + pythonFileNamesExceptInit = getFilesWithFileTypeWithoutWords('py', ['__init__.py'], fileInDirectory) + pythonFileNamesExceptInit.sort() + return pythonFileNamesExceptInit + +def getPythonFileNamesExceptInitRecursively(directoryName=''): + 'Get the python fileNames of the directory recursively, except for the __init__.py files.' + pythonDirectoryNames = getPythonDirectoryNamesRecursively(directoryName) + pythonFileNamesExceptInitRecursively = [] + for pythonDirectoryName in pythonDirectoryNames: + pythonFileNamesExceptInitRecursively += getPythonFileNamesExceptInit(os.path.join(pythonDirectoryName, '__init__.py')) + pythonFileNamesExceptInitRecursively.sort() + return pythonFileNamesExceptInitRecursively + +def getSettingsPath(subName=''): + 'Get the settings directory path, which is the home directory joined with .skeinforge.' + global globalTemporarySettingsPath + return getJoinedPath(globalTemporarySettingsPath, subName) + +def getSkeinforgePath(subName=''): + 'Get the skeinforge directory path.' + return getJoinedPath(getFabmetheusPath('skeinforge_application'), subName) + +def getSkeinforgePluginsPath(subName=''): + 'Get the skeinforge plugins directory path.' + return getJoinedPath(getSkeinforgePath('skeinforge_plugins'), subName) + +def getSummarizedFileName(fileName): + 'Get the fileName basename if the file is in the current working directory, otherwise return the original full name.' + if os.getcwd() == os.path.dirname(fileName): + return os.path.basename(fileName) + return fileName + +def getTemplatesPath(subName=''): + 'Get the templates directory path.' + return getJoinedPath(getFabmetheusUtilitiesPath('templates'), subName) + +def getTextIfEmpty(fileName, text): + 'Get the text from a file if it the text is empty.' + if text != '': + return text + return getFileText(fileName) + +def getTextLines(text): + 'Get the all the lines of text of a text.' + if '\r' in text: + text = text.replace('\r', '\n').replace('\n\n', '\n') + textLines = text.split('\n') + if len(textLines) == 1: + if textLines[0] == '': + return [] + return textLines + +def getUntilDot(text): + 'Get the text until the last dot, if any.' + dotIndex = text.rfind('.') + if dotIndex < 0: + return text + return text[: dotIndex] + +def getVersionFileName(): + 'Get the file name of the version date.getFabmetheusUtilitiesPath(subName='')' + return getFabmetheusUtilitiesPath('version.txt') + +def isFileWithFileTypeWithoutWords(fileType, fileName, words): + 'Determine if file has a given file type, but with does not contain a word in a list.' + fileName = os.path.basename(fileName) + fileTypeDot = '.' + fileType + if not fileName.endswith(fileTypeDot): + return False + for word in words: + if fileName.find(word) >= 0: + return False + return True + +def makeDirectory(directoryPath): + 'Make a directory if it does not already exist.' + if os.path.isdir(directoryPath): + return + try: + print('The following directory was made:') + print(os.path.abspath(directoryPath)) + os.makedirs(directoryPath) + except OSError: + print('Skeinforge can not make the directory %s so give it read/write permission for that directory and the containing directory.' % directoryPath) + +def removeBackupFilesByType(fileType): + 'Remove backup files by type.' + backupFilePaths = getFilesWithFileTypesWithoutWordsRecursively([fileType + '~']) + for backupFilePath in backupFilePaths: + os.remove(backupFilePath) + +def removeBackupFilesByTypes(fileTypes): + 'Remove backup files by types.' + for fileType in fileTypes: + removeBackupFilesByType(fileType) + +def writeFileMessageEnd(end, fileName, fileText, message): + 'Write to a fileName with a suffix and print a message.' + suffixFileName = getUntilDot(fileName) + end + writeFileText(suffixFileName, fileText) + print(message + getSummarizedFileName(suffixFileName)) + +def writeFileText(fileName, fileText, writeMode='w+'): + 'Write a text to a file.' + try: + file = open(fileName, writeMode) + file.write(fileText) + file.close() + except IOError: + print('The file ' + fileName + ' can not be written to.') diff --git a/SkeinPyPy/fabmetheus_utilities/euclidean.py b/SkeinPyPy/fabmetheus_utilities/euclidean.py new file mode 100644 index 0000000..072a5fa --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/euclidean.py @@ -0,0 +1,2498 @@ +""" +Euclidean is a collection of python utilities for complex numbers, paths, polygons & Vector3s. + +To use euclidean, install python 2.x on your machine, which is avaliable from http://www.python.org/download/ + +Then in the folder which euclidean is in, type 'python' in a shell to run the python interpreter. Finally type 'import euclidean' to import these utilities and 'from vector3 import Vector3' to import the Vector3 class. + + +Below are examples of euclidean use. + +>>> from euclidean import * +>>> origin=complex() +>>> right=complex(1.0,0.0) +>>> back=complex(0.0,1.0) +>>> getMaximum(right,back) +1.0, 1.0 +>>> polygon=[origin, right, back] +>>> getLoopLength(polygon) +3.4142135623730949 +>>> getAreaLoop(polygon) +0.5 +""" + +from __future__ import absolute_import +try: + import psyco + psyco.full() +except: + pass +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import xml_simple_writer +import cStringIO +import math +import random + + +__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' + + +globalGoldenAngle = 3.8832220774509332 # (math.sqrt(5.0) - 1.0) * math.pi +globalGoldenRatio = 1.6180339887498948482045868 # math.sqrt(1.25) + 0.5 +globalTau = math.pi + math.pi # http://tauday.com/ + + +def addElementToListDictionary(element, key, listDictionary): + 'Add an element to the list table.' + if key in listDictionary: + listDictionary[key].append(element) + else: + listDictionary[key] = [element] + +def addElementToListDictionaryIfNotThere(element, key, listDictionary): + 'Add the value to the lists.' + if key in listDictionary: + elements = listDictionary[key] + if element not in elements: + elements.append(element) + else: + listDictionary[key] = [element] + +def addElementToPixelList( element, pixelDictionary, x, y ): + 'Add an element to the pixel list.' + addElementToListDictionary( element, (x, y), pixelDictionary ) + +def addElementToPixelListFromPoint( element, pixelDictionary, point ): + 'Add an element to the pixel list.' + addElementToPixelList( element, pixelDictionary, int( round( point.real ) ), int( round( point.imag ) ) ) + +def addHorizontallyBoundedPoint(begin, center, end, horizontalBegin, horizontalEnd, path): + 'Add point if it is within the horizontal bounds.' + if center.real >= horizontalEnd and center.real <= horizontalBegin: + path.append(center) + return + if end != None: + if center.real > horizontalBegin and end.real <= horizontalBegin: + centerMinusEnd = center - end + along = (center.real - horizontalBegin) / centerMinusEnd.real + path.append(center - along * centerMinusEnd) + return + if begin != None: + if center.real < horizontalEnd and begin.real >= horizontalEnd: + centerMinusBegin = center - begin + along = (center.real - horizontalEnd) / centerMinusBegin.real + path.append(center - along * centerMinusBegin) + +def addListToListTable( elementList, key, listDictionary ): + 'Add a list to the list table.' + if key in listDictionary: + listDictionary[key] += elementList + else: + listDictionary[key] = elementList + +def addLoopToPixelTable( loop, pixelDictionary, width ): + 'Add loop to the pixel table.' + for pointIndex in xrange(len(loop)): + pointBegin = loop[pointIndex] + pointEnd = loop[(pointIndex + 1) % len(loop)] + addValueSegmentToPixelTable( pointBegin, pointEnd, pixelDictionary, None, width ) + +def addNestedRingBeginning(distanceFeedRate, loop, z): + 'Add nested ring beginning to gcode output.' + distanceFeedRate.addLine('()') + distanceFeedRate.addLine('()') + for point in loop: + pointVector3 = Vector3(point.real, point.imag, z) + distanceFeedRate.addLine(distanceFeedRate.getBoundaryLine(pointVector3)) + +def addPathToPixelTable( path, pixelDictionary, value, width ): + 'Add path to the pixel table.' + for pointIndex in xrange( len(path) - 1 ): + pointBegin = path[pointIndex] + pointEnd = path[pointIndex + 1] + addValueSegmentToPixelTable( pointBegin, pointEnd, pixelDictionary, value, width ) + +def addPixelTableToPixelTable( fromPixelTable, intoPixelTable ): + 'Add from pixel table to the into pixel table.' + for fromPixelTableKey in fromPixelTable.keys(): + intoPixelTable[ fromPixelTableKey ] = fromPixelTable[ fromPixelTableKey ] + +def addPixelToPixelTableWithSteepness( isSteep, pixelDictionary, value, x, y ): + 'Add pixels to the pixel table with steepness.' + if isSteep: + pixelDictionary[(y, x)] = value + else: + pixelDictionary[(x, y)] = value + +def addPointToPath( path, pixelDictionary, point, value, width ): + 'Add a point to a path and the pixel table.' + path.append(point) + if len(path) < 2: + return + begin = path[-2] + addValueSegmentToPixelTable( begin, point, pixelDictionary, value, width ) + +def addSegmentToPixelTable( beginComplex, endComplex, pixelDictionary, shortenDistanceBegin, shortenDistanceEnd, width ): + 'Add line segment to the pixel table.' + if abs( beginComplex - endComplex ) <= 0.0: + return + beginComplex /= width + endComplex /= width + if shortenDistanceBegin > 0.0: + endMinusBeginComplex = endComplex - beginComplex + endMinusBeginComplexLength = abs( endMinusBeginComplex ) + if endMinusBeginComplexLength < shortenDistanceBegin: + return + beginComplex = beginComplex + endMinusBeginComplex * shortenDistanceBegin / endMinusBeginComplexLength + if shortenDistanceEnd > 0.0: + beginMinusEndComplex = beginComplex - endComplex + beginMinusEndComplexLength = abs( beginMinusEndComplex ) + if beginMinusEndComplexLength < 0.0: + return + endComplex = endComplex + beginMinusEndComplex * shortenDistanceEnd / beginMinusEndComplexLength + deltaX = endComplex.real - beginComplex.real + deltaY = endComplex.imag - beginComplex.imag + isSteep = abs( deltaY ) > abs( deltaX ) + if isSteep: + beginComplex = complex( beginComplex.imag, beginComplex.real ) + endComplex = complex( endComplex.imag, endComplex.real ) + if beginComplex.real > endComplex.real: + endComplex, beginComplex = beginComplex, endComplex + deltaX = endComplex.real - beginComplex.real + deltaY = endComplex.imag - beginComplex.imag + if deltaX > 0.0: + gradient = deltaY / deltaX + else: + gradient = 0.0 + print('Warning, deltaX in addSegmentToPixelTable in euclidean is 0.') + print(beginComplex) + print(endComplex) + print(shortenDistanceBegin) + print(shortenDistanceEnd) + print(width) + xBegin = int(round(beginComplex.real)) + xEnd = int(round(endComplex.real)) + yIntersection = beginComplex.imag - beginComplex.real * gradient + if isSteep: + pixelDictionary[( int( round( beginComplex.imag ) ), xBegin)] = None + pixelDictionary[( int( round( endComplex.imag ) ), xEnd )] = None + for x in xrange( xBegin + 1, xEnd ): + y = int( math.floor( yIntersection + x * gradient ) ) + pixelDictionary[(y, x)] = None + pixelDictionary[(y + 1, x)] = None + else: + pixelDictionary[(xBegin, int( round( beginComplex.imag ) ) )] = None + pixelDictionary[(xEnd, int( round( endComplex.imag ) ) )] = None + for x in xrange( xBegin + 1, xEnd ): + y = int( math.floor( yIntersection + x * gradient ) ) + pixelDictionary[(x, y)] = None + pixelDictionary[(x, y + 1)] = None + +def addSquareTwoToPixelDictionary(pixelDictionary, point, value, width): + 'Add square with two pixels around the center to pixel dictionary.' + point /= width + x = int(round(point.real)) + y = int(round(point.imag)) + for xStep in xrange(x - 2, x + 3): + for yStep in xrange(y - 2, y + 3): + pixelDictionary[(xStep, yStep)] = value + +def addToThreadsFromLoop(extrusionHalfWidth, gcodeType, loop, oldOrderedLocation, skein): + 'Add to threads from the last location from loop.' + loop = getLoopStartingClosest(extrusionHalfWidth, oldOrderedLocation.dropAxis(), loop) + oldOrderedLocation.x = loop[0].real + oldOrderedLocation.y = loop[0].imag + gcodeTypeStart = gcodeType + if isWiddershins(loop): + skein.distanceFeedRate.addLine('(<%s> outer )' % gcodeType) + else: + skein.distanceFeedRate.addLine('(<%s> inner )' % gcodeType) + skein.addGcodeFromThreadZ(loop + [loop[0]], oldOrderedLocation.z) + skein.distanceFeedRate.addLine('()' % gcodeType) + +def addToThreadsRemove(extrusionHalfWidth, nestedRings, oldOrderedLocation, skein, threadSequence): + 'Add to threads from the last location from nested rings.' + while len(nestedRings) > 0: + getTransferClosestNestedRing(extrusionHalfWidth, nestedRings, oldOrderedLocation, skein, threadSequence) + +def addValueSegmentToPixelTable( beginComplex, endComplex, pixelDictionary, value, width ): + 'Add line segment to the pixel table.' + if abs( beginComplex - endComplex ) <= 0.0: + return + beginComplex /= width + endComplex /= width + deltaX = endComplex.real - beginComplex.real + deltaY = endComplex.imag - beginComplex.imag + isSteep = abs( deltaY ) > abs( deltaX ) + if isSteep: + beginComplex = complex( beginComplex.imag, beginComplex.real ) + endComplex = complex( endComplex.imag, endComplex.real ) + if beginComplex.real > endComplex.real: + endComplex, beginComplex = beginComplex, endComplex + deltaX = endComplex.real - beginComplex.real + deltaY = endComplex.imag - beginComplex.imag + if deltaX > 0.0: + gradient = deltaY / deltaX + else: + gradient = 0.0 + print('Warning, deltaX in addValueSegmentToPixelTable in euclidean is 0.') + print(beginComplex) + print(value) + print(endComplex) + print(width) + xBegin = int(round(beginComplex.real)) + xEnd = int(round(endComplex.real)) + yIntersection = beginComplex.imag - beginComplex.real * gradient + if isSteep: + pixelDictionary[(int( round( beginComplex.imag ) ), xBegin)] = value + pixelDictionary[(int( round( endComplex.imag ) ), xEnd)] = value + for x in xrange( xBegin + 1, xEnd ): + y = int( math.floor( yIntersection + x * gradient ) ) + pixelDictionary[(y, x)] = value + pixelDictionary[(y + 1, x)] = value + else: + pixelDictionary[(xBegin, int( round( beginComplex.imag ) ))] = value + pixelDictionary[(xEnd, int( round( endComplex.imag ) ))] = value + for x in xrange( xBegin + 1, xEnd ): + y = int( math.floor( yIntersection + x * gradient ) ) + pixelDictionary[(x, y)] = value + pixelDictionary[(x, y + 1)] = value + +def addValueToOutput(depth, keyInput, output, value): + 'Add value to the output.' + depthStart = ' ' * depth + output.write('%s%s:' % (depthStart, keyInput)) + if value.__class__ == dict: + output.write('\n') + keys = value.keys() + keys.sort() + for key in keys: + addValueToOutput(depth + 1, key, output, value[key]) + return + if value.__class__ == list: + output.write('\n') + for elementIndex, element in enumerate(value): + addValueToOutput(depth + 1, elementIndex, output, element) + return + output.write(' %s\n' % value) + +def addXIntersectionIndexesFromLoopListsY( loopLists, xIntersectionIndexList, y ): + 'Add the x intersection indexes for the loop lists.' + for loopListIndex in xrange( len(loopLists) ): + loopList = loopLists[ loopListIndex ] + addXIntersectionIndexesFromLoopsY( loopList, loopListIndex, xIntersectionIndexList, y ) + +def addXIntersectionIndexesFromLoopsY( loops, solidIndex, xIntersectionIndexList, y ): + 'Add the x intersection indexes for the loops.' + for loop in loops: + addXIntersectionIndexesFromLoopY( loop, solidIndex, xIntersectionIndexList, y ) + +def addXIntersectionIndexesFromLoopY( loop, solidIndex, xIntersectionIndexList, y ): + 'Add the x intersection indexes for a loop.' + for pointIndex in xrange(len(loop)): + pointFirst = loop[pointIndex] + pointSecond = loop[(pointIndex + 1) % len(loop)] + xIntersection = getXIntersectionIfExists( pointFirst, pointSecond, y ) + if xIntersection != None: + xIntersectionIndexList.append( XIntersectionIndex( solidIndex, xIntersection ) ) + +def addXIntersectionIndexesFromSegment( index, segment, xIntersectionIndexList ): + 'Add the x intersection indexes from the segment.' + for endpoint in segment: + xIntersectionIndexList.append( XIntersectionIndex( index, endpoint.point.real ) ) + +def addXIntersectionIndexesFromSegments( index, segments, xIntersectionIndexList ): + 'Add the x intersection indexes from the segments.' + for segment in segments: + addXIntersectionIndexesFromSegment( index, segment, xIntersectionIndexList ) + +def addXIntersectionIndexesFromXIntersections( index, xIntersectionIndexList, xIntersections ): + 'Add the x intersection indexes from the XIntersections.' + for xIntersection in xIntersections: + xIntersectionIndexList.append( XIntersectionIndex( index, xIntersection ) ) + +def addXIntersections( loop, xIntersections, y ): + 'Add the x intersections for a loop.' + for pointIndex in xrange(len(loop)): + pointFirst = loop[pointIndex] + pointSecond = loop[(pointIndex + 1) % len(loop)] + xIntersection = getXIntersectionIfExists( pointFirst, pointSecond, y ) + if xIntersection != None: + xIntersections.append( xIntersection ) + +def addXIntersectionsFromLoopForTable(loop, xIntersectionsTable, width): + 'Add the x intersections for a loop into a table.' + for pointIndex in xrange(len(loop)): + pointBegin = loop[pointIndex] + pointEnd = loop[(pointIndex + 1) % len(loop)] + if pointBegin.imag > pointEnd.imag: + pointOriginal = pointBegin + pointBegin = pointEnd + pointEnd = pointOriginal + fillBegin = int( math.ceil( pointBegin.imag / width ) ) + fillEnd = int( math.ceil( pointEnd.imag / width ) ) + if fillEnd > fillBegin: + secondMinusFirstComplex = pointEnd - pointBegin + secondMinusFirstImaginaryOverReal = secondMinusFirstComplex.real / secondMinusFirstComplex.imag + beginRealMinusImaginary = pointBegin.real - pointBegin.imag * secondMinusFirstImaginaryOverReal + for fillLine in xrange( fillBegin, fillEnd ): + y = fillLine * width + xIntersection = y * secondMinusFirstImaginaryOverReal + beginRealMinusImaginary + addElementToListDictionary( xIntersection, fillLine, xIntersectionsTable ) + +def addXIntersectionsFromLoops(loops, xIntersections, y): + 'Add the x intersections for the loops.' + for loop in loops: + addXIntersections(loop, xIntersections, y) + +def addXIntersectionsFromLoopsForTable(loops, xIntersectionsTable, width): + 'Add the x intersections for a loop into a table.' + for loop in loops: + addXIntersectionsFromLoopForTable(loop, xIntersectionsTable, width) + +def compareSegmentLength( endpoint, otherEndpoint ): + 'Get comparison in order to sort endpoints in ascending order of segment length.' + if endpoint.segmentLength > otherEndpoint.segmentLength: + return 1 + if endpoint.segmentLength < otherEndpoint.segmentLength: + return - 1 + return 0 + +def concatenateRemovePath( connectedPaths, pathIndex, paths, pixelDictionary, segments, width ): + 'Get connected paths from paths.' + bottomSegment = segments[ pathIndex ] + path = paths[ pathIndex ] + if bottomSegment == None: + connectedPaths.append(path) + return + endpoints = getEndpointsFromSegments( segments[ pathIndex + 1 : ] ) + bottomSegmentEndpoint = bottomSegment[0] + nextEndpoint = bottomSegmentEndpoint.getClosestMissCheckEndpointPath( endpoints, bottomSegmentEndpoint.path, pixelDictionary, width ) + if nextEndpoint == None: + bottomSegmentEndpoint = bottomSegment[1] + nextEndpoint = bottomSegmentEndpoint.getClosestMissCheckEndpointPath( endpoints, bottomSegmentEndpoint.path, pixelDictionary, width ) + if nextEndpoint == None: + connectedPaths.append(path) + return + if len( bottomSegmentEndpoint.path ) > 0 and len( nextEndpoint.path ) > 0: + bottomEnd = bottomSegmentEndpoint.path[-1] + nextBegin = nextEndpoint.path[-1] + nextMinusBottomNormalized = getNormalized( nextBegin - bottomEnd ) + if len( bottomSegmentEndpoint.path ) > 1: + bottomPenultimate = bottomSegmentEndpoint.path[-2] + if getDotProduct( getNormalized( bottomPenultimate - bottomEnd ), nextMinusBottomNormalized ) > 0.9: + connectedPaths.append(path) + return + if len( nextEndpoint.path ) > 1: + nextPenultimate = nextEndpoint.path[-2] + if getDotProduct( getNormalized( nextPenultimate - nextBegin ), - nextMinusBottomNormalized ) > 0.9: + connectedPaths.append(path) + return + nextEndpoint.path.reverse() + concatenatedPath = bottomSegmentEndpoint.path + nextEndpoint.path + paths[ nextEndpoint.pathIndex ] = concatenatedPath + segments[ nextEndpoint.pathIndex ] = getSegmentFromPath( concatenatedPath, nextEndpoint.pathIndex ) + addValueSegmentToPixelTable( bottomSegmentEndpoint.point, nextEndpoint.point, pixelDictionary, None, width ) + +def getAngleAroundZAxisDifference( subtractFromVec3, subtractVec3 ): + 'Get the angle around the Z axis difference between a pair of Vector3s.' + subtractVectorMirror = complex( subtractVec3.x , - subtractVec3.y ) + differenceVector = getRoundZAxisByPlaneAngle( subtractVectorMirror, subtractFromVec3 ) + return math.atan2( differenceVector.y, differenceVector.x ) + +def getAngleDifferenceByComplex( subtractFromComplex, subtractComplex ): + 'Get the angle between a pair of normalized complexes.' + subtractComplexMirror = complex( subtractComplex.real , - subtractComplex.imag ) + differenceComplex = subtractComplexMirror * subtractFromComplex + return math.atan2( differenceComplex.imag, differenceComplex.real ) + +def getAreaLoop(loop): + 'Get the area of a complex polygon.' + areaLoopDouble = 0.0 + for pointIndex, point in enumerate(loop): + pointEnd = loop[(pointIndex + 1) % len(loop)] + areaLoopDouble += point.real * pointEnd.imag - pointEnd.real * point.imag + return 0.5 * areaLoopDouble + +def getAreaLoopAbsolute(loop): + 'Get the absolute area of a complex polygon.' + return abs(getAreaLoop(loop)) + +def getAreaLoops(loops): + 'Get the area of a list of complex polygons.' + areaLoops = 0.0 + for loop in loops: + areaLoops += getAreaLoop(loop) + return areaLoops + +def getAreaVector3LoopAbsolute(loop): + 'Get the absolute area of a vector3 polygon.' + return getAreaLoopAbsolute(getComplexPath(loop)) + +def getAroundLoop(begin, end, loop): + 'Get an arc around a loop.' + aroundLoop = [] + if end <= begin: + end += len(loop) + for pointIndex in xrange(begin, end): + aroundLoop.append(loop[pointIndex % len(loop)]) + return aroundLoop + +def getAwayPath(path, radius): + 'Get a path with only the points that are far enough away from each other, except for the last point.' + if len(path) < 2: + return path + lastPoint = path[-1] + awayPath = getAwayPoints(path, radius) + if len(awayPath) == 0: + return [lastPoint] + if abs(lastPoint - awayPath[-1]) > 0.001 * radius: + awayPath.append(lastPoint) + return awayPath + +def getAwayPoints(points, radius): + 'Get a path with only the points that are far enough away from each other.' + awayPoints = [] + oneOverOverlapDistance = 1000.0 / radius + pixelDictionary = {} + for point in points: + x = int(point.real * oneOverOverlapDistance) + y = int(point.imag * oneOverOverlapDistance) + if not getSquareIsOccupied(pixelDictionary, x, y): + awayPoints.append(point) + pixelDictionary[(x, y)] = None + return awayPoints + +def getBooleanFromDictionary(defaultBoolean, dictionary, key): + 'Get boolean from the dictionary and key.' + if key not in dictionary: + return defaultBoolean + return getBooleanFromValue(dictionary[key]) + +def getBooleanFromValue(value): + 'Get boolean from the word.' + firstCharacter = str(value).lower().lstrip()[: 1] + return firstCharacter == 't' or firstCharacter == '1' + +def getBottomByPath(path): + 'Get the bottom of the path.' + bottom = 987654321987654321.0 + for point in path: + bottom = min(bottom, point.z) + return bottom + +def getBottomByPaths(paths): + 'Get the bottom of the paths.' + bottom = 987654321987654321.0 + for path in paths: + for point in path: + bottom = min(bottom, point.z) + return bottom + +def getClippedAtEndLoopPath( clip, loopPath ): + 'Get a clipped loop path.' + if clip <= 0.0: + return loopPath + loopPathLength = getPathLength(loopPath) + clip = min( clip, 0.3 * loopPathLength ) + lastLength = 0.0 + pointIndex = 0 + totalLength = 0.0 + clippedLength = loopPathLength - clip + while totalLength < clippedLength and pointIndex < len(loopPath) - 1: + firstPoint = loopPath[pointIndex] + secondPoint = loopPath[pointIndex + 1] + pointIndex += 1 + lastLength = totalLength + totalLength += abs(firstPoint - secondPoint) + remainingLength = clippedLength - lastLength + clippedLoopPath = loopPath[ : pointIndex ] + ultimateClippedPoint = loopPath[pointIndex] + penultimateClippedPoint = clippedLoopPath[-1] + segment = ultimateClippedPoint - penultimateClippedPoint + segmentLength = abs(segment) + if segmentLength <= 0.0: + return clippedLoopPath + newUltimatePoint = penultimateClippedPoint + segment * remainingLength / segmentLength + return clippedLoopPath + [newUltimatePoint] + +def getClippedLoopPath(clip, loopPath): + 'Get a clipped loop path.' + if clip <= 0.0: + return loopPath + loopPathLength = getPathLength(loopPath) + clip = min(clip, 0.3 * loopPathLength) + lastLength = 0.0 + pointIndex = 0 + totalLength = 0.0 + while totalLength < clip and pointIndex < len(loopPath) - 1: + firstPoint = loopPath[pointIndex] + secondPoint = loopPath[pointIndex + 1] + pointIndex += 1 + lastLength = totalLength + totalLength += abs(firstPoint - secondPoint) + remainingLength = clip - lastLength + clippedLoopPath = loopPath[pointIndex :] + ultimateClippedPoint = clippedLoopPath[0] + penultimateClippedPoint = loopPath[pointIndex - 1] + segment = ultimateClippedPoint - penultimateClippedPoint + segmentLength = abs(segment) + loopPath = clippedLoopPath + if segmentLength > 0.0: + newUltimatePoint = penultimateClippedPoint + segment * remainingLength / segmentLength + loopPath = [newUltimatePoint] + loopPath + return getClippedAtEndLoopPath(clip, loopPath) + +def getClippedSimplifiedLoopPath(clip, loopPath, radius): + 'Get a clipped and simplified loop path.' + return getSimplifiedPath(getClippedLoopPath(clip, loopPath), radius) + +def getClosestDistanceIndexToLine(point, loop): + 'Get the distance squared to the closest segment of the loop and index of that segment.' + smallestDistance = 987654321987654321.0 + closestDistanceIndex = None + for pointIndex in xrange(len(loop)): + segmentBegin = loop[pointIndex] + segmentEnd = loop[(pointIndex + 1) % len(loop)] + distance = getDistanceToPlaneSegment(segmentBegin, segmentEnd, point) + if distance < smallestDistance: + smallestDistance = distance + closestDistanceIndex = DistanceIndex(distance, pointIndex) + return closestDistanceIndex + +def getClosestPointOnSegment(segmentBegin, segmentEnd, point): + 'Get the closest point on the segment.' + segmentDifference = segmentEnd - segmentBegin + if abs(segmentDifference) <= 0.0: + return segmentBegin + pointMinusSegmentBegin = point - segmentBegin + beginPlaneDot = getDotProduct(pointMinusSegmentBegin, segmentDifference) + differencePlaneDot = getDotProduct(segmentDifference, segmentDifference) + intercept = beginPlaneDot / differencePlaneDot + intercept = max(intercept, 0.0) + intercept = min(intercept, 1.0) + return segmentBegin + segmentDifference * intercept + +def getComplexByCommaString( valueCommaString ): + 'Get the commaString as a complex.' + try: + splitLine = valueCommaString.replace(',', ' ').split() + return complex( float( splitLine[0] ), float(splitLine[1]) ) + except: + pass + return None + +def getComplexByWords(words, wordIndex=0): + 'Get the complex by the first two words.' + try: + return complex(float(words[wordIndex]), float(words[wordIndex + 1])) + except: + pass + return None + +def getComplexDefaultByDictionary( defaultComplex, dictionary, key ): + 'Get the value as a complex.' + if key in dictionary: + return complex( dictionary[key].strip().replace('(', '').replace(')', '') ) + return defaultComplex + +def getComplexDefaultByDictionaryKeys( defaultComplex, dictionary, keyX, keyY ): + 'Get the value as a complex.' + x = getFloatDefaultByDictionary( defaultComplex.real, dictionary, keyX ) + y = getFloatDefaultByDictionary( defaultComplex.real, dictionary, keyY ) + return complex(x, y) + +def getComplexPath(vector3Path): + 'Get the complex path from the vector3 path.' + complexPath = [] + for point in vector3Path: + complexPath.append(point.dropAxis()) + return complexPath + +def getComplexPathByMultiplier(multiplier, path): + 'Get the multiplied complex path.' + complexPath = [] + for point in path: + complexPath.append(multiplier * point) + return complexPath + +def getComplexPaths(vector3Paths): + 'Get the complex paths from the vector3 paths.' + complexPaths = [] + for vector3Path in vector3Paths: + complexPaths.append(getComplexPath(vector3Path)) + return complexPaths + +def getComplexPolygon(center, radius, sides, startAngle=0.0): + 'Get the complex polygon.' + complexPolygon = [] + sideAngle = 2.0 * math.pi / float(sides) + for side in xrange(abs(sides)): + unitPolar = getWiddershinsUnitPolar(startAngle) + complexPolygon.append(unitPolar * radius + center) + startAngle += sideAngle + return complexPolygon + +def getComplexPolygonByComplexRadius(radius, sides, startAngle=0.0): + 'Get the complex polygon.' + complexPolygon = [] + sideAngle = 2.0 * math.pi / float(sides) + for side in xrange(abs(sides)): + unitPolar = getWiddershinsUnitPolar(startAngle) + complexPolygon.append(complex(unitPolar.real * radius.real, unitPolar.imag * radius.imag)) + startAngle += sideAngle + return complexPolygon + +def getComplexPolygonByStartEnd(endAngle, radius, sides, startAngle=0.0): + 'Get the complex polygon by start and end angle.' + angleExtent = endAngle - startAngle + sideAngle = 2.0 * math.pi / float(sides) + sides = int(math.ceil(abs(angleExtent / sideAngle))) + sideAngle = angleExtent / float(sides) + complexPolygon = [] + for side in xrange(abs(sides) + 1): + unitPolar = getWiddershinsUnitPolar(startAngle) + complexPolygon.append(unitPolar * radius) + startAngle += sideAngle + return getLoopWithoutCloseEnds(0.000001 * radius, complexPolygon) + +def getConcatenatedList(originalLists): + 'Get the lists as one concatenated list.' + concatenatedList = [] + for originalList in originalLists: + concatenatedList += originalList + return concatenatedList + +def getConnectedPaths( paths, pixelDictionary, width ): + 'Get connected paths from paths.' + if len(paths) < 2: + return paths + connectedPaths = [] + segments = [] + for pathIndex in xrange( len(paths) ): + path = paths[ pathIndex ] + segments.append( getSegmentFromPath( path, pathIndex ) ) + for pathIndex in xrange( 0, len(paths) - 1 ): + concatenateRemovePath( connectedPaths, pathIndex, paths, pixelDictionary, segments, width ) + connectedPaths.append( paths[-1] ) + return connectedPaths + +def getCrossProduct(firstComplex, secondComplex): + 'Get z component cross product of a pair of complexes.' + return firstComplex.real * secondComplex.imag - firstComplex.imag * secondComplex.real + +def getDecimalPlacesCarried(extraDecimalPlaces, value): + 'Get decimal places carried by the decimal places of the value plus the extraDecimalPlaces.' + return max(0, 1 + int(math.ceil(extraDecimalPlaces - math.log10(value)))) + +def getDiagonalFlippedLoop(loop): + 'Get loop flipped over the dialogonal, in other words with the x and y swapped.' + diagonalFlippedLoop = [] + for point in loop: + diagonalFlippedLoop.append( complex( point.imag, point.real ) ) + return diagonalFlippedLoop + +def getDiagonalFlippedLoops(loops): + 'Get loops flipped over the dialogonal, in other words with the x and y swapped.' + diagonalFlippedLoops = [] + for loop in loops: + diagonalFlippedLoops.append( getDiagonalFlippedLoop(loop) ) + return diagonalFlippedLoops + +def getDictionaryString(dictionary): + 'Get the dictionary string.' + output = cStringIO.StringIO() + keys = dictionary.keys() + keys.sort() + for key in keys: + addValueToOutput(0, key, output, dictionary[key]) + return output.getvalue() + +def getDistanceToLine(begin, end, point): + 'Get the distance from a vector3 point to an infinite line.' + pointMinusBegin = point - begin + if begin == end: + return abs(pointMinusBegin) + endMinusBegin = end - begin + return abs(endMinusBegin.cross(pointMinusBegin)) / abs(endMinusBegin) + +def getDistanceToLineByPath(begin, end, path): + 'Get the maximum distance from a path to an infinite line.' + distanceToLine = -987654321.0 + for point in path: + distanceToLine = max(getDistanceToLine(begin, end, point), distanceToLine) + return distanceToLine + +def getDistanceToLineByPaths(begin, end, paths): + 'Get the maximum distance from paths to an infinite line.' + distanceToLine = -987654321.0 + for path in paths: + distanceToLine = max(getDistanceToLineByPath(begin, end, path), distanceToLine) + return distanceToLine + +def getDistanceToPlaneSegment( segmentBegin, segmentEnd, point ): + 'Get the distance squared from a point to the x & y components of a segment.' + segmentDifference = segmentEnd - segmentBegin + pointMinusSegmentBegin = point - segmentBegin + beginPlaneDot = getDotProduct( pointMinusSegmentBegin, segmentDifference ) + if beginPlaneDot <= 0.0: + return abs( point - segmentBegin ) * abs( point - segmentBegin ) + differencePlaneDot = getDotProduct( segmentDifference, segmentDifference ) + if differencePlaneDot <= beginPlaneDot: + return abs( point - segmentEnd ) * abs( point - segmentEnd ) + intercept = beginPlaneDot / differencePlaneDot + interceptPerpendicular = segmentBegin + segmentDifference * intercept + return abs( point - interceptPerpendicular ) * abs( point - interceptPerpendicular ) + +def getDotProduct(firstComplex, secondComplex): + 'Get the dot product of a pair of complexes.' + return firstComplex.real * secondComplex.real + firstComplex.imag * secondComplex.imag + +def getDotProductPlusOne( firstComplex, secondComplex ): + 'Get the dot product plus one of the x and y components of a pair of Vector3s.' + return 1.0 + getDotProduct( firstComplex, secondComplex ) + +def getDurationString( seconds ): + 'Get the duration string.' + secondsRounded = int( round( seconds ) ) + durationString = getPluralString( secondsRounded % 60, 'second') + if seconds < 60: + return durationString + durationString = '%s %s' % ( getPluralString( ( secondsRounded / 60 ) % 60, 'minute'), durationString ) + if seconds < 3600: + return durationString + return '%s %s' % ( getPluralString( secondsRounded / 3600, 'hour'), durationString ) + +def getEndpointFromPath( path, pathIndex ): + 'Get endpoint segment from a path.' + begin = path[-1] + end = path[-2] + endpointBegin = Endpoint() + endpointEnd = Endpoint().getFromOtherPoint( endpointBegin, end ) + endpointBegin.getFromOtherPoint( endpointEnd, begin ) + endpointBegin.path = path + endpointBegin.pathIndex = pathIndex + return endpointBegin + +def getEndpointsFromSegments( segments ): + 'Get endpoints from segments.' + endpoints = [] + for segment in segments: + for endpoint in segment: + endpoints.append( endpoint ) + return endpoints + +def getEndpointsFromSegmentTable( segmentTable ): + 'Get the endpoints from the segment table.' + endpoints = [] + segmentTableKeys = segmentTable.keys() + segmentTableKeys.sort() + for segmentTableKey in segmentTableKeys: + for segment in segmentTable[ segmentTableKey ]: + for endpoint in segment: + endpoints.append( endpoint ) + return endpoints + +def getEnumeratorKeys(enumerator, keys): + 'Get enumerator keys.' + if len(keys) == 1: + return keys[0] + return getEnumeratorKeysExceptForOneArgument(enumerator, keys) + +def getEnumeratorKeysAlwaysList(enumerator, keys): + 'Get enumerator keys.' + if keys.__class__ != list: + return [keys] + if len(keys) == 1: + return keys + return getEnumeratorKeysExceptForOneArgument(enumerator, keys) + +def getEnumeratorKeysExceptForOneArgument(enumerator, keys): + 'Get enumerator keys, except when there is one argument.' + if len(keys) == 0: + return range(0, len(enumerator)) + beginIndex = keys[0] + endIndex = keys[1] + if len(keys) == 2: + if beginIndex == None: + beginIndex = 0 + if endIndex == None: + endIndex = len(enumerator) + return range(beginIndex, endIndex) + step = keys[2] + beginIndexDefault = 0 + endIndexDefault = len(enumerator) + if step < 0: + beginIndexDefault = endIndexDefault - 1 + endIndexDefault = -1 + if beginIndex == None: + beginIndex = beginIndexDefault + if endIndex == None: + endIndex = endIndexDefault + return range(beginIndex, endIndex, step) + +def getFillOfSurroundings(nestedRings, penultimateFillLoops): + 'Get extra fill loops of nested rings.' + fillOfSurroundings = [] + for nestedRing in nestedRings: + fillOfSurroundings += nestedRing.getFillLoops(penultimateFillLoops) + return fillOfSurroundings + +def getFlattenedNestedRings(nestedRings): + 'Get flattened nested rings.' + flattenedNestedRings = [] + for nestedRing in nestedRings: + nestedRing.addFlattenedNestedRings(flattenedNestedRings) + return flattenedNestedRings + +def getFloatDefaultByDictionary( defaultFloat, dictionary, key ): + 'Get the value as a float.' + evaluatedFloat = None + if key in dictionary: + evaluatedFloat = getFloatFromValue(dictionary[key]) + if evaluatedFloat == None: + return defaultFloat + return evaluatedFloat + +def getFloatFromValue(value): + 'Get the value as a float.' + try: + return float(value) + except: + pass + return None + +def getFourSignificantFigures(number): + 'Get number rounded to four significant figures as a string.' + if number == None: + return None + absoluteNumber = abs(number) + if absoluteNumber >= 100.0: + return getRoundedToPlacesString( 2, number ) + if absoluteNumber < 0.000000001: + return getRoundedToPlacesString( 13, number ) + return getRoundedToPlacesString( 3 - math.floor( math.log10( absoluteNumber ) ), number ) + +def getHalfSimplifiedLoop( loop, radius, remainder ): + 'Get the loop with half of the points inside the channel removed.' + if len(loop) < 2: + return loop + channelRadius = radius * .01 + simplified = [] + addIndex = 0 + if remainder == 1: + addIndex = len(loop) - 1 + for pointIndex in xrange(len(loop)): + point = loop[pointIndex] + if pointIndex % 2 == remainder or pointIndex == addIndex: + simplified.append(point) + elif not isWithinChannel( channelRadius, pointIndex, loop ): + simplified.append(point) + return simplified + +def getHalfSimplifiedPath(path, radius, remainder): + 'Get the path with half of the points inside the channel removed.' + if len(path) < 2: + return path + channelRadius = radius * .01 + simplified = [path[0]] + for pointIndex in xrange(1, len(path) - 1): + point = path[pointIndex] + if pointIndex % 2 == remainder: + simplified.append(point) + elif not isWithinChannel(channelRadius, pointIndex, path): + simplified.append(point) + simplified.append(path[-1]) + return simplified + +def getHorizontallyBoundedPath(horizontalBegin, horizontalEnd, path): + 'Get horizontally bounded path.' + horizontallyBoundedPath = [] + for pointIndex, point in enumerate(path): + begin = None + previousIndex = pointIndex - 1 + if previousIndex >= 0: + begin = path[previousIndex] + end = None + nextIndex = pointIndex + 1 + if nextIndex < len(path): + end = path[nextIndex] + addHorizontallyBoundedPoint(begin, point, end, horizontalBegin, horizontalEnd, horizontallyBoundedPath) + return horizontallyBoundedPath + +def getIncrementFromRank( rank ): + 'Get the increment from the rank which is 0 at 1 and increases by three every power of ten.' + rankZone = int( math.floor( rank / 3 ) ) + rankModulo = rank % 3 + powerOfTen = pow( 10, rankZone ) + moduloMultipliers = ( 1, 2, 5 ) + return float( powerOfTen * moduloMultipliers[ rankModulo ] ) + +def getInsidesAddToOutsides( loops, outsides ): + 'Add loops to either the insides or outsides.' + insides = [] + for loopIndex in xrange( len(loops) ): + loop = loops[loopIndex] + if isInsideOtherLoops( loopIndex, loops ): + insides.append(loop) + else: + outsides.append(loop) + return insides + +def getIntermediateLocation( alongWay, begin, end ): + 'Get the intermediate location between begin and end.' + return begin * ( 1.0 - alongWay ) + end * alongWay + +def getIntersectionOfXIntersectionIndexes( totalSolidSurfaceThickness, xIntersectionIndexList ): + 'Get x intersections from surrounding layers.' + xIntersectionList = [] + solidTable = {} + solid = False + xIntersectionIndexList.sort() + for xIntersectionIndex in xIntersectionIndexList: + toggleHashtable(solidTable, xIntersectionIndex.index, '') + oldSolid = solid + solid = len(solidTable) >= totalSolidSurfaceThickness + if oldSolid != solid: + xIntersectionList.append(xIntersectionIndex.x) + return xIntersectionList + +def getIntersectionOfXIntersectionsTables(xIntersectionsTables): + 'Get the intersection of the XIntersections tables.' + if len(xIntersectionsTables) == 0: + return {} + intersectionOfXIntersectionsTables = {} + firstIntersectionTable = xIntersectionsTables[0] + for firstIntersectionTableKey in firstIntersectionTable.keys(): + xIntersectionIndexList = [] + for xIntersectionsTableIndex in xrange(len(xIntersectionsTables)): + xIntersectionsTable = xIntersectionsTables[xIntersectionsTableIndex] + if firstIntersectionTableKey in xIntersectionsTable: + addXIntersectionIndexesFromXIntersections(xIntersectionsTableIndex, xIntersectionIndexList, xIntersectionsTable[firstIntersectionTableKey]) + xIntersections = getIntersectionOfXIntersectionIndexes(len(xIntersectionsTables), xIntersectionIndexList) + if len(xIntersections) > 0: + intersectionOfXIntersectionsTables[firstIntersectionTableKey] = xIntersections + return intersectionOfXIntersectionsTables + +def getIntFromValue(value): + 'Get the value as an int.' + try: + return int(value) + except: + pass + return None + +def getIsInFilledRegion(loops, point): + 'Determine if the point is in the filled region of the loops.' + return getNumberOfIntersectionsToLeftOfLoops(loops, point) % 2 == 1 + +def getIsInFilledRegionByPaths(loops, paths): + 'Determine if the point of any path is in the filled region of the loops.' + for path in paths: + if len(path) > 0: + if getIsInFilledRegion(loops, path[0]): + return True + return False + +def getIsRadianClose(firstRadian, secondRadian): + 'Determine if the firstRadian is close to the secondRadian.' + return abs(math.pi - abs(math.pi - ((firstRadian - secondRadian) % (math.pi + math.pi) ))) < 0.000001 + +def getIsWiddershinsByVector3( polygon ): + 'Determine if the polygon goes round in the widdershins direction.' + return isWiddershins( getComplexPath( polygon ) ) + +def getJoinOfXIntersectionIndexes( xIntersectionIndexList ): + 'Get joined x intersections from surrounding layers.' + xIntersections = [] + solidTable = {} + solid = False + xIntersectionIndexList.sort() + for xIntersectionIndex in xIntersectionIndexList: + toggleHashtable(solidTable, xIntersectionIndex.index, '') + oldSolid = solid + solid = len(solidTable) > 0 + if oldSolid != solid: + xIntersections.append(xIntersectionIndex.x) + return xIntersections + +def getLargestLoop(loops): + 'Get largest loop from loops.' + largestArea = -987654321.0 + largestLoop = [] + for loop in loops: + loopArea = abs(getAreaLoopAbsolute(loop)) + if loopArea > largestArea: + largestArea = loopArea + largestLoop = loop + return largestLoop + +def getLeftPoint(points): + 'Get the leftmost complex point in the points.' + leftmost = 987654321.0 + leftPointComplex = None + for pointComplex in points: + if pointComplex.real < leftmost: + leftmost = pointComplex.real + leftPointComplex = pointComplex + return leftPointComplex + +def getLeftPointIndex(points): + 'Get the index of the leftmost complex point in the points.' + if len(points) < 1: + return None + leftPointIndex = 0 + for pointIndex in xrange( len(points) ): + if points[pointIndex].real < points[ leftPointIndex ].real: + leftPointIndex = pointIndex + return leftPointIndex + +def getListTableElements( listDictionary ): + 'Get all the element in a list table.' + listDictionaryElements = [] + for listDictionaryValue in listDictionary.values(): + listDictionaryElements += listDictionaryValue + return listDictionaryElements + +def getLoopCentroid(polygonComplex): + 'Get the area of a complex polygon using http://en.wikipedia.org/wiki/Centroid.' + polygonDoubleArea = 0.0 + polygonTorque = 0.0 + for pointIndex in xrange( len(polygonComplex) ): + pointBegin = polygonComplex[pointIndex] + pointEnd = polygonComplex[ (pointIndex + 1) % len(polygonComplex) ] + doubleArea = pointBegin.real * pointEnd.imag - pointEnd.real * pointBegin.imag + doubleCenter = complex( pointBegin.real + pointEnd.real, pointBegin.imag + pointEnd.imag ) + polygonDoubleArea += doubleArea + polygonTorque += doubleArea * doubleCenter + torqueMultiplier = 0.333333333333333333333333 / polygonDoubleArea + return polygonTorque * torqueMultiplier + +def getLoopConvex(points): + 'Get convex hull of points using gift wrap algorithm.' + loopConvex = [] + pointSet = set() + for point in points: + if point not in pointSet: + pointSet.add(point) + loopConvex.append(point) + if len(loopConvex) < 4: + return loopConvex + leftPoint = getLeftPoint(loopConvex) + lastPoint = leftPoint + pointSet.remove(leftPoint) + loopConvex = [leftPoint] + lastSegment = complex(0.0, 1.0) + while True: + greatestDotProduct = -9.9 + greatestPoint = None + greatestSegment = None + if len(loopConvex) > 2: + nextSegment = getNormalized(leftPoint - lastPoint) + if abs(nextSegment) > 0.0: + greatestDotProduct = getDotProduct(nextSegment, lastSegment) + for point in pointSet: + nextSegment = getNormalized(point - lastPoint) + if abs(nextSegment) > 0.0: + dotProduct = getDotProduct(nextSegment, lastSegment) + if dotProduct > greatestDotProduct: + greatestDotProduct = dotProduct + greatestPoint = point + greatestSegment = nextSegment + if greatestPoint == None: + return loopConvex + lastPoint = greatestPoint + loopConvex.append(greatestPoint) + pointSet.remove(greatestPoint) + lastSegment = greatestSegment + return loopConvex + +def getLoopConvexCentroid(polygonComplex): + 'Get centroid of the convex hull of a complex polygon.' + return getLoopCentroid( getLoopConvex(polygonComplex) ) + +def getLoopInsideContainingLoop( containingLoop, loops ): + 'Get a loop that is inside the containing loop.' + for loop in loops: + if loop != containingLoop: + if isPathInsideLoop( containingLoop, loop ): + return loop + return None + +def getLoopLength( polygon ): + 'Get the length of a polygon perimeter.' + polygonLength = 0.0 + for pointIndex in xrange( len( polygon ) ): + point = polygon[pointIndex] + secondPoint = polygon[ (pointIndex + 1) % len( polygon ) ] + polygonLength += abs( point - secondPoint ) + return polygonLength + +def getLoopStartingClosest(extrusionHalfWidth, location, loop): + 'Add to threads from the last location from loop.' + closestIndex = getClosestDistanceIndexToLine(location, loop).index + loop = getAroundLoop(closestIndex, closestIndex, loop) + closestPoint = getClosestPointOnSegment(loop[0], loop[1], location) + if abs(closestPoint - loop[0]) > extrusionHalfWidth and abs(closestPoint - loop[1]) > extrusionHalfWidth: + loop = [closestPoint] + loop[1 :] + [loop[0]] + elif abs(closestPoint - loop[0]) > abs(closestPoint - loop[1]): + loop = loop[1 :] + [loop[0]] + return loop + +def getLoopWithoutCloseEnds(close, loop): + 'Get loop without close ends.' + if len(loop) < 2: + return loop + if abs(loop[0] - loop[-1]) > close: + return loop + return loop[: -1] + +def getLoopWithoutCloseSequentialPoints(close, loop): + 'Get loop without close sequential points.' + if len(loop) < 2: + return loop + lastPoint = loop[-1] + loopWithoutCloseSequentialPoints = [] + for point in loop: + if abs(point - lastPoint) > close: + loopWithoutCloseSequentialPoints.append(point) + lastPoint = point + return loopWithoutCloseSequentialPoints + +def getMaximum(firstComplex, secondComplex): + 'Get a complex with each component the maximum of the respective components of a pair of complexes.' + return complex(max(firstComplex.real, secondComplex.real), max(firstComplex.imag, secondComplex.imag)) + +def getMaximumByComplexPath(path): + 'Get a complex with each component the maximum of the respective components of a complex path.' + maximum = complex(-987654321987654321.0, -987654321987654321.0) + for point in path: + maximum = getMaximum(maximum, point) + return maximum + +def getMaximumByComplexPaths(paths): + 'Get a complex with each component the maximum of the respective components of complex paths.' + maximum = complex(-987654321987654321.0, -987654321987654321.0) + for path in paths: + for point in path: + maximum = getMaximum(maximum, point) + return maximum + +def getMaximumByVector3Path(path): + 'Get a vector3 with each component the maximum of the respective components of a vector3 path.' + maximum = Vector3(-987654321987654321.0, -987654321987654321.0, -987654321987654321.0) + for point in path: + maximum.maximize(point) + return maximum + +def getMaximumByVector3Paths(paths): + 'Get a complex with each component the maximum of the respective components of a complex path.' + maximum = Vector3(-987654321987654321.0, -987654231987654321.0, -987654321987654321.0) + for path in paths: + for point in path: + maximum.maximize(point) + return maximum + +def getMaximumSpan(loop): + 'Get the maximum span of the loop.' + extent = getMaximumByComplexPath(loop) - getMinimumByComplexPath(loop) + return max(extent.real, extent.imag) + +def getMinimum(firstComplex, secondComplex): + 'Get a complex with each component the minimum of the respective components of a pair of complexes.' + return complex(min(firstComplex.real, secondComplex.real), min(firstComplex.imag, secondComplex.imag)) + +def getMinimumByComplexPath(path): + 'Get a complex with each component the minimum of the respective components of a complex path.' + minimum = complex(987654321987654321.0, 987654321987654321.0) + for point in path: + minimum = getMinimum(minimum, point) + return minimum + +def getMinimumByComplexPaths(paths): + 'Get a complex with each component the minimum of the respective components of complex paths.' + minimum = complex(987654321987654321.0, 987654321987654321.0) + for path in paths: + for point in path: + minimum = getMinimum(minimum, point) + return minimum + +def getMinimumByVector3Path(path): + 'Get a vector3 with each component the minimum of the respective components of a vector3 path.' + minimum = Vector3(987654321987654321.0, 987654321987654321.0, 987654321987654321.0) + for point in path: + minimum.minimize(point) + return minimum + +def getMinimumByVector3Paths(paths): + 'Get a complex with each component the minimum of the respective components of a complex path.' + minimum = Vector3(987654321987654321.0, 987654321987654321.0, 987654321987654321.0) + for path in paths: + for point in path: + minimum.minimize(point) + return minimum + +def getMirrorPath(path): + "Get mirror path." + close = 0.001 * getPathLength(path) + for pointIndex in xrange(len(path) - 1, -1, -1): + point = path[pointIndex] + flipPoint = complex(-point.real, point.imag) + if abs(flipPoint - path[-1]) > close: + path.append(flipPoint) + return path + +def getNormal(begin, center, end): + 'Get normal.' + centerMinusBegin = (center - begin).getNormalized() + endMinusCenter = (end - center).getNormalized() + return centerMinusBegin.cross(endMinusCenter) + +def getNormalByPath(path): + 'Get normal by path.' + totalNormal = Vector3() + for pointIndex, point in enumerate(path): + center = path[(pointIndex + 1) % len(path)] + end = path[(pointIndex + 2) % len(path)] + totalNormal += getNormalWeighted(point, center, end) + return totalNormal.getNormalized() + +def getNormalized(complexNumber): + 'Get the normalized complex.' + complexNumberLength = abs(complexNumber) + if complexNumberLength > 0.0: + return complexNumber / complexNumberLength + return complexNumber + +def getNormalWeighted(begin, center, end): + 'Get weighted normal.' + return (center - begin).cross(end - center) + +def getNumberOfIntersectionsToLeft(loop, point): + 'Get the number of intersections through the loop for the line going left.' + numberOfIntersectionsToLeft = 0 + for pointIndex in xrange(len(loop)): + firstPointComplex = loop[pointIndex] + secondPointComplex = loop[(pointIndex + 1) % len(loop)] + xIntersection = getXIntersectionIfExists(firstPointComplex, secondPointComplex, point.imag) + if xIntersection != None: + if xIntersection < point.real: + numberOfIntersectionsToLeft += 1 + return numberOfIntersectionsToLeft + +def getNumberOfIntersectionsToLeftOfLoops(loops, point): + 'Get the number of intersections through the loop for the line starting from the left point and going left.' + totalNumberOfIntersectionsToLeft = 0 + for loop in loops: + totalNumberOfIntersectionsToLeft += getNumberOfIntersectionsToLeft(loop, point) + return totalNumberOfIntersectionsToLeft + +def getOrderedNestedRings(nestedRings): + 'Get ordered nestedRings from nestedRings.' + insides = [] + orderedNestedRings = [] + for loopIndex in xrange(len(nestedRings)): + nestedRing = nestedRings[loopIndex] + otherLoops = [] + for beforeIndex in xrange(loopIndex): + otherLoops.append(nestedRings[beforeIndex].boundary) + for afterIndex in xrange(loopIndex + 1, len(nestedRings)): + otherLoops.append(nestedRings[afterIndex].boundary) + if isPathEntirelyInsideLoops(otherLoops, nestedRing.boundary): + insides.append(nestedRing) + else: + orderedNestedRings.append(nestedRing) + for outside in orderedNestedRings: + outside.getFromInsideSurroundings(insides) + return orderedNestedRings + +def getPathCopy(path): + 'Get path copy.' + pathCopy = [] + for point in path: + pathCopy.append(point.copy()) + return pathCopy + +def getPathLength(path): + 'Get the length of a path ( an open polyline ).' + pathLength = 0.0 + for pointIndex in xrange( len(path) - 1 ): + firstPoint = path[pointIndex] + secondPoint = path[pointIndex + 1] + pathLength += abs(firstPoint - secondPoint) + return pathLength + +def getPathsFromEndpoints(endpoints, maximumConnectionLength, pixelDictionary, width): + 'Get paths from endpoints.' + if len(endpoints) < 2: + return [] + endpoints = endpoints[:] # so that the first two endpoints aren't removed when used again + for beginningEndpoint in endpoints[: : 2]: + beginningPoint = beginningEndpoint.point + addSegmentToPixelTable(beginningPoint, beginningEndpoint.otherEndpoint.point, pixelDictionary, 0, 0, width) + endpointFirst = endpoints[0] + endpoints.remove(endpointFirst) + otherEndpoint = endpointFirst.otherEndpoint + endpoints.remove(otherEndpoint) + nextEndpoint = None + path = [] + paths = [path] + if len(endpoints) > 1: + nextEndpoint = otherEndpoint.getClosestMiss(endpoints, path, pixelDictionary, width) + if nextEndpoint != None: + if abs(nextEndpoint.point - endpointFirst.point) < abs(nextEndpoint.point - otherEndpoint.point): + endpointFirst = endpointFirst.otherEndpoint + otherEndpoint = endpointFirst.otherEndpoint + addPointToPath(path, pixelDictionary, endpointFirst.point, None, width) + addPointToPath(path, pixelDictionary, otherEndpoint.point, len(paths) - 1, width) + oneOverEndpointWidth = 1.0 / maximumConnectionLength + endpointTable = {} + for endpoint in endpoints: + addElementToPixelListFromPoint(endpoint, endpointTable, endpoint.point * oneOverEndpointWidth) + while len(endpointTable) > 0: + if len(endpointTable) == 1: + if len(endpointTable.values()[0]) < 2: + return [] + endpoints = getSquareValuesFromPoint(endpointTable, otherEndpoint.point * oneOverEndpointWidth) + nextEndpoint = otherEndpoint.getClosestMiss(endpoints, path, pixelDictionary, width) + if nextEndpoint == None: + path = [] + paths.append(path) + endpoints = getListTableElements(endpointTable) + nextEndpoint = otherEndpoint.getClosestEndpoint(endpoints) +# this commented code should be faster than the getListTableElements code, but it isn't, someday a spiral algorithim could be tried +# endpoints = getSquareValuesFromPoint( endpointTable, otherEndpoint.point * oneOverEndpointWidth ) +# nextEndpoint = otherEndpoint.getClosestEndpoint(endpoints) +# if nextEndpoint == None: +# endpoints = [] +# for endpointTableValue in endpointTable.values(): +# endpoints.append( endpointTableValue[0] ) +# nextEndpoint = otherEndpoint.getClosestEndpoint(endpoints) +# endpoints = getSquareValuesFromPoint( endpointTable, nextEndpoint.point * oneOverEndpointWidth ) +# nextEndpoint = otherEndpoint.getClosestEndpoint(endpoints) + addPointToPath(path, pixelDictionary, nextEndpoint.point, len(paths) - 1, width) + removeElementFromPixelListFromPoint(nextEndpoint, endpointTable, nextEndpoint.point * oneOverEndpointWidth) + otherEndpoint = nextEndpoint.otherEndpoint + addPointToPath(path, pixelDictionary, otherEndpoint.point, len(paths) - 1, width) + removeElementFromPixelListFromPoint(otherEndpoint, endpointTable, otherEndpoint.point * oneOverEndpointWidth) + return paths + +def getPlaneDot( vec3First, vec3Second ): + 'Get the dot product of the x and y components of a pair of Vector3s.' + return vec3First.x * vec3Second.x + vec3First.y * vec3Second.y + +def getPluralString( number, suffix ): + 'Get the plural string.' + if number == 1: + return '1 %s' % suffix + return '%s %ss' % ( number, suffix ) + +def getPointPlusSegmentWithLength( length, point, segment ): + 'Get point plus a segment scaled to a given length.' + return segment * length / abs(segment) + point + +def getPointsByHorizontalDictionary(width, xIntersectionsDictionary): + 'Get points from the horizontalXIntersectionsDictionary.' + points = [] + xIntersectionsDictionaryKeys = xIntersectionsDictionary.keys() + xIntersectionsDictionaryKeys.sort() + for xIntersectionsDictionaryKey in xIntersectionsDictionaryKeys: + for xIntersection in xIntersectionsDictionary[xIntersectionsDictionaryKey]: + points.append(complex(xIntersection, xIntersectionsDictionaryKey * width)) + return points + +def getPointsByVerticalDictionary(width, xIntersectionsDictionary): + 'Get points from the verticalXIntersectionsDictionary.' + points = [] + xIntersectionsDictionaryKeys = xIntersectionsDictionary.keys() + xIntersectionsDictionaryKeys.sort() + for xIntersectionsDictionaryKey in xIntersectionsDictionaryKeys: + for xIntersection in xIntersectionsDictionary[xIntersectionsDictionaryKey]: + points.append(complex(xIntersectionsDictionaryKey * width, xIntersection)) + return points + +def getRadiusArealizedMultiplier(sides): + 'Get the radius multiplier for a polygon of equal area.' + return math.sqrt(globalTau / sides / math.sin(globalTau / sides)) + +def getRandomComplex(begin, end): + 'Get random complex.' + endMinusBegin = end - begin + return begin + complex(random.random() * endMinusBegin.real, random.random() * endMinusBegin.imag) + +def getRank(width): + 'Get the rank which is 0 at 1 and increases by three every power of ten.' + return int(math.floor(3.0 * math.log10(width))) + +def getRotatedComplexes(planeAngle, points): + 'Get points rotated by the plane angle' + rotatedComplexes = [] + for point in points: + rotatedComplexes.append(planeAngle * point) + return rotatedComplexes + +def getRotatedComplexLists(planeAngle, pointLists): + 'Get point lists rotated by the plane angle' + rotatedComplexLists = [] + for pointList in pointLists: + rotatedComplexLists.append(getRotatedComplexes(planeAngle, pointList)) + return rotatedComplexLists + +def getRotatedWiddershinsQuarterAroundZAxis(vector3): + 'Get Vector3 rotated a quarter widdershins turn around Z axis.' + return Vector3(-vector3.y, vector3.x, vector3.z) + +def getRoundedPoint(point): + 'Get point with each component rounded.' + return Vector3(round(point.x), round( point.y ), round(point.z)) + +def getRoundedToPlaces(decimalPlaces, number): + 'Get number rounded to a number of decimal places.' + decimalPlacesRounded = max(1, int(round(decimalPlaces))) + return round(number, decimalPlacesRounded) + +def getRoundedToPlacesString(decimalPlaces, number): + 'Get number rounded to a number of decimal places as a string, without exponential formatting.' + roundedToPlaces = getRoundedToPlaces(decimalPlaces, number) + roundedToPlacesString = str(roundedToPlaces) + if 'e' in roundedToPlacesString: + return ('%.15f' % roundedToPlaces).rstrip('0') + return roundedToPlacesString + +def getRoundedToThreePlaces(number): + 'Get number rounded to three places as a string.' + return str(round(number, 3)) + +def getRoundZAxisByPlaneAngle( planeAngle, vector3 ): + 'Get Vector3 rotated by a plane angle.' + return Vector3( vector3.x * planeAngle.real - vector3.y * planeAngle.imag, vector3.x * planeAngle.imag + vector3.y * planeAngle.real, vector3.z ) + +def getSegmentFromPath( path, pathIndex ): + 'Get endpoint segment from a path.' + if len(path) < 2: + return None + begin = path[-1] + end = path[-2] + forwardEndpoint = getEndpointFromPath( path, pathIndex ) + reversePath = path[:] + reversePath.reverse() + reverseEndpoint = getEndpointFromPath( reversePath, pathIndex ) + return ( forwardEndpoint, reverseEndpoint ) + +def getSegmentFromPoints( begin, end ): + 'Get endpoint segment from a pair of points.' + endpointFirst = Endpoint() + endpointSecond = Endpoint().getFromOtherPoint( endpointFirst, end ) + endpointFirst.getFromOtherPoint( endpointSecond, begin ) + return ( endpointFirst, endpointSecond ) + +def getSegmentsFromXIntersectionIndexes( xIntersectionIndexList, y ): + 'Get endpoint segments from the x intersection indexes.' + xIntersections = getXIntersectionsFromIntersections( xIntersectionIndexList ) + return getSegmentsFromXIntersections( xIntersections, y ) + +def getSegmentsFromXIntersections( xIntersections, y ): + 'Get endpoint segments from the x intersections.' + segments = [] + end = len( xIntersections ) + if len( xIntersections ) % 2 == 1: + end -= 1 + for xIntersectionIndex in xrange( 0, end, 2 ): + firstX = xIntersections[ xIntersectionIndex ] + secondX = xIntersections[ xIntersectionIndex + 1 ] + if firstX != secondX: + segments.append( getSegmentFromPoints( complex( firstX, y ), complex( secondX, y ) ) ) + return segments + +def getSimplifiedLoop( loop, radius ): + 'Get loop with points inside the channel removed.' + if len(loop) < 2: + return loop + simplificationMultiplication = 256 + simplificationRadius = radius / float( simplificationMultiplication ) + maximumIndex = len(loop) * simplificationMultiplication + pointIndex = 1 + while pointIndex < maximumIndex: + oldLoopLength = len(loop) + loop = getHalfSimplifiedLoop( loop, simplificationRadius, 0 ) + loop = getHalfSimplifiedLoop( loop, simplificationRadius, 1 ) + simplificationRadius += simplificationRadius + if oldLoopLength == len(loop): + if simplificationRadius > radius: + return getAwayPoints( loop, radius ) + else: + simplificationRadius *= 1.5 + simplificationRadius = min( simplificationRadius, radius ) + pointIndex += pointIndex + return getAwayPoints( loop, radius ) + +def getSimplifiedLoops( loops, radius ): + 'Get the simplified loops.' + simplifiedLoops = [] + for loop in loops: + simplifiedLoops.append( getSimplifiedLoop( loop, radius ) ) + return simplifiedLoops + +def getSimplifiedPath(path, radius): + 'Get path with points inside the channel removed.' + if len(path) < 2: + return path + simplificationMultiplication = 256 + simplificationRadius = radius / float(simplificationMultiplication) + maximumIndex = len(path) * simplificationMultiplication + pointIndex = 1 + while pointIndex < maximumIndex: + oldPathLength = len(path) + path = getHalfSimplifiedPath(path, simplificationRadius, 0) + path = getHalfSimplifiedPath(path, simplificationRadius, 1) + simplificationRadius += simplificationRadius + if oldPathLength == len(path): + if simplificationRadius > radius: + return getAwayPath(path, radius) + else: + simplificationRadius *= 1.5 + simplificationRadius = min(simplificationRadius, radius) + pointIndex += pointIndex + return getAwayPath(path, radius) + +def getSquareIsOccupied( pixelDictionary, x, y ): + 'Determine if a square around the x and y pixel coordinates is occupied.' + squareValues = [] + for xStep in xrange(x - 1, x + 2): + for yStep in xrange(y - 1, y + 2): + if (xStep, yStep) in pixelDictionary: + return True + return False + +def getSquareLoopWiddershins(beginComplex, endComplex): + 'Get a square loop from the beginning to the end and back.' + loop = [beginComplex, complex(endComplex.real, beginComplex.imag), endComplex] + loop.append(complex(beginComplex.real, endComplex.imag)) + return loop + +def getSquareValues( pixelDictionary, x, y ): + 'Get a list of the values in a square around the x and y pixel coordinates.' + squareValues = [] + for xStep in xrange(x - 1, x + 2): + for yStep in xrange(y - 1, y + 2): + stepKey = (xStep, yStep) + if stepKey in pixelDictionary: + squareValues += pixelDictionary[ stepKey ] + return squareValues + +def getSquareValuesFromPoint( pixelDictionary, point ): + 'Get a list of the values in a square around the point.' + return getSquareValues(pixelDictionary, int(round(point.real)), int(round(point.imag))) + +def getStepKeyFromPoint(point): + 'Get step key for the point.' + return (int(round(point.real)), int(round(point.imag))) + +def getThreeSignificantFigures(number): + 'Get number rounded to three significant figures as a string.' + absoluteNumber = abs(number) + if absoluteNumber >= 10.0: + return getRoundedToPlacesString( 1, number ) + if absoluteNumber < 0.000000001: + return getRoundedToPlacesString( 12, number ) + return getRoundedToPlacesString( 1 - math.floor( math.log10( absoluteNumber ) ), number ) + +def getTopPath(path): + 'Get the top of the path.' + top = -987654321987654321.0 + for point in path: + top = max(top, point.z) + return top + +def getTopPaths(paths): + 'Get the top of the paths.' + top = -987654321987654321.0 + for path in paths: + for point in path: + top = max(top, point.z) + return top + +def getTransferClosestNestedRing(extrusionHalfWidth, nestedRings, oldOrderedLocation, skein, threadSequence): + 'Get and transfer the closest remaining nested ring.' + if len(nestedRings) > 0: + oldOrderedLocation.z = nestedRings[0].z + closestDistance = 987654321987654321.0 + closestNestedRing = None + for remainingNestedRing in nestedRings: + distance = getClosestDistanceIndexToLine(oldOrderedLocation.dropAxis(), remainingNestedRing.boundary).distance + if distance < closestDistance: + closestDistance = distance + closestNestedRing = remainingNestedRing + nestedRings.remove(closestNestedRing) + closestNestedRing.addToThreads(extrusionHalfWidth, oldOrderedLocation, skein, threadSequence) + return closestNestedRing + +def getTransferredNestedRings( insides, loop ): + 'Get transferred paths from inside nested rings.' + transferredSurroundings = [] + for insideIndex in xrange( len( insides ) - 1, - 1, - 1 ): + insideSurrounding = insides[ insideIndex ] + if isPathInsideLoop( loop, insideSurrounding.boundary ): + transferredSurroundings.append( insideSurrounding ) + del insides[ insideIndex ] + return transferredSurroundings + +def getTransferredPaths( insides, loop ): + 'Get transferred paths from inside paths.' + transferredPaths = [] + for insideIndex in xrange( len( insides ) - 1, - 1, - 1 ): + inside = insides[ insideIndex ] + if isPathInsideLoop( loop, inside ): + transferredPaths.append( inside ) + del insides[ insideIndex ] + return transferredPaths + +def getTranslatedComplexPath(path, translateComplex): + 'Get the translated complex path.' + translatedComplexPath = [] + for point in path: + translatedComplexPath.append(point + translateComplex) + return translatedComplexPath + +def getVector3Path(complexPath, z=0.0): + 'Get the vector3 path from the complex path.' + vector3Path = [] + for complexPoint in complexPath: + vector3Path.append(Vector3(complexPoint.real, complexPoint.imag, z)) + return vector3Path + +def getVector3Paths(complexPaths, z=0.0): + 'Get the vector3 paths from the complex paths.' + vector3Paths = [] + for complexPath in complexPaths: + vector3Paths.append(getVector3Path(complexPath, z)) + return vector3Paths + +def getWiddershinsUnitPolar(angle): + 'Get polar complex from counterclockwise angle from 1, 0.' + return complex(math.cos(angle), math.sin(angle)) + +def getXIntersectionIfExists( beginComplex, endComplex, y ): + 'Get the x intersection if it exists.' + if ( y > beginComplex.imag ) == ( y > endComplex.imag ): + return None + endMinusBeginComplex = endComplex - beginComplex + return ( y - beginComplex.imag ) / endMinusBeginComplex.imag * endMinusBeginComplex.real + beginComplex.real + +def getXIntersectionsFromIntersections( xIntersectionIndexList ): + 'Get x intersections from the x intersection index list, in other words subtract non negative intersections from negatives.' + xIntersections = [] + fill = False + solid = False + solidTable = {} + xIntersectionIndexList.sort() + for solidX in xIntersectionIndexList: + if solidX.index >= 0: + toggleHashtable( solidTable, solidX.index, '' ) + else: + fill = not fill + oldSolid = solid + solid = ( len( solidTable ) == 0 and fill ) + if oldSolid != solid: + xIntersections.append( solidX.x ) + return xIntersections + +def getXYComplexFromVector3(vector3): + 'Get an xy complex from a vector3 if it exists, otherwise return None.' + if vector3 == None: + return None + return vector3.dropAxis() + +def getYIntersectionIfExists( beginComplex, endComplex, x ): + 'Get the y intersection if it exists.' + if ( x > beginComplex.real ) == ( x > endComplex.real ): + return None + endMinusBeginComplex = endComplex - beginComplex + return ( x - beginComplex.real ) / endMinusBeginComplex.real * endMinusBeginComplex.imag + beginComplex.imag + +def getZComponentCrossProduct( vec3First, vec3Second ): + 'Get z component cross product of a pair of Vector3s.' + return vec3First.x * vec3Second.y - vec3First.y * vec3Second.x + +def isInsideOtherLoops( loopIndex, loops ): + 'Determine if a loop in a list is inside another loop in that list.' + return isPathInsideLoops( loops[ : loopIndex ] + loops[loopIndex + 1 :], loops[loopIndex] ) + +def isLineIntersectingInsideXSegment( beginComplex, endComplex, segmentFirstX, segmentSecondX, y ): + 'Determine if the line is crossing inside the x segment.' + xIntersection = getXIntersectionIfExists( beginComplex, endComplex, y ) + if xIntersection == None: + return False + if xIntersection < min( segmentFirstX, segmentSecondX ): + return False + return xIntersection <= max( segmentFirstX, segmentSecondX ) + +def isLineIntersectingLoop( loop, pointBegin, pointEnd ): + 'Determine if the line is intersecting loops.' + normalizedSegment = pointEnd - pointBegin + normalizedSegmentLength = abs( normalizedSegment ) + if normalizedSegmentLength > 0.0: + normalizedSegment /= normalizedSegmentLength + segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag) + pointBeginRotated = segmentYMirror * pointBegin + pointEndRotated = segmentYMirror * pointEnd + if isLoopIntersectingInsideXSegment( loop, pointBeginRotated.real, pointEndRotated.real, segmentYMirror, pointBeginRotated.imag ): + return True + return False + +def isLineIntersectingLoops( loops, pointBegin, pointEnd ): + 'Determine if the line is intersecting loops.' + normalizedSegment = pointEnd - pointBegin + normalizedSegmentLength = abs( normalizedSegment ) + if normalizedSegmentLength > 0.0: + normalizedSegment /= normalizedSegmentLength + segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag) + pointBeginRotated = segmentYMirror * pointBegin + pointEndRotated = segmentYMirror * pointEnd + if isLoopListIntersectingInsideXSegment( loops, pointBeginRotated.real, pointEndRotated.real, segmentYMirror, pointBeginRotated.imag ): + return True + return False + +def isLoopIntersectingInsideXSegment( loop, segmentFirstX, segmentSecondX, segmentYMirror, y ): + 'Determine if the loop is intersecting inside the x segment.' + rotatedLoop = getRotatedComplexes( segmentYMirror, loop ) + for pointIndex in xrange( len( rotatedLoop ) ): + pointFirst = rotatedLoop[pointIndex] + pointSecond = rotatedLoop[ (pointIndex + 1) % len( rotatedLoop ) ] + if isLineIntersectingInsideXSegment( pointFirst, pointSecond, segmentFirstX, segmentSecondX, y ): + return True + return False + +def isLoopIntersectingLoop( loop, otherLoop ): + 'Determine if the loop is intersecting the other loop.' + for pointIndex in xrange(len(loop)): + pointBegin = loop[pointIndex] + pointEnd = loop[(pointIndex + 1) % len(loop)] + if isLineIntersectingLoop( otherLoop, pointBegin, pointEnd ): + return True + return False + +def isLoopIntersectingLoops( loop, otherLoops ): + 'Determine if the loop is intersecting other loops.' + for pointIndex in xrange(len(loop)): + pointBegin = loop[pointIndex] + pointEnd = loop[(pointIndex + 1) % len(loop)] + if isLineIntersectingLoops( otherLoops, pointBegin, pointEnd ): + return True + return False + +def isLoopListIntersecting(loops): + 'Determine if a loop in the list is intersecting the other loops.' + for loopIndex in xrange(len(loops) - 1): + loop = loops[loopIndex] + if isLoopIntersectingLoops(loop, loops[loopIndex + 1 :]): + return True + return False + +def isLoopListIntersectingInsideXSegment( loopList, segmentFirstX, segmentSecondX, segmentYMirror, y ): + 'Determine if the loop list is crossing inside the x segment.' + for alreadyFilledLoop in loopList: + if isLoopIntersectingInsideXSegment( alreadyFilledLoop, segmentFirstX, segmentSecondX, segmentYMirror, y ): + return True + return False + +def isPathEntirelyInsideLoop(loop, path): + 'Determine if a path is entirely inside another loop.' + for point in path: + if not isPointInsideLoop(loop, point): + return False + return True + +def isPathEntirelyInsideLoops(loops, path): + 'Determine if a path is entirely inside another loop in a list.' + for loop in loops: + if isPathEntirelyInsideLoop(loop, path): + return True + return False + +def isPathInsideLoop(loop, path): + 'Determine if a path is inside another loop.' + return isPointInsideLoop(loop, getLeftPoint(path)) + +def isPathInsideLoops(loops, path): + 'Determine if a path is inside another loop in a list.' + for loop in loops: + if isPathInsideLoop(loop, path): + return True + return False + +def isPixelTableIntersecting( bigTable, littleTable, maskTable = {} ): + 'Add path to the pixel table.' + littleTableKeys = littleTable.keys() + for littleTableKey in littleTableKeys: + if littleTableKey not in maskTable: + if littleTableKey in bigTable: + return True + return False + +def isPointInsideLoop(loop, point): + 'Determine if a point is inside another loop.' + return getNumberOfIntersectionsToLeft(loop, point) % 2 == 1 + +def isSegmentCompletelyInX( segment, xFirst, xSecond ): + 'Determine if the segment overlaps within x.' + segmentFirstX = segment[0].point.real + segmentSecondX = segment[1].point.real + if max( segmentFirstX, segmentSecondX ) > max( xFirst, xSecond ): + return False + return min( segmentFirstX, segmentSecondX ) >= min( xFirst, xSecond ) + +def isWiddershins(polygonComplex): + 'Determine if the complex polygon goes round in the widdershins direction.' + return getAreaLoop(polygonComplex) > 0.0 + +def isWithinChannel( channelRadius, pointIndex, loop ): + 'Determine if the the point is within the channel between two adjacent points.' + point = loop[pointIndex] + behindSegmentComplex = loop[(pointIndex + len(loop) - 1) % len(loop)] - point + behindSegmentComplexLength = abs( behindSegmentComplex ) + if behindSegmentComplexLength < channelRadius: + return True + aheadSegmentComplex = loop[(pointIndex + 1) % len(loop)] - point + aheadSegmentComplexLength = abs( aheadSegmentComplex ) + if aheadSegmentComplexLength < channelRadius: + return True + behindSegmentComplex /= behindSegmentComplexLength + aheadSegmentComplex /= aheadSegmentComplexLength + absoluteZ = getDotProductPlusOne( aheadSegmentComplex, behindSegmentComplex ) + if behindSegmentComplexLength * absoluteZ < channelRadius: + return True + return aheadSegmentComplexLength * absoluteZ < channelRadius + +def isXSegmentIntersectingPath( path, segmentFirstX, segmentSecondX, segmentYMirror, y ): + 'Determine if a path is crossing inside the x segment.' + rotatedPath = getRotatedComplexes( segmentYMirror, path ) + for pointIndex in xrange( len( rotatedPath ) - 1 ): + pointFirst = rotatedPath[pointIndex] + pointSecond = rotatedPath[pointIndex + 1] + if isLineIntersectingInsideXSegment( pointFirst, pointSecond, segmentFirstX, segmentSecondX, y ): + return True + return False + +def isXSegmentIntersectingPaths( paths, segmentFirstX, segmentSecondX, segmentYMirror, y ): + 'Determine if a path list is crossing inside the x segment.' + for path in paths: + if isXSegmentIntersectingPath( path, segmentFirstX, segmentSecondX, segmentYMirror, y ): + return True + return False + +def joinSegmentTables( fromTable, intoTable ): + 'Join both segment tables and put the join into the intoTable.' + intoTableKeys = intoTable.keys() + fromTableKeys = fromTable.keys() + joinedKeyTable = {} + concatenatedTableKeys = intoTableKeys + fromTableKeys + for concatenatedTableKey in concatenatedTableKeys: + joinedKeyTable[ concatenatedTableKey ] = None + joinedKeys = joinedKeyTable.keys() + joinedKeys.sort() + for joinedKey in joinedKeys: + xIntersectionIndexList = [] + if joinedKey in intoTable: + addXIntersectionIndexesFromSegments( 0, intoTable[ joinedKey ], xIntersectionIndexList ) + if joinedKey in fromTable: + addXIntersectionIndexesFromSegments( 1, fromTable[ joinedKey ], xIntersectionIndexList ) + xIntersections = getJoinOfXIntersectionIndexes( xIntersectionIndexList ) + lineSegments = getSegmentsFromXIntersections( xIntersections, joinedKey ) + if len( lineSegments ) > 0: + intoTable[ joinedKey ] = lineSegments + else: + print('This should never happen, there are no line segments in joinSegments in euclidean') + +def joinXIntersectionsTables( fromTable, intoTable ): + 'Join both XIntersections tables and put the join into the intoTable.' + joinedKeyTable = {} + concatenatedTableKeys = fromTable.keys() + intoTable.keys() + for concatenatedTableKey in concatenatedTableKeys: + joinedKeyTable[ concatenatedTableKey ] = None + for joinedKey in joinedKeyTable.keys(): + xIntersectionIndexList = [] + if joinedKey in intoTable: + addXIntersectionIndexesFromXIntersections( 0, xIntersectionIndexList, intoTable[ joinedKey ] ) + if joinedKey in fromTable: + addXIntersectionIndexesFromXIntersections( 1, xIntersectionIndexList, fromTable[ joinedKey ] ) + xIntersections = getJoinOfXIntersectionIndexes( xIntersectionIndexList ) + if len( xIntersections ) > 0: + intoTable[ joinedKey ] = xIntersections + else: + print('This should never happen, there are no line segments in joinSegments in euclidean') + +def overwriteDictionary(fromDictionary, keys, toDictionary): + 'Overwrite the dictionary.' + for key in keys: + if key in fromDictionary: + toDictionary[key] = fromDictionary[key] + +def removeElementFromDictionary(dictionary, key): + 'Remove element from the dictionary.' + if key in dictionary: + del dictionary[key] + +def removeElementFromListTable(element, key, listDictionary): + 'Remove an element from the list table.' + if key not in listDictionary: + return + elementList = listDictionary[key] + if len( elementList ) < 2: + del listDictionary[key] + return + if element in elementList: + elementList.remove(element) + +def removeElementFromPixelListFromPoint( element, pixelDictionary, point ): + 'Remove an element from the pixel list.' + stepKey = getStepKeyFromPoint(point) + removeElementFromListTable( element, stepKey, pixelDictionary ) + +def removeElementsFromDictionary(dictionary, keys): + 'Remove list from the dictionary.' + for key in keys: + removeElementFromDictionary(dictionary, key) + +def removePixelTableFromPixelTable( pixelDictionaryToBeRemoved, pixelDictionaryToBeRemovedFrom ): + 'Remove pixel from the pixel table.' + removeElementsFromDictionary( pixelDictionaryToBeRemovedFrom, pixelDictionaryToBeRemoved.keys() ) + +def removePrefixFromDictionary( dictionary, prefix ): + 'Remove the attributes starting with the prefix from the dictionary.' + for key in dictionary.keys(): + if key.startswith( prefix ): + del dictionary[key] + +def removeTrueFromDictionary(dictionary, key): + 'Remove key from the dictionary in the value is true.' + if key in dictionary: + if getBooleanFromValue(dictionary[key]): + del dictionary[key] + +def removeTrueListFromDictionary( dictionary, keys ): + 'Remove list from the dictionary in the value is true.' + for key in keys: + removeTrueFromDictionary( dictionary, key ) + +def subtractXIntersectionsTable( subtractFromTable, subtractTable ): + 'Subtract the subtractTable from the subtractFromTable.' + subtractFromTableKeys = subtractFromTable.keys() + subtractFromTableKeys.sort() + for subtractFromTableKey in subtractFromTableKeys: + xIntersectionIndexList = [] + addXIntersectionIndexesFromXIntersections( - 1, xIntersectionIndexList, subtractFromTable[ subtractFromTableKey ] ) + if subtractFromTableKey in subtractTable: + addXIntersectionIndexesFromXIntersections( 0, xIntersectionIndexList, subtractTable[ subtractFromTableKey ] ) + xIntersections = getXIntersectionsFromIntersections( xIntersectionIndexList ) + if len( xIntersections ) > 0: + subtractFromTable[ subtractFromTableKey ] = xIntersections + else: + del subtractFromTable[ subtractFromTableKey ] + +def swapList( elements, indexBegin, indexEnd ): + 'Swap the list elements.' + elements[ indexBegin ], elements[ indexEnd ] = elements[ indexEnd ], elements[ indexBegin ] + +def toggleHashtable( hashtable, key, value ): + 'Toggle a hashtable between having and not having a key.' + if key in hashtable: + del hashtable[key] + else: + hashtable[key] = value + +def transferClosestFillLoop(extrusionHalfWidth, oldOrderedLocation, remainingFillLoops, skein): + 'Transfer the closest remaining fill loop.' + closestDistance = 987654321987654321.0 + closestFillLoop = None + for remainingFillLoop in remainingFillLoops: + distance = getClosestDistanceIndexToLine(oldOrderedLocation.dropAxis(), remainingFillLoop).distance + if distance < closestDistance: + closestDistance = distance + closestFillLoop = remainingFillLoop + newClosestFillLoop = getLoopInsideContainingLoop(closestFillLoop, remainingFillLoops) + while newClosestFillLoop != None: + closestFillLoop = newClosestFillLoop + newClosestFillLoop = getLoopInsideContainingLoop(closestFillLoop, remainingFillLoops) + remainingFillLoops.remove(closestFillLoop) + addToThreadsFromLoop(extrusionHalfWidth, 'loop', closestFillLoop[:], oldOrderedLocation, skein) + +def transferClosestPath( oldOrderedLocation, remainingPaths, skein ): + 'Transfer the closest remaining path.' + closestDistance = 987654321987654321.0 + closestPath = None + oldOrderedLocationComplex = oldOrderedLocation.dropAxis() + for remainingPath in remainingPaths: + distance = min( abs( oldOrderedLocationComplex - remainingPath[0] ), abs( oldOrderedLocationComplex - remainingPath[-1] ) ) + if distance < closestDistance: + closestDistance = distance + closestPath = remainingPath + remainingPaths.remove( closestPath ) + skein.addGcodeFromThreadZ( closestPath, oldOrderedLocation.z ) + oldOrderedLocation.x = closestPath[-1].real + oldOrderedLocation.y = closestPath[-1].imag + +def transferClosestPaths(oldOrderedLocation, remainingPaths, skein): + 'Transfer the closest remaining paths.' + while len(remainingPaths) > 0: + transferClosestPath(oldOrderedLocation, remainingPaths, skein) + +def transferPathsToNestedRings(nestedRings, paths): + 'Transfer paths to nested rings.' + for nestedRing in nestedRings: + nestedRing.transferPaths(paths) + +def translateVector3Path(path, translateVector3): + 'Translate the vector3 path.' + for point in path: + point.setToVector3(point + translateVector3) + +def translateVector3Paths(paths, translateVector3): + 'Translate the vector3 paths.' + for path in paths: + translateVector3Path(path, translateVector3) + +def unbuckleBasis( basis, maximumUnbuckling, normal ): + 'Unbuckle space.' + normalDot = basis.dot( normal ) + dotComplement = math.sqrt( 1.0 - normalDot * normalDot ) + unbuckling = maximumUnbuckling + if dotComplement > 0.0: + unbuckling = min( 1.0 / dotComplement, maximumUnbuckling ) + basis.setToVector3( basis * unbuckling ) + + +class DistanceIndex: + 'A class to hold the distance and the index of the loop.' + def __init__(self, distance, index): + 'Initialize.' + self.distance = distance + self.index = index + + def __repr__(self): + 'Get the string representation of this distance index.' + return '%s, %s' % (self.distance, self.index) + + +class Endpoint: + 'The endpoint of a segment.' + def __repr__(self): + 'Get the string representation of this Endpoint.' + return 'Endpoint %s, %s' % ( self.point, self.otherEndpoint.point ) + + def getClosestEndpoint( self, endpoints ): + 'Get closest endpoint.' + smallestDistance = 987654321987654321.0 + closestEndpoint = None + for endpoint in endpoints: + distance = abs( self.point - endpoint.point ) + if distance < smallestDistance: + smallestDistance = distance + closestEndpoint = endpoint + return closestEndpoint + + def getClosestMiss(self, endpoints, path, pixelDictionary, width): + 'Get the closest endpoint which the segment to that endpoint misses the other extrusions.' + pathMaskTable = {} + smallestDistance = 987654321.0 + penultimateMinusPoint = complex(0.0, 0.0) + if len(path) > 1: + penultimatePoint = path[-2] + addSegmentToPixelTable(penultimatePoint, self.point, pathMaskTable, 0, 0, width) + penultimateMinusPoint = penultimatePoint - self.point + if abs(penultimateMinusPoint) > 0.0: + penultimateMinusPoint /= abs(penultimateMinusPoint) + for endpoint in endpoints: + endpoint.segment = endpoint.point - self.point + endpoint.segmentLength = abs(endpoint.segment) + if endpoint.segmentLength <= 0.0: + return endpoint + endpoints.sort(compareSegmentLength) + for endpoint in endpoints[: 15]: # increasing the number of searched endpoints increases the search time, with 20 fill took 600 seconds for cilinder.gts, with 10 fill took 533 seconds + normalizedSegment = endpoint.segment / endpoint.segmentLength + isOverlappingSelf = getDotProduct(penultimateMinusPoint, normalizedSegment) > 0.9 + if not isOverlappingSelf: + if len(path) > 2: + segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag) + pointRotated = segmentYMirror * self.point + endpointPointRotated = segmentYMirror * endpoint.point + if isXSegmentIntersectingPath(path[max(0, len(path) - 21) : -1], pointRotated.real, endpointPointRotated.real, segmentYMirror, pointRotated.imag): + isOverlappingSelf = True + if not isOverlappingSelf: + totalMaskTable = pathMaskTable.copy() + addSegmentToPixelTable(endpoint.point, endpoint.otherEndpoint.point, totalMaskTable, 0, 0, width) + segmentTable = {} + addSegmentToPixelTable(self.point, endpoint.point, segmentTable, 0, 0, width) + if not isPixelTableIntersecting(pixelDictionary, segmentTable, totalMaskTable): + return endpoint + return None + + def getClosestMissCheckEndpointPath( self, endpoints, path, pixelDictionary, width ): + 'Get the closest endpoint which the segment to that endpoint misses the other extrusions, also checking the path of the endpoint.' + pathMaskTable = {} + smallestDistance = 987654321.0 + penultimateMinusPoint = complex(0.0, 0.0) + if len(path) > 1: + penultimatePoint = path[-2] + addSegmentToPixelTable( penultimatePoint, self.point, pathMaskTable, 0, 0, width ) + penultimateMinusPoint = penultimatePoint - self.point + if abs(penultimateMinusPoint) > 0.0: + penultimateMinusPoint /= abs(penultimateMinusPoint) + for endpoint in endpoints: + endpoint.segment = endpoint.point - self.point + endpoint.segmentLength = abs(endpoint.segment) + if endpoint.segmentLength <= 0.0: + return endpoint + endpoints.sort( compareSegmentLength ) + for endpoint in endpoints[ : 15 ]: # increasing the number of searched endpoints increases the search time, with 20 fill took 600 seconds for cilinder.gts, with 10 fill took 533 seconds + normalizedSegment = endpoint.segment / endpoint.segmentLength + isOverlappingSelf = getDotProduct( penultimateMinusPoint, normalizedSegment ) > 0.9 + if not isOverlappingSelf: + if len(path) > 2: + segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag) + pointRotated = segmentYMirror * self.point + endpointPointRotated = segmentYMirror * endpoint.point + if isXSegmentIntersectingPath( path[ max( 0, len(path) - 21 ) : - 1 ], pointRotated.real, endpointPointRotated.real, segmentYMirror, pointRotated.imag ): + isOverlappingSelf = True + endpointPath = endpoint.path + if len( endpointPath ) > 2: + segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag) + pointRotated = segmentYMirror * self.point + endpointPointRotated = segmentYMirror * endpoint.point + if isXSegmentIntersectingPath( endpointPath, pointRotated.real, endpointPointRotated.real, segmentYMirror, pointRotated.imag ): + isOverlappingSelf = True + if not isOverlappingSelf: + totalMaskTable = pathMaskTable.copy() + addSegmentToPixelTable( endpoint.point, endpoint.otherEndpoint.point, totalMaskTable, 0, 0, width ) + segmentTable = {} + addSegmentToPixelTable( self.point, endpoint.point, segmentTable, 0, 0, width ) + if not isPixelTableIntersecting( pixelDictionary, segmentTable, totalMaskTable ): + return endpoint + return None + + def getFromOtherPoint( self, otherEndpoint, point ): + 'Initialize from other endpoint.' + self.otherEndpoint = otherEndpoint + self.point = point + return self + + +class LoopLayer: + 'Loops with a z.' + def __init__(self, z): + 'Initialize.' + self.loops = [] + self.z = z + + def __repr__(self): + 'Get the string representation of this loop layer.' + return '%s, %s' % (self.z, self.loops) + + +class NestedRing: + 'A nested ring.' + def __init__(self): + 'Initialize.' + self.boundary = [] + self.innerNestedRings = None + + def __repr__(self): + 'Get the string representation of this nested ring.' + return str(self.__dict__) + + def addFlattenedNestedRings(self, flattenedNestedRings): + 'Add flattened nested rings.' + flattenedNestedRings.append(self) + for innerNestedRing in self.innerNestedRings: + flattenedNestedRings += getFlattenedNestedRings(innerNestedRing.innerNestedRings) + + def getFromInsideSurroundings(self, inputSurroundingInsides): + 'Initialize from inside nested rings.' + transferredSurroundings = getTransferredNestedRings(inputSurroundingInsides, self.boundary) + self.innerNestedRings = getOrderedNestedRings(transferredSurroundings) + return self + + +class NestedBand(NestedRing): + 'A loop that surrounds paths.' + def __init__(self): + 'Initialize.' + NestedRing.__init__(self) + self.edgePaths = [] + self.extraLoops = [] + self.infillBoundaries = [] + self.infillPaths = [] +# self.lastExistingFillLoops = None + self.lastFillLoops = None + self.loop = None + self.penultimateFillLoops = [] + self.z = None + + def __repr__(self): + 'Get the string representation of this nested ring.' + stringRepresentation = 'boundary\n%s\n' % self.boundary + stringRepresentation += 'loop\n%s\n' % self.loop + stringRepresentation += 'inner nested rings\n%s\n' % self.innerNestedRings + stringRepresentation += 'infillPaths\n' + for infillPath in self.infillPaths: + stringRepresentation += 'infillPath\n%s\n' % infillPath + stringRepresentation += 'edgePaths\n' + for edgePath in self.edgePaths: + stringRepresentation += 'edgePath\n%s\n' % edgePath + return stringRepresentation + '\n' + + def addPerimeterInner(self, extrusionHalfWidth, oldOrderedLocation, skein, threadSequence): + 'Add to the edge and the inner island.' + if self.loop == None: + skein.distanceFeedRate.addLine('()') + transferClosestPaths(oldOrderedLocation, self.edgePaths[:], skein) + skein.distanceFeedRate.addLine('()') + else: + addToThreadsFromLoop(extrusionHalfWidth, 'edge', self.loop[:], oldOrderedLocation, skein) + skein.distanceFeedRate.addLine('()') + addToThreadsRemove(extrusionHalfWidth, self.innerNestedRings[:], oldOrderedLocation, skein, threadSequence) + + def addToBoundary(self, vector3): + 'Add vector3 to boundary.' + self.boundary.append(vector3.dropAxis()) + self.z = vector3.z + + def addToLoop(self, vector3): + 'Add vector3 to loop.' + if self.loop == None: + self.loop = [] + self.loop.append(vector3.dropAxis()) + self.z = vector3.z + + def addToThreads(self, extrusionHalfWidth, oldOrderedLocation, skein, threadSequence): + 'Add to paths from the last location.' + addNestedRingBeginning(skein.distanceFeedRate, self.boundary, self.z) + threadFunctionDictionary = { + 'infill' : self.transferInfillPaths, 'loops' : self.transferClosestFillLoops, 'edge' : self.addPerimeterInner} + for threadType in threadSequence: + threadFunctionDictionary[threadType](extrusionHalfWidth, oldOrderedLocation, skein, threadSequence) + skein.distanceFeedRate.addLine('()') + + def getFillLoops(self, penultimateFillLoops): + 'Get last fill loops from the outside loop and the loops inside the inside loops.' + fillLoops = self.getLoopsToBeFilled()[:] + surroundingBoundaries = self.getSurroundingBoundaries() + withinLoops = [] + if penultimateFillLoops == None: + penultimateFillLoops = self.penultimateFillLoops + if penultimateFillLoops == None: + print('Warning, penultimateFillLoops == None in getFillLoops in NestedBand in euclidean.') + return fillLoops + for penultimateFillLoop in penultimateFillLoops: + if len(penultimateFillLoop) > 2: + if getIsInFilledRegion(surroundingBoundaries, penultimateFillLoop[0]): + withinLoops.append(penultimateFillLoop) + if not getIsInFilledRegionByPaths(self.penultimateFillLoops, fillLoops): + fillLoops += self.penultimateFillLoops + for nestedRing in self.innerNestedRings: + fillLoops += getFillOfSurroundings(nestedRing.innerNestedRings, penultimateFillLoops) + return fillLoops +# +# def getLastExistingFillLoops(self): +# 'Get last existing fill loops.' +# lastExistingFillLoops = self.lastExistingFillLoops[:] +# for nestedRing in self.innerNestedRings: +# lastExistingFillLoops += nestedRing.getLastExistingFillLoops() +# return lastExistingFillLoops + + def getLoopsToBeFilled(self): + 'Get last fill loops from the outside loop and the loops inside the inside loops.' + if self.lastFillLoops == None: + return self.getSurroundingBoundaries() + return self.lastFillLoops + + def getSurroundingBoundaries(self): + 'Get the boundary of the surronding loop plus any boundaries of the innerNestedRings.' + surroundingBoundaries = [self.boundary] + for nestedRing in self.innerNestedRings: + surroundingBoundaries.append(nestedRing.boundary) + return surroundingBoundaries + + def transferClosestFillLoops(self, extrusionHalfWidth, oldOrderedLocation, skein, threadSequence): + 'Transfer closest fill loops.' + if len( self.extraLoops ) < 1: + return + remainingFillLoops = self.extraLoops[:] + while len( remainingFillLoops ) > 0: + transferClosestFillLoop(extrusionHalfWidth, oldOrderedLocation, remainingFillLoops, skein) + + def transferInfillPaths(self, extrusionHalfWidth, oldOrderedLocation, skein, threadSequence): + 'Transfer the infill paths.' + if len(self.infillBoundaries) == 0 and len(self.infillPaths) == 0: + return + skein.distanceFeedRate.addLine('()') + for infillBoundary in self.infillBoundaries: + skein.distanceFeedRate.addLine('()') + for infillPoint in infillBoundary: + infillPointVector3 = Vector3(infillPoint.real, infillPoint.imag, self.z) + skein.distanceFeedRate.addLine(skein.distanceFeedRate.getInfillBoundaryLine(infillPointVector3)) + skein.distanceFeedRate.addLine('()') + transferClosestPaths(oldOrderedLocation, self.infillPaths[:], skein) + skein.distanceFeedRate.addLine('()') + + def transferPaths(self, paths): + 'Transfer paths.' + for nestedRing in self.innerNestedRings: + transferPathsToNestedRings(nestedRing.innerNestedRings, paths) + self.infillPaths = getTransferredPaths(paths, self.boundary) + + +class PathZ: + 'Complex path with a z.' + def __init__( self, z ): + self.path = [] + self.z = z + + def __repr__(self): + 'Get the string representation of this path z.' + return '%s, %s' % ( self.z, self.path ) + + +class ProjectiveSpace: + 'Class to define a projective space.' + def __init__( self, basisX = Vector3(1.0, 0.0, 0.0), basisY = Vector3( 0.0, 1.0, 0.0 ), basisZ = Vector3(0.0, 0.0, 1.0) ): + 'Initialize the basis vectors.' + self.basisX = basisX + self.basisY = basisY + self.basisZ = basisZ + + def __repr__(self): + 'Get the string representation of this ProjectivePlane.' + return '%s, %s, %s' % ( self.basisX, self.basisY, self.basisZ ) + + def getByBasisXZ( self, basisX, basisZ ): + 'Get by x basis x and y basis.' + self.basisX = basisX + self.basisZ = basisZ + self.basisX.normalize() + self.basisY = basisZ.cross(self.basisX) + self.basisY.normalize() + return self + + def getByBasisZFirst(self, basisZ, firstVector3): + 'Get by basisZ and first.' + self.basisZ = basisZ + self.basisY = basisZ.cross(firstVector3) + self.basisY.normalize() + self.basisX = self.basisY.cross(self.basisZ) + self.basisX.normalize() + return self + + def getByBasisZTop(self, basisZ, top): + 'Get by basisZ and top.' + return self.getByBasisXZ(top.cross(basisZ), basisZ) + + def getByLatitudeLongitude( self, viewpointLatitude, viewpointLongitude ): + 'Get by latitude and longitude.' + longitudeComplex = getWiddershinsUnitPolar( math.radians( 90.0 - viewpointLongitude ) ) + viewpointLatitudeRatio = getWiddershinsUnitPolar( math.radians( viewpointLatitude ) ) + basisZ = Vector3( viewpointLatitudeRatio.imag * longitudeComplex.real, viewpointLatitudeRatio.imag * longitudeComplex.imag, viewpointLatitudeRatio.real ) + return self.getByBasisXZ( Vector3( - longitudeComplex.imag, longitudeComplex.real, 0.0 ), basisZ ) + + def getByTilt( self, tilt ): + 'Get by latitude and longitude.' + xPlaneAngle = getWiddershinsUnitPolar( tilt.real ) + self.basisX = Vector3( xPlaneAngle.real, 0.0, xPlaneAngle.imag ) + yPlaneAngle = getWiddershinsUnitPolar( tilt.imag ) + self.basisY = Vector3( 0.0, yPlaneAngle.real, yPlaneAngle.imag ) + self.basisZ = self.basisX.cross(self.basisY) + return self + + def getComplexByComplex( self, pointComplex ): + 'Get complex by complex point.' + return self.basisX.dropAxis() * pointComplex.real + self.basisY.dropAxis() * pointComplex.imag + + def getCopy(self): + 'Get copy.' + return ProjectiveSpace( self.basisX, self.basisY, self.basisZ ) + + def getDotComplex(self, point): + 'Get the dot complex.' + return complex( point.dot(self.basisX), point.dot(self.basisY) ) + + def getDotVector3(self, point): + 'Get the dot vector3.' + return Vector3(point.dot(self.basisX), point.dot(self.basisY), point.dot(self.basisZ)) + + def getNextSpace( self, nextNormal ): + 'Get next space by next normal.' + nextSpace = self.getCopy() + nextSpace.normalize() + dotNext = nextSpace.basisZ.dot( nextNormal ) + if dotNext > 0.999999: + return nextSpace + if dotNext < - 0.999999: + nextSpace.basisX = - nextSpace.basisX + return nextSpace + crossNext = nextSpace.basisZ.cross( nextNormal ) + oldBasis = ProjectiveSpace().getByBasisZTop( nextSpace.basisZ, crossNext ) + newBasis = ProjectiveSpace().getByBasisZTop( nextNormal, crossNext ) + nextSpace.basisX = newBasis.getVector3ByPoint( oldBasis.getDotVector3( nextSpace.basisX ) ) + nextSpace.basisY = newBasis.getVector3ByPoint( oldBasis.getDotVector3( nextSpace.basisY ) ) + nextSpace.basisZ = newBasis.getVector3ByPoint( oldBasis.getDotVector3( nextSpace.basisZ ) ) + nextSpace.normalize() + return nextSpace + + def getSpaceByXYScaleAngle( self, angle, scale ): + 'Get space by angle and scale.' + spaceByXYScaleRotation = ProjectiveSpace() + planeAngle = getWiddershinsUnitPolar(angle) + spaceByXYScaleRotation.basisX = self.basisX * scale.real * planeAngle.real + self.basisY * scale.imag * planeAngle.imag + spaceByXYScaleRotation.basisY = - self.basisX * scale.real * planeAngle.imag + self.basisY * scale.imag * planeAngle.real + spaceByXYScaleRotation.basisZ = self.basisZ + return spaceByXYScaleRotation + + def getVector3ByPoint(self, point): + 'Get vector3 by point.' + return self.basisX * point.x + self.basisY * point.y + self.basisZ * point.z + + def normalize(self): + 'Normalize.' + self.basisX.normalize() + self.basisY.normalize() + self.basisZ.normalize() + + def unbuckle( self, maximumUnbuckling, normal ): + 'Unbuckle space.' + unbuckleBasis( self.basisX, maximumUnbuckling, normal ) + unbuckleBasis( self.basisY, maximumUnbuckling, normal ) + + +class XIntersectionIndex: + 'A class to hold the x intersection position and the index of the loop which intersected.' + def __init__( self, index, x ): + 'Initialize.' + self.index = index + self.x = x + + def __cmp__(self, other): + 'Get comparison in order to sort x intersections in ascending order of x.' + if self.x < other.x: + return - 1 + return int( self.x > other.x ) + + def __eq__(self, other): + 'Determine whether this XIntersectionIndex is identical to other one.' + if other == None: + return False + if other.__class__ != self.__class__: + return False + return self.index == other.index and self.x == other.x + + def __ne__(self, other): + 'Determine whether this XIntersectionIndex is not identical to other one.' + return not self.__eq__(other) + + def __repr__(self): + 'Get the string representation of this x intersection.' + return 'XIntersectionIndex index %s; x %s ' % ( self.index, self.x ) diff --git a/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/__init__.py b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/__init__.py new file mode 100644 index 0000000..2dc8ddc --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 2 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/alphabetize.py b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/alphabetize.py new file mode 100644 index 0000000..379d2ef --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/alphabetize.py @@ -0,0 +1,232 @@ +""" +Alphabetize is a script to alphabetize functions and signatures. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +import cStringIO +import os + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def addTogetherList(functionList, togetherLists): + 'Add the togetherList to the togetherLists is the sorted is different.' + sortedList = functionList[:] + sortedList.sort(compareFunctionName) + togetherList = None + for functionIndex in xrange(len(functionList)): + function = functionList[functionIndex] + sorted = sortedList[functionIndex] + if function != sorted: + together = (function, sorted) + if togetherList == None: + togetherList = [] + togetherLists.append(togetherList) + togetherList.append(together) + +def compareFunctionName(first, second): + 'Compare the function names.' + first = getConvertedName(first) + second = getConvertedName(second) + if first < second: + return -1 + return first < second + +def getConvertedName(name): + 'Get converted name with init at the beginning and main at the endCompare the function names.' + if name == 'def __init__': + return 'def !__init__' + if name == 'def main': + return 'def |main' + return name.lower() + +def getFunctionLists(fileName): + 'Get the function lists in the file.' + fileText = archive.getFileText(fileName) + functionList = [] + functionLists = [functionList] + lines = archive.getTextLines(fileText) + for line in lines: + lineStripped = line.strip() + if lineStripped.startswith('def '): + bracketIndex = lineStripped.find('(') + if bracketIndex > -1: + lineStripped = lineStripped[: bracketIndex] + functionList.append(lineStripped) + elif line.startswith('class'): + functionList = [] + functionLists.append(functionList) + return functionLists + +def getFunctionsWithStringByFileName(fileName, searchString): + 'Get the functions with the search string in the file.' + fileText = archive.getFileText(fileName) + functions = [] + lines = archive.getTextLines(fileText) + for line in lines: + lineStripped = line.strip() +# if lineStripped.startswith('def ') and searchString in lineStripped and '=' in lineStripped: + if lineStripped.startswith('def ') and searchString in lineStripped: + if '(self, ' not in lineStripped or lineStripped.count(',') > 1: + functions.append(lineStripped[len('def ') :].strip()) + functions.sort() + return functions + +def getFunctionsWithStringByFileNames(fileNames, searchString): + 'Get the functions with the search string in the files.' + functions = [] + for fileName in fileNames: + functions += getFunctionsWithStringByFileName(fileName, searchString) + functions.sort() + return functions + +def getParameterSequence(functionName): + 'Get the parameter sequence.' + parameterDictionary = {} + parameterSequence = [] + parameterText = functionName[functionName.find('(') + 1 :].replace('xmlElement', 'elementNode') + snippet = Snippet(0, parameterText) + strippedParameters = [] + for parameter in snippet.parameters: + strippedParameter = parameter.strip() + if strippedParameter != 'self': + strippedParameters.append(strippedParameter) + for parameterIndex, parameter in enumerate(strippedParameters): + parameterDictionary[parameter] = parameterIndex + sortedParameters = strippedParameters[:] + sortedParameters.sort() + for sortedParameter in sortedParameters: + parameterSequence.append(parameterDictionary[sortedParameter]) + return parameterSequence + +def getSnippetsByFileName(fileName, functionName): + 'Get the function signature snippets by the file name.' + fileText = archive.getFileText(fileName) + snippets = [] + functionStart = functionName[: functionName.find('(') + 1] + tokenEnd = getTokenEnd(0, fileText, functionStart) + while tokenEnd != -1: + snippet = Snippet(tokenEnd, fileText) + snippets.append(snippet) + tokenEnd = getTokenEnd(snippet.characterIndex, fileText, functionStart) + return snippets + +def getTogetherLists(fileName): + 'Get the lists of the unsorted and sorted functions in the file.' + functionLists = getFunctionLists(fileName) + togetherLists = [] + for functionList in functionLists: + addTogetherList(functionList, togetherLists) + return togetherLists + +def getTokenEnd(characterIndex, fileText, token): + 'Get the token end index for the file text and token.' + tokenIndex = fileText.find(token, characterIndex) + if tokenIndex == -1: + return -1 + return tokenIndex + len(token) + +def printTogetherListsByFileNames(fileNames): + 'Print the together lists of the file names, if the file name has a together list.' + for fileName in fileNames: + togetherLists = getTogetherLists(fileName) + if len(togetherLists) > 0: + for togetherList in togetherLists: + for together in togetherList: + function = together[0] + sorted = together[1] + return + + +class EndCharacterMonad: + 'A monad to return the parent monad when it encounters the end character.' + def __init__(self, endCharacter, parentMonad): + 'Initialize.' + self.endCharacter = endCharacter + self.parentMonad = parentMonad + + def getNextMonad(self, character): + 'Get the next monad.' + self.getSnippet().input.write(character) + if character == self.endCharacter: + return self.parentMonad + return self + + def getSnippet(self): + 'Get the snippet.' + return self.parentMonad.getSnippet() + + +class ParameterMonad: + 'A monad to handle parameters.' + def __init__(self, snippet): + 'Initialize.' + self.snippet = snippet + + def addParameter(self): + 'Add parameter to the snippet.' + parameterString = self.snippet.input.getvalue() + if len(parameterString) != 0: + self.snippet.input = cStringIO.StringIO() + self.snippet.parameters.append(parameterString) + + def getNextMonad(self, character): + 'Get the next monad.' + if character == '"': + self.snippet.input.write(character) + return EndCharacterMonad('"', self) + if character == '"': + self.snippet.input.write(character) + return EndCharacterMonad('"', self) + if character == '(': + self.snippet.input.write(character) + return EndCharacterMonad(')', self) + if character == ')': + self.addParameter() + return None + if character == ',': + self.addParameter() + return self + self.snippet.input.write(character) + return self + + def getSnippet(self): + 'Get the snippet.' + return self.snippet + + +class Snippet: + 'A class to get the variables for a function.' + def __init__(self, characterIndex, fileText): + 'Initialize.' + self.characterIndex = characterIndex + self.input = cStringIO.StringIO() + self.parameters = [] + monad = ParameterMonad(self) + for characterIndex in xrange(self.characterIndex, len(fileText)): + character = fileText[characterIndex] + monad = monad.getNextMonad(character) + if monad == None: + return + + def __repr__(self): + 'Get the string representation of this Snippet.' + return '%s %s' % (self.characterIndex, self.parameters) + + +def main(): + 'Run main function.' +# printTogetherListsByFileNames(archive.getPythonFileNamesExceptInitRecursively('/home/enrique/Desktop/fabmetheus')) + functions = getFunctionsWithStringByFileNames(archive.getPythonFileNamesExceptInitRecursively('/home/enrique/Desktop/fabmetheus'), ', xmlElement') + print(functions) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/fabmetheus_interpret.py b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/fabmetheus_interpret.py new file mode 100644 index 0000000..65de7cf --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/fabmetheus_interpret.py @@ -0,0 +1,132 @@ +""" +Fabmetheus interpret is a fabmetheus utility to interpret a file, turning it into fabmetheus constructive solid geometry xml. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import os +import time + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCarving(fileName): + "Get carving." + pluginModule = getInterpretPlugin(fileName) + if pluginModule == None: + return None + return pluginModule.getCarving(fileName) + +def getGNUTranslatorFilesUnmodified(): + "Get the file types from the translators in the import plugins folder." + return archive.getFilesWithFileTypesWithoutWords(getImportPluginFileNames()) + +def getGNUTranslatorGcodeFileTypeTuples(): + "Get the file type tuples from the translators in the import plugins folder plus gcode." + fileTypeTuples = getTranslatorFileTypeTuples() + fileTypeTuples.append( ('Gcode text files', '*.gcode') ) + fileTypeTuples.sort() + return fileTypeTuples + +def getImportPluginFileNames(): + "Get interpret plugin fileNames." + return archive.getPluginFileNamesFromDirectoryPath( getPluginsDirectoryPath() ) + +def getInterpretPlugin(fileName): + "Get the interpret plugin for the file." + importPluginFileNames = getImportPluginFileNames() + for importPluginFileName in importPluginFileNames: + fileTypeDot = '.' + importPluginFileName + if fileName[ - len(fileTypeDot) : ].lower() == fileTypeDot: + importPluginsDirectoryPath = getPluginsDirectoryPath() + pluginModule = archive.getModuleWithDirectoryPath( importPluginsDirectoryPath, importPluginFileName ) + if pluginModule != None: + return pluginModule + print('Could not find plugin to handle ' + fileName ) + return None + +def getNewRepository(): + 'Get new repository.' + return InterpretRepository() + +def getPluginsDirectoryPath(): + "Get the plugins directory path." + return archive.getInterpretPluginsPath() + +def getTranslatorFileTypeTuples(): + "Get the file types from the translators in the import plugins folder." + importPluginFileNames = getImportPluginFileNames() + fileTypeTuples = [] + for importPluginFileName in importPluginFileNames: + fileTypeTitle = importPluginFileName.upper() + ' files' + fileType = ( fileTypeTitle, '*.' + importPluginFileName ) + fileTypeTuples.append( fileType ) + fileTypeTuples.sort() + return fileTypeTuples + +def getWindowAnalyzeFile(fileName): + "Get file interpretion." + startTime = time.time() + carving = getCarving(fileName) + if carving == None: + return None + interpretGcode = str( carving ) + if interpretGcode == '': + return None + repository = settings.getReadRepository( InterpretRepository() ) + if repository.printInterpretion.value: + print(interpretGcode) + suffixFileName = fileName[ : fileName.rfind('.') ] + '_interpret.' + carving.getInterpretationSuffix() + suffixDirectoryName = os.path.dirname(suffixFileName) + suffixReplacedBaseName = os.path.basename(suffixFileName).replace(' ', '_') + suffixFileName = os.path.join( suffixDirectoryName, suffixReplacedBaseName ) + archive.writeFileText( suffixFileName, interpretGcode ) + print('The interpret file is saved as ' + archive.getSummarizedFileName(suffixFileName) ) + print('It took %s to interpret the file.' % euclidean.getDurationString( time.time() - startTime ) ) + textProgram = repository.textProgram.value + if textProgram == '': + return None + if textProgram == 'webbrowser': + settings.openWebPage(suffixFileName) + return None + textFilePath = '"' + os.path.normpath(suffixFileName) + '"' # " to send in file name with spaces + shellCommand = textProgram + ' ' + textFilePath + print('Sending the shell command:') + print(shellCommand) + commandResult = os.system(shellCommand) + if commandResult != 0: + print('It may be that the system could not find the %s program.' % textProgram ) + print('If so, try installing the %s program or look for another one, like Open Office which can be found at:' % textProgram ) + print('http://www.openoffice.org/') + print('Open office writer can then be started from the command line with the command "soffice -writer".') + + +class InterpretRepository: + "A class to handle the interpret settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.analyze_plugins.interpret.html', self) + self.fileNameInput = settings.FileNameInput().getFromFileName( getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Interpret', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Interpret') + self.activateInterpret = settings.BooleanSetting().getFromValue('Activate Interpret', self, False ) + self.printInterpretion = settings.BooleanSetting().getFromValue('Print Interpretion', self, False ) + self.textProgram = settings.StringSetting().getFromValue('Text Program:', self, 'webbrowser') + self.executeTitle = 'Interpret' + + def execute(self): + "Write button has been clicked." + fileNames = skeinforge_polyfile.getFileOrGcodeDirectory( self.fileNameInput.value, self.fileNameInput.wasCancelled ) + for fileName in fileNames: + getWindowAnalyzeFile(fileName) diff --git a/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/__init__.py b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/__init__.py new file mode 100644 index 0000000..cefa3e7 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/__init__.py @@ -0,0 +1,12 @@ +""" +This page is in the table of contents. +This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +""" +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 3 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/csv.py b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/csv.py new file mode 100644 index 0000000..95886ed --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/csv.py @@ -0,0 +1,159 @@ +""" +This page is in the table of contents. +The csv.py script is an import translator plugin to get a carving from an csv file. + +An import plugin is a script in the interpret_plugins folder which has the function getCarving. It is meant to be run from the interpret tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name. + +The getCarving function takes the file name of an csv file and returns the carving. + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import xml_simple_reader +import sys + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Nophead \nArt of Illusion ' +__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() diff --git a/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/gts.py b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/gts.py new file mode 100644 index 0000000..50ed1d9 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/gts.py @@ -0,0 +1,80 @@ +""" +This page is in the table of contents. +The gts.py script is an import translator plugin to get a carving from an gts file. + +An import plugin is a script in the interpret_plugins folder which has the function getCarving. It is meant to be run from the interpret tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name. + +The getCarving function takes the file name of an gts file and returns the carving. + +The GNU Triangulated Surface (.gts) format is described at: +http://gts.sourceforge.net/reference/gts-surfaces.html#GTS-SURFACE-WRITE + +Quoted from http://gts.sourceforge.net/reference/gts-surfaces.html#GTS-SURFACE-WRITE +"All the lines beginning with GTS_COMMENTS (#!) are ignored. The first line contains three unsigned integers separated by spaces. The first integer is the number of vertexes, nv, the second is the number of edges, ne and the third is the number of faces, nf. + +Follows nv lines containing the x, y and z coordinates of the vertexes. Follows ne lines containing the two indices (starting from one) of the vertexes of each edge. Follows nf lines containing the three ordered indices (also starting from one) of the edges of each face. + +The format described above is the least common denominator to all GTS files. Consistent with an object-oriented approach, the GTS file format is extensible. Each of the lines of the file can be extended with user-specific attributes accessible through the read() and write() virtual methods of each of the objects written (surface, vertexes, edges or faces). When read with different object classes, these extra attributes are just ignored." + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_tools import face +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import gcodec + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Nophead \nArt of Illusion ' +__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 diff --git a/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/obj.py b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/obj.py new file mode 100644 index 0000000..257d82f --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/obj.py @@ -0,0 +1,78 @@ +""" +This page is in the table of contents. +The obj.py script is an import translator plugin to get a carving from an obj file. + +An example obj file is box.obj in the models folder. + +An import plugin is a script in the interpret_plugins folder which has the function getCarving. It is meant to be run from the interpret tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name. + +The getCarving function takes the file name of an obj file and returns the carving. + +From wikipedia, OBJ (or .OBJ) is a geometry definition file format first developed by Wavefront Technologies for its Advanced Visualizer animation package: +http://en.wikipedia.org/wiki/Obj + +The Object File specification is at: +http://local.wasp.uwa.edu.au/~pbourke/dataformats/obj/ + +An excellent link page about obj files is at: +http://people.sc.fsu.edu/~burkardt/data/obj/obj.html + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_tools import face +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import gcodec +from struct import unpack + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Nophead \nArt of Illusion ' +__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] ) ) diff --git a/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/slc.py b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/slc.py new file mode 100644 index 0000000..23b2d19 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/slc.py @@ -0,0 +1,188 @@ +""" +This page is in the table of contents. +The slc.py script is an import translator plugin to get a carving from an [http://rapid.lpt.fi/archives/rp-ml-1999/0713.html slc file]. + +An import plugin is a script in the interpret_plugins folder which has the function getCarving. It is meant to be run from the interpret tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name. + +The getCarving function takes the file name of an slc file and returns the carving. + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import svg_writer +from struct import unpack +import math +import sys + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Nophead \nArt of Illusion ' +__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(' 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() diff --git a/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/stl.py b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/stl.py new file mode 100644 index 0000000..7286297 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/stl.py @@ -0,0 +1,111 @@ +""" +This page is in the table of contents. +The stl.py script is an import translator plugin to get a carving from an stl file. + +An import plugin is a script in the interpret_plugins folder which has the function getCarving. It is meant to be run from the interpret tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name. + +The getCarving function takes the file name of an stl file and returns the carving. + +STL is an inferior triangle surface format, described at: +http://en.wikipedia.org/wiki/STL_(file_format) + +A good triangle surface format is the GNU Triangulated Surface format which is described at: +http://gts.sourceforge.net/reference/gts-surfaces.html#GTS-SURFACE-WRITE + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_tools import face +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import gcodec +from struct import unpack + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Nophead \nArt of Illusion ' +__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] ) ) diff --git a/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/svg.py b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/svg.py new file mode 100644 index 0000000..bfe9e0d --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/svg.py @@ -0,0 +1,109 @@ +""" +This page is in the table of contents. +The svg.py script is an import translator plugin to get a carving from an svg file. This script will read an svg file made by skeinforge or by inkscape. + +An example inkscape svg file is inkscape_star.svg in the models folder. + +An import plugin is a script in the interpret_plugins folder which has the function getCarving. It is meant to be run from the interpret tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name. + +The getCarving function takes the file name of an svg file and returns the carving. + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.svg_reader import SVGReader +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import svg_writer +from fabmetheus_utilities import xml_simple_writer +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Nophead \nArt of Illusion ' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCarving(fileName=''): + 'Get the triangle mesh for the gts file.' + carving = SVGCarving() + carving.parseSVG(fileName, archive.getFileText(fileName)) + return carving + + +class SVGCarving: + 'An svg carving.' + def __init__(self): + 'Add empty lists.' + self.layerHeight = 1.0 + self.maximumZ = - 987654321.0 + self.minimumZ = 987654321.0 + self.svgReader = SVGReader() + + def __repr__(self): + 'Get the string representation of this carving.' + return self.getCarvedSVG() + + def addXML(self, depth, output): + 'Add xml for this object.' + xml_simple_writer.addXMLFromObjects(depth, self.svgReader.loopLayers, output) + + def getCarveBoundaryLayers(self): + 'Get the boundary layers.' + return self.svgReader.loopLayers + + def getCarveCornerMaximum(self): + 'Get the corner maximum of the vertexes.' + return self.cornerMaximum + + def getCarveCornerMinimum(self): + 'Get the corner minimum of the vertexes.' + return self.cornerMinimum + + def getCarvedSVG(self): + 'Get the carved svg text.' + return svg_writer.getSVGByLoopLayers(True, self, self.svgReader.loopLayers) + + def getCarveLayerHeight(self): + 'Get the layer height.' + return self.layerHeight + + def getFabmetheusXML(self): + 'Return the fabmetheus XML.' + return None + + def getInterpretationSuffix(self): + 'Return the suffix for a carving.' + return 'svg' + + def parseSVG(self, fileName, svgText): + 'Parse SVG text and store the layers.' + if svgText == '': + return + self.fileName = fileName + self.svgReader.parseSVG(fileName, svgText) + self.layerHeight = euclidean.getFloatDefaultByDictionary( + self.layerHeight, self.svgReader.sliceDictionary, 'layerHeight') + self.cornerMaximum = Vector3(-987654321.0, -987654321.0, self.maximumZ) + self.cornerMinimum = Vector3(987654321.0, 987654321.0, self.minimumZ) + svg_writer.setSVGCarvingCorners( + self.cornerMaximum, self.cornerMinimum, self.layerHeight, self.svgReader.loopLayers) + + def setCarveImportRadius(self, importRadius): + 'Set the import radius.' + pass + + def setCarveIsCorrectMesh(self, isCorrectMesh): + 'Set the is correct mesh flag.' + pass + + def setCarveLayerHeight(self, layerHeight): + 'Set the layer height.' + self.layerHeight = layerHeight diff --git a/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/xml.py b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/xml.py new file mode 100644 index 0000000..a6df203 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/xml.py @@ -0,0 +1,128 @@ +""" +This page is in the table of contents. +The xml.py script is an import translator plugin to get a carving from an 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 example of an xml boolean geometry format file follows below. + + + + + + + + + + + + + +In the 'fabmetheus' format, all class names are lower case. The defined geometric objects are cube, cylinder, difference, group, sphere, trianglemesh and union. The id attribute is not necessary. The default matrix is a four by four identity matrix. The attributes of the cube, cylinder and sphere default to one. The attributes of the vertexes in the triangle mesh default to zero. The boolean solids are difference, intersection and union. The difference solid is the first solid minus the remaining solids. The combined_shape.xml example in the xml_models folder in the models folder is pasted below. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +The 'fabmetheus' xml format is the preferred skeinforge format. When the Interpret button in the Interpret tool in Analyze is clicked, any xml format for which there is a plugin will be converted to the 'fabmetheus' format. + +There is a plugin for the 'Art of Illusion' xml format. 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, the artofillusion plugin will ignore the light and camera. If you want to fabricate more than one object at a time, you can have multiple objects in the Art of Illusion scene and they will all be carved, then fabricated together. + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.xml_simple_reader import DocumentNode +from fabmetheus_utilities import archive +from fabmetheus_utilities import gcodec +import os +import sys + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Nophead \nArt of Illusion ' +__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 xml file." + xmlText = archive.getFileText(fileName) + if xmlText == '': + return None + xmlParser = DocumentNode(fileName, xmlText) + lowerLocalName = xmlParser.getDocumentElement().getNodeName().lower() + pluginModule = archive.getModuleWithDirectoryPath( getPluginsDirectoryPath(), lowerLocalName ) + if pluginModule == None: + return None + return pluginModule.getCarvingFromParser( xmlParser ) + +def getPluginsDirectoryPath(): + "Get the plugins directory path." + return archive.getInterpretPluginsPath('xml_plugins') + +def main(): + "Display the inset dialog." + if len(sys.argv) > 1: + getCarving(' '.join(sys.argv[1 :])) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/xml_plugins/__init__.py b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/xml_plugins/__init__.py new file mode 100644 index 0000000..58ec332 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/xml_plugins/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 4 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/xml_plugins/artofillusion.py b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/xml_plugins/artofillusion.py new file mode 100644 index 0000000..047b4b1 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/xml_plugins/artofillusion.py @@ -0,0 +1,194 @@ +""" +This page is in the table of contents. +The xml.py script is an import translator plugin to get a carving from an Art of Illusion xml file. + +An import plugin is a script in the interpret_plugins folder which has the function getCarving. It is meant to be run from the interpret tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name. + +The getCarving function takes the file name of an xml file and returns the carving. + +An xml file can be exported from Art of Illusion by going to the "File" menu, then going into the "Export" menu item, then picking the XML choice. This will bring up the XML file chooser window, choose a place to save the file then click "OK". Leave the "compressFile" checkbox unchecked. All the objects from the scene will be exported, this plugin will ignore the light and camera. If you want to fabricate more than one object at a time, you can have multiple objects in the Art of Illusion scene and they will all be carved, then fabricated together. + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_tools import face +from fabmetheus_utilities.geometry.geometry_utilities import boolean_geometry +from fabmetheus_utilities.geometry.geometry_utilities import boolean_solid +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities.geometry.solids import cube +from fabmetheus_utilities.geometry.solids import cylinder +from fabmetheus_utilities.geometry.solids import group +from fabmetheus_utilities.geometry.solids import sphere +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Nophead \nArt of Illusion ' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCarvableObject(elementNode, globalObject, object): + "Get new carvable object info." + object.xmlObject = globalObject() + object.xmlObject.elementNode = object + object.attributes['id'] = elementNode.getFirstChildByLocalName('name').getTextContent() + coords = elementNode.getFirstChildByLocalName('coords') + transformElementNode = getTransformElementNode(coords, 'transformFrom') + if len(transformElementNode.attributes) < 16: + transformElementNode = getTransformElementNode(coords, 'transformTo') + matrix.setElementNodeDictionaryMatrix(object, object.xmlObject.matrix4X4.getFromElementNode(transformElementNode, '')) + return object.xmlObject + +def getCarvingFromParser( xmlParser ): + "Get the carving for the parser." + booleanGeometry = boolean_geometry.BooleanGeometry() + artOfIllusionElement = xmlParser.getDocumentElement() + artOfIllusionElement.xmlObject = booleanGeometry + euclidean.removeElementsFromDictionary( artOfIllusionElement.attributes, ['fileversion', 'xmlns:bf'] ) + sceneElement = artOfIllusionElement.getFirstChildByLocalName('Scene') + elementNodes = sceneElement.getFirstChildByLocalName('objects').getChildElementsByLocalName('bf:Elem') + for elementNode in elementNodes: + processAppendElementNode(booleanGeometry.archivableObjects, elementNode, artOfIllusionElement) + return booleanGeometry + +def getTransformElementNode( coords, transformName ): + "Get the transform attributes." + transformElementNode = coords.getFirstChildByLocalName( transformName ) + if len( transformElementNode.attributes ) < 16: + if 'bf:ref' in transformElementNode.attributes: + idReference = transformElementNode.attributes['bf:ref'] + return coords.getDocumentElement().getSubChildWithID( idReference ) + return transformElementNode + +def processAppendElementNode(archivableObjects, elementNode, parentNode): + "Add the object info if it is carvable." + if elementNode == None: + return + object = elementNode.getFirstChildByLocalName('object') + if 'bf:type' not in object.attributes: + return + shapeType = object.attributes['bf:type'] + if shapeType not in globalCarvableClassObjectTable: + return + carvableClassObject = globalCarvableClassObjectTable[ shapeType ] + archivableObject = getCarvableObject(elementNode, carvableClassObject, object) + archivableObject.elementNode.attributes['visible'] = elementNode.attributes['visible'] + archivableObject.setToArtOfIllusionDictionary() + archivableObject.elementNode.parentNode = parentNode + archivableObjects.append(archivableObject) + +def processElementNode(elementNode): + "Process the xml element." + evaluate.processArchivable(group.Group, elementNode) + +def removeListArtOfIllusionFromDictionary( dictionary, scrubKeys ): + "Remove the list and art of illusion keys from the dictionary." + euclidean.removeElementsFromDictionary( dictionary, ['bf:id', 'bf:type'] ) + euclidean.removeElementsFromDictionary( dictionary, scrubKeys ) + + +class BooleanSolid( boolean_solid.BooleanSolid ): + "An Art of Illusion CSG object info." + def setToArtOfIllusionDictionary(self): + "Set the shape of this carvable object info." + processAppendElementNode(self.archivableObjects, self.elementNode.getFirstChildByLocalName('obj1'), self.elementNode) + processAppendElementNode(self.archivableObjects, self.elementNode.getFirstChildByLocalName('obj2'), self.elementNode) + operationString = self.elementNode.attributes['operation'] + self.operationFunction = { '0': self.getUnion, '1': self.getIntersection, '2': self.getDifference, '3': self.getDifference }[ operationString ] + if operationString == '3': + self.archivableObjects.reverse() + removeListArtOfIllusionFromDictionary( self.elementNode.attributes, ['operation'] ) + + +class Cube( cube.Cube ): + "An Art of Illusion Cube object." + def setToArtOfIllusionDictionary(self): + "Set the shape of this carvable object info." + self.inradius = Vector3( + float( self.elementNode.attributes['halfx'] ), + float( self.elementNode.attributes['halfy'] ), + float( self.elementNode.attributes['halfz'] ) ) + self.elementNode.attributes['inradius.x'] = self.elementNode.attributes['halfx'] + self.elementNode.attributes['inradius.y'] = self.elementNode.attributes['halfy'] + self.elementNode.attributes['inradius.z'] = self.elementNode.attributes['halfz'] + removeListArtOfIllusionFromDictionary( self.elementNode.attributes, ['halfx', 'halfy', 'halfz'] ) + self.createShape() + + +class Cylinder(cylinder.Cylinder): + "An Art of Illusion Cylinder object." + def setToArtOfIllusionDictionary(self): + "Set the shape of this carvable object info." + self.inradius = Vector3() + self.inradius.x = float(self.elementNode.attributes['rx']) + self.inradius.y = float(self.elementNode.attributes['rz']) + self.inradius.z = float(self.elementNode.attributes['height']) + self.topOverBottom = float(self.elementNode.attributes['ratio']) + self.elementNode.attributes['radius.x'] = self.elementNode.attributes['rx'] + self.elementNode.attributes['radius.y'] = self.elementNode.attributes['rz'] + self.elementNode.attributes['topOverBottom'] = self.elementNode.attributes['ratio'] + xmlObject = self.elementNode.xmlObject + xmlObject.matrix4X4 = xmlObject.matrix4X4.getOtherTimesSelf(matrix.getDiagonalSwitchedTetragrid(90.0, [0, 2])) + removeListArtOfIllusionFromDictionary(self.elementNode.attributes, ['rx', 'rz', 'ratio']) + self.createShape() + + +class Group( group.Group ): + "An Art of Illusion Group object." + def setToArtOfIllusionDictionary(self): + "Set the shape of this group." + childNodesElement = self.elementNode.parentNode.getFirstChildByLocalName('children') + childNodes = childNodesElement.getChildElementsByLocalName('bf:Elem') + for childNode in childNodes: + processAppendElementNode(self.archivableObjects, childNode, self.elementNode) + removeListArtOfIllusionFromDictionary( self.elementNode.attributes, [] ) + +class Sphere( sphere.Sphere ): + "An Art of Illusion Sphere object." + def setToArtOfIllusionDictionary(self): + "Set the shape of this carvable object." + self.radius = Vector3( + float( self.elementNode.attributes['rx'] ), + float( self.elementNode.attributes['ry'] ), + float( self.elementNode.attributes['rz'] ) ) + self.elementNode.attributes['radius.x'] = self.elementNode.attributes['rx'] + self.elementNode.attributes['radius.y'] = self.elementNode.attributes['ry'] + self.elementNode.attributes['radius.z'] = self.elementNode.attributes['rz'] + removeListArtOfIllusionFromDictionary( self.elementNode.attributes, ['rx', 'ry', 'rz'] ) + self.createShape() + + +class TriangleMesh(triangle_mesh.TriangleMesh): + "An Art of Illusion triangle mesh object." + def setToArtOfIllusionDictionary(self): + "Set the shape of this carvable object info." + vertexElement = self.elementNode.getFirstChildByLocalName('vertex') + vertexPointElements = vertexElement.getChildElementsByLocalName('bf:Elem') + for vertexPointElement in vertexPointElements: + coordinateElement = vertexPointElement.getFirstChildByLocalName('r') + vertex = Vector3( float( coordinateElement.attributes['x'] ), float( coordinateElement.attributes['y'] ), float( coordinateElement.attributes['z'] ) ) + self.vertexes.append(vertex) + edgeElement = self.elementNode.getFirstChildByLocalName('edge') + edgeSubelements = edgeElement.getChildElementsByLocalName('bf:Elem') + for edgeSubelementIndex in xrange( len( edgeSubelements ) ): + edgeSubelement = edgeSubelements[ edgeSubelementIndex ] + vertexIndexes = [ int( edgeSubelement.attributes['v1'] ), int( edgeSubelement.attributes['v2'] ) ] + edge = face.Edge().getFromVertexIndexes( edgeSubelementIndex, vertexIndexes ) + self.edges.append( edge ) + faceElement = self.elementNode.getFirstChildByLocalName('face') + faceSubelements = faceElement.getChildElementsByLocalName('bf:Elem') + for faceSubelementIndex in xrange( len( faceSubelements ) ): + faceSubelement = faceSubelements[ faceSubelementIndex ] + edgeIndexes = [ int( faceSubelement.attributes['e1'] ), int( faceSubelement.attributes['e2'] ), int( faceSubelement.attributes['e3'] ) ] + self.faces.append( face.Face().getFromEdgeIndexes( edgeIndexes, self.edges, faceSubelementIndex ) ) + removeListArtOfIllusionFromDictionary( self.elementNode.attributes, ['closed', 'smoothingMethod'] ) + + +globalCarvableClassObjectTable = { 'CSGObject' : BooleanSolid, 'Cube' : Cube, 'Cylinder' : Cylinder, 'artofillusion.object.NullObject' : Group, 'Sphere' : Sphere, 'TriangleMesh' : TriangleMesh } diff --git a/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/xml_plugins/fabmetheus.py b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/xml_plugins/fabmetheus.py new file mode 100644 index 0000000..2f5f028 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/interpret_plugins/xml_plugins/fabmetheus.py @@ -0,0 +1,116 @@ +""" +This page is in the table of contents. +The xml.py script is an import translator plugin to get a carving from an Art of Illusion xml file. + +An import plugin is a script in the interpret_plugins folder which has the function getCarving. It is meant to be run from the interpret tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name. + +The getCarving function takes the file name of an xml file and returns the carving. + +An xml file can be exported from Art of Illusion by going to the "File" menu, then going into the "Export" menu item, then picking the XML choice. This will bring up the XML file chooser window, choose a place to save the file then click "OK". Leave the "compressFile" checkbox unchecked. All the objects from the scene will be exported, this plugin will ignore the light and camera. If you want to fabricate more than one object at a time, you can have multiple objects in the Art of Illusion scene and they will all be carved, then fabricated together. + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools.interpret_plugins import xml +from fabmetheus_utilities.geometry.geometry_utilities import boolean_geometry +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.solids import group +from fabmetheus_utilities import archive +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from fabmetheus_utilities import xml_simple_reader +import os +import sys +import traceback + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Nophead \nArt of Illusion ' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCarvingFromParser(xmlParser): + "Get the carving for the parser." + booleanGeometryElement = xmlParser.getDocumentElement() + booleanGeometryElement.xmlObject = boolean_geometry.BooleanGeometry() + booleanGeometryElement.xmlProcessor = XMLBooleanGeometryProcessor() + booleanGeometryElement.xmlProcessor.processChildNodes(booleanGeometryElement) + return booleanGeometryElement.xmlObject + +def processElementNode(elementNode): + "Process the xml element." + evaluate.processArchivable(group.Group, elementNode) + + +class XMLBooleanGeometryProcessor(): + "A class to process xml boolean geometry elements." + def __init__(self): + "Initialize processor." + self.functions = [] + self.manipulationMatrixDictionary = archive.getGeometryDictionary('manipulation_matrix') + self.manipulationPathDictionary = archive.getGeometryDictionary('manipulation_paths') + self.manipulationShapeDictionary = archive.getGeometryDictionary('manipulation_shapes') + self.namePathDictionary = {} + self.namePathDictionary.update(evaluate.globalCreationDictionary) + self.namePathDictionary.update(archive.getGeometryDictionary('manipulation_meta')) + self.namePathDictionary.update(self.manipulationMatrixDictionary) + self.namePathDictionary.update(self.manipulationPathDictionary) + self.namePathDictionary.update(self.manipulationShapeDictionary) + archive.addToNamePathDictionary(archive.getGeometryToolsPath(), self.namePathDictionary) + archive.addToNamePathDictionary(archive.getGeometryToolsPath('path_elements'), self.namePathDictionary) + archive.addToNamePathDictionary(archive.getGeometryPath('solids'), self.namePathDictionary) + archive.addToNamePathDictionary(archive.getGeometryPath('statements'), self.namePathDictionary) + archive.addToNamePathDictionary(xml.getPluginsDirectoryPath(), self.namePathDictionary) + + def __repr__(self): + 'Get the string representation of this XMLBooleanGeometryProcessor.' + return 'XMLBooleanGeometryProcessor with %s functions.' % len(self.functions) + + def convertElementNode(self, elementNode, geometryOutput): + "Convert the xml element." + geometryOutputKeys = geometryOutput.keys() + if len( geometryOutputKeys ) < 1: + return None + firstKey = geometryOutputKeys[0] + lowerLocalName = firstKey.lower() + if lowerLocalName not in self.namePathDictionary: + return None + pluginModule = archive.getModuleWithPath( self.namePathDictionary[ lowerLocalName ] ) + if pluginModule == None: + return None + elementNode.localName = lowerLocalName + return pluginModule.convertElementNode(elementNode, geometryOutput[ firstKey ]) + + def createChildNodes( self, geometryOutput, parentNode ): + "Create childNodes for the parentNode." + for geometryOutputChild in geometryOutput: + childNode = xml_simple_reader.ElementNode() + childNode.setParentAddToChildNodes( parentNode ) + self.convertElementNode(childNode, geometryOutputChild) + + def processChildNodes(self, elementNode): + "Process the childNodes of the xml element." + for childNode in elementNode.childNodes: + self.processElementNode(childNode) + + def processElementNode(self, elementNode): + 'Process the xml element.' + lowerLocalName = elementNode.getNodeName().lower() + if lowerLocalName not in self.namePathDictionary: + return None + pluginModule = archive.getModuleWithPath(self.namePathDictionary[lowerLocalName]) + if pluginModule == None: + return None + try: + return pluginModule.processElementNode(elementNode) + except: + print('Warning, could not processElementNode in fabmetheus for:') + print(pluginModule) + print(elementNode) + traceback.print_exc(file=sys.stdout) + return None diff --git a/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/prepare.py b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/prepare.py new file mode 100644 index 0000000..bac2757 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/prepare.py @@ -0,0 +1,86 @@ +""" +Prepare is a script to remove the generated files, run wikifier, and finally zip the package. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities.fabmetheus_tools import wikifier +import os + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def prepareWikify(): + 'Remove generated files, then wikify the file comments.' + removeGeneratedFiles() + wikifier.main() + removeZip() + +def removeCSVFile(csvFilePath): + 'Remove csv file.' + if 'alterations' in csvFilePath and 'example_' not in csvFilePath: + os.remove(csvFilePath) + print('removeGeneratedFiles deleted ' + csvFilePath) + +def removeGcodeFile(gcodeFilePath): + 'Remove gcode file.' + if 'alterations' not in gcodeFilePath: + os.remove(gcodeFilePath) + print('removeGeneratedFiles deleted ' + gcodeFilePath) + return + if 'example_' not in gcodeFilePath: + os.remove(gcodeFilePath) + print('removeGeneratedFiles deleted ' + gcodeFilePath) + +def removeGeneratedFiles(): + 'Remove generated files.' + csvFilePaths = archive.getFilesWithFileTypesWithoutWordsRecursively(['csv']) + for csvFilePath in csvFilePaths: + removeCSVFile(csvFilePath) + gcodeFilePaths = archive.getFilesWithFileTypesWithoutWordsRecursively(['gcode']) + for gcodeFilePath in gcodeFilePaths: + removeGcodeFile(gcodeFilePath) + svgFilePaths = archive.getFilesWithFileTypesWithoutWordsRecursively(['svg']) + for svgFilePath in svgFilePaths: + removeSVGFile(svgFilePath) + xmlFilePaths = archive.getFilesWithFileTypesWithoutWordsRecursively(['xml']) + for xmlFilePath in xmlFilePaths: + removeXMLFile(xmlFilePath) + archive.removeBackupFilesByTypes(['gcode', 'svg', 'xml']) + +def removeSVGFile(svgFilePath): + 'Remove svg file.' + if archive.getEndsWithList(svgFilePath, ['_bottom.svg', '_carve.svg', '_chop.svg', '_cleave.svg', '_scale.svg', '_vectorwrite.svg']): + os.remove(svgFilePath) + print('removeGeneratedFiles deleted ' + svgFilePath) + +def removeXMLFile(xmlFilePath): + 'Remove xml file.' + if archive.getEndsWithList(xmlFilePath, ['_interpret.xml']): + os.remove(xmlFilePath) + print('removeGeneratedFiles deleted ' + xmlFilePath) + +def removeZip(): + 'Remove the zip file, then generate a new one.zip -r reprap_python_beanshell * -x \*.pyc \*~' + zipName = 'reprap_python_beanshell' + zipNameExtension = zipName + '.zip' + if zipNameExtension in os.listdir(os.getcwd()): + os.remove(zipNameExtension) + shellCommand = 'zip -r %s * -x \*.pyc \*~' % zipName + if os.system(shellCommand) != 0: + print('Failed to execute the following command in removeZip in prepare.') + print(shellCommand) + +def main(): + 'Run main function.' + prepareWikify() + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/wikifier.py b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/wikifier.py new file mode 100644 index 0000000..50e0233 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/fabmetheus_tools/wikifier.py @@ -0,0 +1,215 @@ +""" +Wikifier is a script to add spaces to the pydoc files and move them to the documentation folder. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +import cStringIO +import os + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalWikiLinkStart = '[', linkStartIndex, squareEndBracketIndex) + greaterThanIndexPlusOne = greaterThanIndex + 1 + closeATagIndex = line.find('', greaterThanIndexPlusOne, squareEndBracketIndex) + linkText = line[closeATagIndex + len('') + 1: squareEndBracketIndex] + linkLine = line[: linkStartIndex] + line[linkStartIndex + 1: greaterThanIndexPlusOne] + linkText + '' + line[squareEndBracketIndex + 1 :] + return linkLine + +def getNavigationHypertext(fileText, transferredFileNameIndex, transferredFileNames): + 'Get the hypertext help with navigation lines.' + helpTextEnd = fileText.find('

') + helpTextStart = fileText.find('

') + helpText = fileText[helpTextStart : helpTextEnd] + lines = archive.getTextLines(helpText) + headings = [] + headingLineTable = {} + for line in lines: + addToHeadings(headingLineTable, headings, line) + headingsToBeenAdded = True + output = cStringIO.StringIO() + for line in lines: + if line[: 2] == '==': + if headingsToBeenAdded: + output.write('
\n') + for heading in headings: + heading.addToOutput(output) + output.write('
\n') + headingsToBeenAdded = False + if line in headingLineTable: + line = headingLineTable[line] + if '<a href=' in line: + line = line.replace('<', '<').replace('>', '>') + while globalWikiLinkStart in line and ']' in line: + line = getLinkLine(line) + output.write(line + '\n') + helpText = output.getvalue() + previousFileName = 'contents.html' + previousIndex = transferredFileNameIndex - 1 + if previousIndex >= 0: + previousFileName = transferredFileNames[previousIndex] + previousLinkText = 'Previous' % previousFileName + nextLinkText = getNextLinkText(transferredFileNames, transferredFileNameIndex + 1) + navigationLine = getNavigationLine('Contents', previousLinkText, nextLinkText) + helpText = navigationLine + helpText + '
\n
\n' + navigationLine + '


\n' + return fileText[: helpTextStart] + helpText + fileText[helpTextEnd :] + +def getNavigationLine(contentsLinkText, previousLinkText, nextLinkText): + 'Get the wrapped pydoc hypertext help.' + return '

\n%s / %s / %s\n

\n' % (previousLinkText, nextLinkText, contentsLinkText) + +def getNextLinkText(hypertextFiles, nextIndex): + 'Get the next link text.' + nextFileName = 'contents.html' + if nextIndex < len(hypertextFiles): + nextFileName = hypertextFiles[nextIndex] + return 'Next' % nextFileName + +def getWrappedHypertext(fileText, hypertextFileIndex, hypertextFiles): + 'Get the wrapped pydoc hypertext help.' + helpTextEnd = fileText.find('

') + if helpTextEnd < 0: + print('Failed to find the helpTextEnd in getWrappedHypertext in docwrap.') + helpTextStart = fileText.find('

') + if helpTextStart < 0: + print('Failed to find the helpTextStart in getWrappedHypertext in docwrap.') + helpText = fileText[helpTextStart : helpTextEnd] + helpText = helpText.replace(' ', ' ') + return fileText[: helpTextStart] + helpText + fileText[helpTextEnd :] + +def readWriteDeleteHypertextHelp(documentDirectoryPath, hypertextFileIndex, hypertextFiles, transferredFileNames): + 'Read the pydoc hypertext help documents, write them in the documentation folder then delete the originals.' + fileName = os.path.basename(hypertextFiles[hypertextFileIndex]) + print('readWriteDeleteHypertextHelp ' + fileName) + filePath = os.path.join(documentDirectoryPath, fileName) + fileText = archive.getFileText(fileName) + fileText = getWrappedHypertext(fileText, hypertextFileIndex, hypertextFiles) + if fileText.find('This page is in the table of contents.') > - 1: + fileText = fileText.replace('This page is in the table of contents.', '') + transferredFileNames.append(fileName) + archive.writeFileText(filePath, fileText) + os.remove(fileName) + +def readWriteNavigationHelp(documentDirectoryPath, transferredFileNameIndex, transferredFileNames): + 'Read the hypertext help documents, and add the navigation lines to them.' + fileName = os.path.basename(transferredFileNames[transferredFileNameIndex]) + print('readWriteNavigationHelp ' + fileName) + filePath = os.path.join(documentDirectoryPath, fileName) + fileText = archive.getFileText(filePath) + fileText = getNavigationHypertext(fileText, transferredFileNameIndex, transferredFileNames) + archive.writeFileText(filePath, fileText) + +def removeFilesInDirectory(directoryPath): + 'Remove all the files in a directory.' + fileNames = os.listdir(directoryPath) + for fileName in fileNames: + filePath = os.path.join(directoryPath, fileName) + os.remove(filePath) + +def writeContentsFile(documentDirectoryPath, hypertextFiles): + 'Write the contents file.' + output = cStringIO.StringIO() + output.write('\n \n Contents\n \n \n') + navigationLine = getNavigationLine('Contents', 'Previous', getNextLinkText(hypertextFiles, 0)) + output.write(navigationLine) + for hypertextFile in hypertextFiles: + writeContentsLine(hypertextFile, output) + output.write(navigationLine) + output.write(' \n\n') + filePath = os.path.join( documentDirectoryPath, 'contents.html') + archive.writeFileText(filePath, output.getvalue()) + +def writeContentsLine(hypertextFile, output): + 'Write a line of the contents file.' + summarizedFileName = hypertextFile[: hypertextFile.rfind('.')] + numberOfDots = summarizedFileName.count('.') + prefixSpaces = '  ' * numberOfDots + if numberOfDots > 0: + summarizedFileName = summarizedFileName[summarizedFileName.rfind('.') + 1 :] + capitalizedSummarizedFileName = settings.getEachWordCapitalized(summarizedFileName) + output.write('%s%s
\n' % (prefixSpaces, hypertextFile, capitalizedSummarizedFileName)) + +def writeHypertext(): + 'Run pydoc, then read, write and delete each of the files.' + shellCommand = 'pydoc -w ./' + commandResult = os.system(shellCommand) + if commandResult != 0: + print('Failed to execute the following command in writeHypertext in docwrap.') + print(shellCommand) + hypertextFiles = archive.getFilesWithFileTypeWithoutWords('html') + if len( hypertextFiles ) <= 0: + print('Failed to find any help files in writeHypertext in docwrap.') + return + documentDirectoryPath = archive.getAbsoluteFolderPath( hypertextFiles[0], 'documentation') + removeFilesInDirectory(documentDirectoryPath) + sortedReplaceFiles = [] + for hypertextFile in hypertextFiles: + sortedReplaceFiles.append(hypertextFile.replace('.html', '. html')) + sortedReplaceFiles.sort() + hypertextFiles = [] + for sortedReplaceFile in sortedReplaceFiles: + hypertextFiles.append(sortedReplaceFile.replace('. html', '.html')) + transferredFileNames = [] + for hypertextFileIndex in xrange(len(hypertextFiles)): + readWriteDeleteHypertextHelp(documentDirectoryPath, hypertextFileIndex, hypertextFiles, transferredFileNames) + for transferredFileNameIndex in xrange(len(transferredFileNames)): + readWriteNavigationHelp(documentDirectoryPath, transferredFileNameIndex, transferredFileNames) + writeContentsFile(documentDirectoryPath, transferredFileNames) + print('%s files were wrapped.' % len(transferredFileNames)) + + +class Heading: + 'A class to hold the heading and subheadings.' + def __init__(self, depth=0): + 'Initialize.' + self.depth = depth + + def addToOutput(self, output): + 'Add to the output.' + line = '  ' * self.depth + '%s
\n' % (self.name, self.name) + output.write(line) + + def getFromLine(self, headingLineTable, line): + 'Get the heading from a line.' + heading = 'h%s' % (self.depth + 2) + nextLine = '\n


\n' + if self.depth > 0: + nextLine = '\n' + self.name = line.replace('=', '').replace('
', '') + name = self.name + headingLine = '<%s>%s%s' % (name, name, heading, name, heading, nextLine) + headingLineTable[line] = headingLine + return self + + +def main(): + 'Display the craft dialog.' + writeHypertext() + +if __name__ == '__main__': + main() diff --git a/SkeinPyPy/fabmetheus_utilities/fonts/gentium_basic_regular.svg b/SkeinPyPy/fabmetheus_utilities/fonts/gentium_basic_regular.svg new file mode 100644 index 0000000..418f023 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/fonts/gentium_basic_regular.svg @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SkeinPyPy/fabmetheus_utilities/gcodec.py b/SkeinPyPy/fabmetheus_utilities/gcodec.py new file mode 100644 index 0000000..9ec90f9 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/gcodec.py @@ -0,0 +1,427 @@ +""" +Gcodec is a collection of utilities to decode and encode gcode. + +To run gcodec, install python 2.x on your machine, which is avaliable from http://www.python.org/download/ + +Then in the folder which gcodec is in, type 'python' in a shell to run the python interpreter. Finally type 'from gcodec import *' to import this program. + +Below is an example of gcodec use. This example is run in a terminal in the folder which contains gcodec and Screw Holder Bottom_export.gcode. + +>>> from gcodec import * +>>> getFileText('Screw Holder Bottom_export.gcode') +'G90\nG21\nM103\nM105\nM106\nM110 S60.0\nM111 S30.0\nM108 S210.0\nM104 S235.0\nG1 X0.37 Y-4.07 Z1.9 F60.0\nM101\n +.. +many lines of text +.. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +import cStringIO +import math +import os +import sys +import traceback + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def addLineAndNewlineIfNecessary(line, output): + 'Add the line and if the line does not end with a newline add a newline.' + output.write(line) + if len(line) < 1: + return + if not line.endswith('\n'): + output.write('\n') + +def addLinesToCString(cString, lines): + 'Add lines which have something to cStringIO.' + for line in lines: + if line != '': + cString.write(line + '\n') + +def getArcDistance(relativeLocation, splitLine): + 'Get arc distance.' + halfPlaneLineDistance = 0.5 * abs(relativeLocation.dropAxis()) + radius = getDoubleFromCharacterSplitLine('R', splitLine) + if radius == None: + iFloat = getDoubleFromCharacterSplitLine('I', splitLine) + jFloat = getDoubleFromCharacterSplitLine('J', splitLine) + radius = abs(complex(iFloat, jFloat)) + angle = 0.0 + if radius > 0.0: + halfPlaneLineDistanceOverRadius = halfPlaneLineDistance / radius + if halfPlaneLineDistance < radius: + angle = 2.0 * math.asin(halfPlaneLineDistanceOverRadius) + else: + angle = math.pi * halfPlaneLineDistanceOverRadius + return abs(complex(angle * radius, relativeLocation.z)) + +def getDoubleAfterFirstLetter(word): + 'Get the double value of the word after the first letter.' + return float(word[1 :]) + +def getDoubleForLetter(letter, splitLine): + 'Get the double value of the word after the first occurence of the letter in the split line.' + return getDoubleAfterFirstLetter(splitLine[getIndexOfStartingWithSecond(letter, splitLine)]) + +def getDoubleFromCharacterSplitLine(character, splitLine): + 'Get the double value of the string after the first occurence of the character in the split line.' + indexOfCharacter = getIndexOfStartingWithSecond(character, splitLine) + if indexOfCharacter < 0: + return None + floatString = splitLine[indexOfCharacter][1 :] + try: + return float(floatString) + except ValueError: + return None + +def getDoubleFromCharacterSplitLineValue(character, splitLine, value): + 'Get the double value of the string after the first occurence of the character in the split line, if it does not exist return the value.' + splitLineFloat = getDoubleFromCharacterSplitLine(character, splitLine) + if splitLineFloat == None: + return value + return splitLineFloat + +def getFeedRateMinute(feedRateMinute, splitLine): + 'Get the feed rate per minute if the split line has a feed rate.' + indexOfF = getIndexOfStartingWithSecond('F', splitLine) + if indexOfF > 0: + return getDoubleAfterFirstLetter( splitLine[indexOfF] ) + return feedRateMinute + +def getFirstWord(splitLine): + 'Get the first word of a split line.' + if len(splitLine) > 0: + return splitLine[0] + return '' + +def getFirstWordFromLine(line): + 'Get the first word of a line.' + return getFirstWord(line.split()) + +def getFirstWordIndexReverse(firstWord, lines, startIndex): + 'Parse gcode in reverse order until the first word if there is one, otherwise return -1.' + for lineIndex in xrange(len(lines) - 1, startIndex - 1, -1): + if firstWord == getFirstWord(getSplitLineBeforeBracketSemicolon(lines[lineIndex])): + return lineIndex + return -1 + +def getGcodeFileText(fileName, gcodeText): + 'Get the gcode text from a file if it the gcode text is empty and if the file is a gcode file.' + if gcodeText != '': + return gcodeText + if fileName.endswith('.gcode'): + return archive.getFileText(fileName) + return '' + +def getGcodeWithoutDuplication(duplicateWord, gcodeText): + 'Get gcode text without duplicate first words.' + lines = archive.getTextLines(gcodeText) + oldWrittenLine = None + output = cStringIO.StringIO() + for line in lines: + firstWord = getFirstWordFromLine(line) + if firstWord == duplicateWord: + if line != oldWrittenLine: + output.write(line + '\n') + oldWrittenLine = line + else: + if len(line) > 0: + output.write(line + '\n') + return output.getvalue() + +def getIndexOfStartingWithSecond(letter, splitLine): + 'Get index of the first occurence of the given letter in the split line, starting with the second word. Return - 1 if letter is not found' + for wordIndex in xrange( 1, len(splitLine) ): + word = splitLine[ wordIndex ] + firstLetter = word[0] + if firstLetter == letter: + return wordIndex + return - 1 + +def getLineWithValueString(character, line, splitLine, valueString): + 'Get the line with a valueString.' + roundedValueString = character + valueString + indexOfValue = getIndexOfStartingWithSecond(character, splitLine) + if indexOfValue == -1: + return line + ' ' + roundedValueString + word = splitLine[indexOfValue] + return line.replace(word, roundedValueString) + +def getLocationFromSplitLine(oldLocation, splitLine): + 'Get the location from the split line.' + if oldLocation == None: + oldLocation = Vector3() + return Vector3( + getDoubleFromCharacterSplitLineValue('X', splitLine, oldLocation.x), + getDoubleFromCharacterSplitLineValue('Y', splitLine, oldLocation.y), + getDoubleFromCharacterSplitLineValue('Z', splitLine, oldLocation.z)) + +def getRotationBySplitLine(splitLine): + 'Get the complex rotation from the split gcode line.' + return complex(splitLine[1].replace('(', '').replace(')', '')) + +def getSplitLineBeforeBracketSemicolon(line): + 'Get the split line before a bracket or semicolon.' + if ';' in line: + line = line[: line.find(';')] + bracketIndex = line.find('(') + if bracketIndex > 0: + return line[: bracketIndex].split() + return line.split() + +def getStringFromCharacterSplitLine(character, splitLine): + 'Get the string after the first occurence of the character in the split line.' + indexOfCharacter = getIndexOfStartingWithSecond(character, splitLine) + if indexOfCharacter < 0: + return None + return splitLine[indexOfCharacter][1 :] + +def getTagBracketedLine(tagName, value): + 'Get line with a begin tag, value and end tag.' + return '(<%s> %s )' % (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('()') + if extruderInitializationIndex == -1: + return False + return gcodeText.find(getTagBracketedProcedure(procedure), 0, extruderInitializationIndex) != -1 + +def isProcedureDoneOrFileIsEmpty(gcodeText, procedure): + 'Determine if the procedure has been done on the gcode text or the file is empty.' + if gcodeText == '': + return True + return isProcedureDone(gcodeText, procedure) + +def isThereAFirstWord(firstWord, lines, startIndex): + 'Parse gcode until the first word if there is one.' + for lineIndex in xrange(startIndex, len(lines)): + line = lines[lineIndex] + splitLine = getSplitLineBeforeBracketSemicolon(line) + if firstWord == getFirstWord(splitLine): + return True + return False + + +class BoundingRectangle: + 'A class to get the corners of a gcode text.' + def getFromGcodeLines(self, lines, radius): + 'Parse gcode text and get the minimum and maximum corners.' + self.cornerMaximum = complex(-987654321.0, -987654321.0) + self.cornerMinimum = complex(987654321.0, 987654321.0) + self.oldLocation = None + self.cornerRadius = complex(radius, radius) + for line in lines: + self.parseCorner(line) + return self + + def isPointInside(self, point): + 'Determine if the point is inside the bounding rectangle.' + return point.imag >= self.cornerMinimum.imag and point.imag <= self.cornerMaximum.imag and point.real >= self.cornerMinimum.real and point.real <= self.cornerMaximum.real + + def parseCorner(self, line): + 'Parse a gcode line and use the location to update the bounding corners.' + splitLine = getSplitLineBeforeBracketSemicolon(line) + firstWord = getFirstWord(splitLine) + if firstWord == '(': + locationComplex = getLocationFromSplitLine(None, splitLine).dropAxis() + self.cornerMaximum = euclidean.getMaximum(self.cornerMaximum, locationComplex) + self.cornerMinimum = euclidean.getMinimum(self.cornerMinimum, locationComplex) + elif firstWord == 'G1': + location = getLocationFromSplitLine(self.oldLocation, splitLine) + locationComplex = location.dropAxis() + self.cornerMaximum = euclidean.getMaximum(self.cornerMaximum, locationComplex + self.cornerRadius) + self.cornerMinimum = euclidean.getMinimum(self.cornerMinimum, locationComplex - self.cornerRadius) + self.oldLocation = location + + +class DistanceFeedRate: + 'A class to limit the z feed rate and round values.' + def __init__(self): + 'Initialize.' + self.isAlteration = False + self.decimalPlacesCarried = 3 + self.output = cStringIO.StringIO() + + def 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.addLine('M101') # Turn extruder on. + for point in thread[1 :]: + self.addGcodeMovementZWithFeedRate(feedRateMinute, point, z) + self.addLine('M103') # Turn extruder off. + + def addGcodeFromLoop(self, loop, z): + 'Add the gcode loop.' + euclidean.addNestedRingBeginning(self, loop, z) + self.addPerimeterBlock(loop, z) + self.addLine('()') + self.addLine('()') + + 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.addLine('M101') # Turn extruder on. + for point in thread[1 :]: + self.addGcodeMovementZ(point, z) + self.addLine('M103') # Turn extruder off. + + def addGcodeMovementZ(self, point, z): + 'Add a movement to the output.' + self.addLine(self.getLinearGcodeMovement(point, z)) + + def addGcodeMovementZWithFeedRate(self, feedRateMinute, point, z): + 'Add a movement to the output.' + self.addLine(self.getLinearGcodeMovementWithFeedRate(feedRateMinute, point, z)) + + 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 == '()': + self.isAlteration = True + elif firstWord == '()': + 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('()') + for line in lines: + splitLine = getSplitLineBeforeBracketSemicolon(line) + firstWord = getFirstWord(splitLine) + if firstWord == 'G90': + absoluteDistanceMode = True + elif firstWord == 'G91': + absoluteDistanceMode = False + self.addLine('()' + line) + if not absoluteDistanceMode: + self.addLine('G90') + self.addLine('()') + + 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('( outer )') + else: + self.addLine('( inner )') + self.addGcodeFromThreadZ(loop + [loop[0]], z) + self.addLine('()') # 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 )' % (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 '( X%s Y%s Z%s )' % (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 '( X%s Y%s Z%s )' % (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 == '(': + self.decimalPlacesCarried = int(splitLine[1]) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/__init__.py b/SkeinPyPy/fabmetheus_utilities/geometry/__init__.py new file mode 100644 index 0000000..3345e96 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/__init__.py @@ -0,0 +1,12 @@ +""" +This page is in the table of contents. +This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +""" +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 2 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/creation/__init__.py b/SkeinPyPy/fabmetheus_utilities/geometry/creation/__init__.py new file mode 100644 index 0000000..cefa3e7 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/creation/__init__.py @@ -0,0 +1,12 @@ +""" +This page is in the table of contents. +This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +""" +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 3 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/creation/_drill.py b/SkeinPyPy/fabmetheus_utilities/geometry/creation/_drill.py new file mode 100644 index 0000000..72fb9dd --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/creation/_drill.py @@ -0,0 +1,59 @@ +""" +Drill negative solid. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import extrude +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.creation import solid +from fabmetheus_utilities.geometry.creation import teardrop +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getGeometryOutput(derivation, elementNode): + "Get vector3 vertexes from attribute dictionary." + if derivation == None: + derivation = DrillDerivation(elementNode) + negatives = [] + teardrop.addNegativesByRadius(elementNode, derivation.end, negatives, derivation.radius, derivation.start) + return solid.getGeometryOutputByManipulation(elementNode, negatives[0]) + +def getGeometryOutputByArguments(arguments, elementNode): + "Get vector3 vertexes from attribute dictionary by arguments." + evaluate.setAttributesByArguments(['radius', 'start', 'end'], arguments, elementNode) + return getGeometryOutput(None, elementNode) + +def getNewDerivation(elementNode): + 'Get new derivation.' + return DrillDerivation(elementNode) + +def processElementNode(elementNode): + "Process the xml element." + solid.processElementNodeByGeometry(elementNode, getGeometryOutput(None, elementNode)) + + +class DrillDerivation: + "Class to hold drill variables." + def __init__(self, elementNode): + 'Set defaults.' + self.elementNode = elementNode + self.end = evaluate.getVector3ByPrefix(Vector3(0.0, 0.0, 1.0), elementNode, 'end') + self.start = evaluate.getVector3ByPrefix(Vector3(), elementNode, 'start') + self.radius = lineation.getFloatByPrefixBeginEnd(elementNode, 'radius', 'diameter', 1.0) + size = evaluate.getEvaluatedFloat(None, elementNode, 'size') + if size != None: + self.radius = 0.5 * size diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/creation/_svg.py b/SkeinPyPy/fabmetheus_utilities/geometry/creation/_svg.py new file mode 100644 index 0000000..9904d56 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/creation/_svg.py @@ -0,0 +1,60 @@ +""" +Svg reader. + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.geometry_tools import path +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import svg_reader + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Nophead \nArt of Illusion ' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getGeometryOutput(derivation, elementNode): + "Get vector3 vertexes from attribute dictionary." + if derivation == None: + derivation = SVGDerivation(elementNode) + return getGeometryOutputBySVGReader(elementNode, derivation.svgReader) + +def getGeometryOutputByArguments(arguments, elementNode): + "Get vector3 vertexes from attribute dictionary by arguments." + derivation = SVGDerivation() + derivation.svgReader.parseSVG('', arguments[0]) + return getGeometryOutput(derivation, elementNode) + +def getGeometryOutputBySVGReader(elementNode, svgReader): + "Get vector3 vertexes from svgReader." + geometryOutput = [] + for loopLayer in svgReader.loopLayers: + for loop in loopLayer.loops: + vector3Path = euclidean.getVector3Path(loop, loopLayer.z) + sideLoop = lineation.SideLoop(vector3Path) + sideLoop.rotate(elementNode) + geometryOutput += lineation.getGeometryOutputByManipulation(elementNode, sideLoop) + return geometryOutput + +def getNewDerivation(elementNode): + 'Get new derivation.' + return SVGDerivation(elementNode) + +def processElementNode(elementNode): + "Process the xml element." + path.convertElementNode(elementNode, getGeometryOutput(None, elementNode)) + + +class SVGDerivation: + "Class to hold svg variables." + def __init__(self, elementNode): + 'Set defaults.' + self.svgReader = svg_reader.SVGReader() + self.svgReader.parseSVGByElementNode(elementNode) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/creation/circle.py b/SkeinPyPy/fabmetheus_utilities/geometry/creation/circle.py new file mode 100644 index 0000000..15651d7 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/creation/circle.py @@ -0,0 +1,76 @@ +""" +Polygon path. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.geometry_tools import path +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getGeometryOutput(derivation, elementNode): + "Get vector3 vertexes from attribute dictionary." + if derivation == None: + derivation = CircleDerivation(elementNode) + angleTotal = math.radians(derivation.start) + loop = [] + sidesCeiling = int(math.ceil(abs(derivation.sides) * derivation.extent / 360.0)) + sideAngle = math.radians(derivation.extent) / sidesCeiling + if derivation.sides < 0.0: + sideAngle = -sideAngle + spiral = lineation.Spiral(derivation.spiral, 0.5 * sideAngle / math.pi) + for side in xrange(sidesCeiling + 1): + unitPolar = euclidean.getWiddershinsUnitPolar(angleTotal) + x = unitPolar.real * derivation.radiusArealized.real + y = unitPolar.imag * derivation.radiusArealized.imag + vertex = spiral.getSpiralPoint(unitPolar, Vector3(x, y)) + angleTotal += sideAngle + loop.append(vertex) + radiusMaximum = 0.000001 * max(derivation.radiusArealized.real, derivation.radiusArealized.imag) + loop = euclidean.getLoopWithoutCloseEnds(radiusMaximum, loop) + lineation.setClosedAttribute(elementNode, derivation.revolutions) + return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop(loop, sideAngle)) + +def getGeometryOutputByArguments(arguments, elementNode): + "Get vector3 vertexes from attribute dictionary by arguments." + evaluate.setAttributesByArguments(['radius', 'start', 'end', 'revolutions'], arguments, elementNode) + return getGeometryOutput(None, elementNode) + +def getNewDerivation(elementNode): + 'Get new derivation.' + return CircleDerivation(elementNode) + +def processElementNode(elementNode): + "Process the xml element." + path.convertElementNode(elementNode, getGeometryOutput(None, elementNode)) + + +class CircleDerivation: + "Class to hold circle variables." + def __init__(self, elementNode): + 'Set defaults.' + self.radius = lineation.getRadiusComplex(elementNode, complex(1.0, 1.0)) + self.sides = evaluate.getEvaluatedFloat(None, elementNode, 'sides') + if self.sides == None: + radiusMaximum = max(self.radius.real, self.radius.imag) + self.sides = evaluate.getSidesMinimumThreeBasedOnPrecisionSides(elementNode, radiusMaximum) + self.radiusArealized = evaluate.getRadiusArealizedBasedOnAreaRadius(elementNode, self.radius, self.sides) + self.start = evaluate.getEvaluatedFloat(0.0, elementNode, 'start') + end = evaluate.getEvaluatedFloat(360.0, elementNode, 'end') + self.revolutions = evaluate.getEvaluatedFloat(1.0, elementNode, 'revolutions') + self.extent = evaluate.getEvaluatedFloat(end - self.start, elementNode, 'extent') + self.extent += 360.0 * (self.revolutions - 1.0) + self.spiral = evaluate.getVector3ByPrefix(None, elementNode, 'spiral') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/creation/concatenate.py b/SkeinPyPy/fabmetheus_utilities/geometry/creation/concatenate.py new file mode 100644 index 0000000..62e7e6d --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/creation/concatenate.py @@ -0,0 +1,54 @@ +""" +Boolean geometry concatenation. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.geometry_tools import path +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getGeometryOutput(derivation, elementNode): + 'Get triangle mesh from attribute dictionary.' + if derivation == None: + derivation = ConcatenateDerivation(elementNode) + concatenatedList = euclidean.getConcatenatedList(derivation.target)[:] + if len(concatenatedList) == 0: + print('Warning, in concatenate there are no paths.') + print(elementNode.attributes) + return None + if 'closed' not in elementNode.attributes: + elementNode.attributes['closed'] = 'true' + return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop(concatenatedList)) + +def getGeometryOutputByArguments(arguments, elementNode): + 'Get triangle mesh from attribute dictionary by arguments.' + return getGeometryOutput(None, elementNode) + +def getNewDerivation(elementNode): + 'Get new derivation.' + return ConcatenateDerivation(elementNode) + +def processElementNode(elementNode): + 'Process the xml element.' + path.convertElementNode(elementNode, getGeometryOutput(None, elementNode)) + + +class ConcatenateDerivation: + 'Class to hold concatenate variables.' + def __init__(self, elementNode): + 'Initialize.' + self.target = evaluate.getTransformedPathsByKey([], elementNode, 'target') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/creation/extrude.py b/SkeinPyPy/fabmetheus_utilities/geometry/creation/extrude.py new file mode 100644 index 0000000..665966f --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/creation/extrude.py @@ -0,0 +1,441 @@ +""" +Boolean geometry extrusion. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.creation import solid +from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities.vector3index import Vector3Index +from fabmetheus_utilities import euclidean +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def addLoop(derivation, endMultiplier, loopLists, path, portionDirectionIndex, portionDirections, vertexes): + 'Add an indexed loop to the vertexes.' + portionDirection = portionDirections[ portionDirectionIndex ] + if portionDirection.directionReversed == True: + loopLists.append([]) + loops = loopLists[-1] + interpolationOffset = derivation.interpolationDictionary['offset'] + offset = interpolationOffset.getVector3ByPortion( portionDirection ) + if endMultiplier != None: + if portionDirectionIndex == 0: + setOffsetByMultiplier( interpolationOffset.path[1], interpolationOffset.path[0], endMultiplier, offset ) + elif portionDirectionIndex == len( portionDirections ) - 1: + setOffsetByMultiplier( interpolationOffset.path[-2], interpolationOffset.path[-1], endMultiplier, offset ) + scale = derivation.interpolationDictionary['scale'].getComplexByPortion( portionDirection ) + twist = derivation.interpolationDictionary['twist'].getYByPortion( portionDirection ) + projectiveSpace = euclidean.ProjectiveSpace() + if derivation.tiltTop == None: + tilt = derivation.interpolationDictionary['tilt'].getComplexByPortion( portionDirection ) + projectiveSpace = projectiveSpace.getByTilt( tilt ) + else: + normals = getNormals( interpolationOffset, offset, portionDirection ) + normalFirst = normals[0] + normalAverage = getNormalAverage(normals) + if derivation.tiltFollow and derivation.oldProjectiveSpace != None: + projectiveSpace = derivation.oldProjectiveSpace.getNextSpace( normalAverage ) + else: + projectiveSpace = projectiveSpace.getByBasisZTop( normalAverage, derivation.tiltTop ) + derivation.oldProjectiveSpace = projectiveSpace + projectiveSpace.unbuckle( derivation.maximumUnbuckling, normalFirst ) + projectiveSpace = projectiveSpace.getSpaceByXYScaleAngle( twist, scale ) + loop = [] + if ( abs( projectiveSpace.basisX ) + abs( projectiveSpace.basisY ) ) < 0.0001: + vector3Index = Vector3Index(len(vertexes)) + addOffsetAddToLists( loop, offset, vector3Index, vertexes ) + loops.append(loop) + return + for point in path: + vector3Index = Vector3Index(len(vertexes)) + projectedVertex = projectiveSpace.getVector3ByPoint(point) + vector3Index.setToVector3( projectedVertex ) + addOffsetAddToLists( loop, offset, vector3Index, vertexes ) + loops.append(loop) + +def addNegatives(derivation, negatives, paths): + 'Add pillars output to negatives.' + portionDirections = getSpacedPortionDirections(derivation.interpolationDictionary) + for path in paths: + loopLists = getLoopListsByPath(derivation, 1.000001, path, portionDirections) + geometryOutput = triangle_mesh.getPillarsOutput(loopLists) + negatives.append(geometryOutput) + +def addNegativesPositives(derivation, negatives, paths, positives): + 'Add pillars output to negatives and positives.' + portionDirections = getSpacedPortionDirections(derivation.interpolationDictionary) + for path in paths: + endMultiplier = None + if not euclidean.getIsWiddershinsByVector3(path): + endMultiplier = 1.000001 + loopLists = getLoopListsByPath(derivation, endMultiplier, path, portionDirections) + geometryOutput = triangle_mesh.getPillarsOutput(loopLists) + if endMultiplier == None: + positives.append(geometryOutput) + else: + negatives.append(geometryOutput) + +def addOffsetAddToLists(loop, offset, vector3Index, vertexes): + 'Add an indexed loop to the vertexes.' + vector3Index += offset + loop.append(vector3Index) + vertexes.append(vector3Index) + +def addPositives(derivation, paths, positives): + 'Add pillars output to positives.' + portionDirections = getSpacedPortionDirections(derivation.interpolationDictionary) + for path in paths: + loopLists = getLoopListsByPath(derivation, None, path, portionDirections) + geometryOutput = triangle_mesh.getPillarsOutput(loopLists) + positives.append(geometryOutput) + +def addSpacedPortionDirection( portionDirection, spacedPortionDirections ): + 'Add spaced portion directions.' + lastSpacedPortionDirection = spacedPortionDirections[-1] + if portionDirection.portion - lastSpacedPortionDirection.portion > 0.003: + spacedPortionDirections.append( portionDirection ) + return + if portionDirection.directionReversed > lastSpacedPortionDirection.directionReversed: + spacedPortionDirections.append( portionDirection ) + +def addTwistPortions( interpolationTwist, remainderPortionDirection, twistPrecision ): + 'Add twist portions.' + lastPortionDirection = interpolationTwist.portionDirections[-1] + if remainderPortionDirection.portion == lastPortionDirection.portion: + return + lastTwist = interpolationTwist.getYByPortion( lastPortionDirection ) + remainderTwist = interpolationTwist.getYByPortion( remainderPortionDirection ) + twistSegments = int( math.floor( abs( remainderTwist - lastTwist ) / twistPrecision ) ) + if twistSegments < 1: + return + portionDifference = remainderPortionDirection.portion - lastPortionDirection.portion + twistSegmentsPlusOne = float( twistSegments + 1 ) + for twistSegment in xrange( twistSegments ): + additionalPortion = portionDifference * float( twistSegment + 1 ) / twistSegmentsPlusOne + portionDirection = PortionDirection( lastPortionDirection.portion + additionalPortion ) + interpolationTwist.portionDirections.append( portionDirection ) + +def comparePortionDirection( portionDirection, otherPortionDirection ): + 'Comparison in order to sort portion directions in ascending order of portion then direction.' + if portionDirection.portion > otherPortionDirection.portion: + return 1 + if portionDirection.portion < otherPortionDirection.portion: + return - 1 + if portionDirection.directionReversed < otherPortionDirection.directionReversed: + return - 1 + return portionDirection.directionReversed > otherPortionDirection.directionReversed + +def getGeometryOutput(derivation, elementNode): + 'Get triangle mesh from attribute dictionary.' + if derivation == None: + derivation = ExtrudeDerivation(elementNode) + if len(euclidean.getConcatenatedList(derivation.target)) == 0: + print('Warning, in extrude there are no paths.') + print(elementNode.attributes) + return None + return getGeometryOutputByLoops(derivation, derivation.target) + +def getGeometryOutputByArguments(arguments, elementNode): + 'Get triangle mesh from attribute dictionary by arguments.' + return getGeometryOutput(None, elementNode) + +def getGeometryOutputByLoops(derivation, loops): + 'Get geometry output by sorted, nested loops.' + loops.sort(key=euclidean.getAreaVector3LoopAbsolute, reverse=True) + complexLoops = euclidean.getComplexPaths(loops) + nestedRings = [] + for loopIndex, loop in enumerate(loops): + complexLoop = complexLoops[loopIndex] + leftPoint = euclidean.getLeftPoint(complexLoop) + isInFilledRegion = euclidean.getIsInFilledRegion(complexLoops[: loopIndex] + complexLoops[loopIndex + 1 :], leftPoint) + if isInFilledRegion == euclidean.isWiddershins(complexLoop): + loop.reverse() + nestedRing = euclidean.NestedRing() + nestedRing.boundary = complexLoop + nestedRing.vector3Loop = loop + nestedRings.append(nestedRing) + nestedRings = euclidean.getOrderedNestedRings(nestedRings) + nestedRings = euclidean.getFlattenedNestedRings(nestedRings) + portionDirections = getSpacedPortionDirections(derivation.interpolationDictionary) + if len(nestedRings) < 1: + return {} + if len(nestedRings) == 1: + geometryOutput = getGeometryOutputByNestedRing(derivation, nestedRings[0], portionDirections) + return solid.getGeometryOutputByManipulation(derivation.elementNode, geometryOutput) + shapes = [] + for nestedRing in nestedRings: + shapes.append(getGeometryOutputByNestedRing(derivation, nestedRing, portionDirections)) + return solid.getGeometryOutputByManipulation(derivation.elementNode, {'union' : {'shapes' : shapes}}) + +def getGeometryOutputByNegativesPositives(elementNode, negatives, positives): + 'Get triangle mesh from elementNode, negatives and positives.' + positiveOutput = triangle_mesh.getUnifiedOutput(positives) + if len(negatives) < 1: + return solid.getGeometryOutputByManipulation(elementNode, positiveOutput) + if len(positives) < 1: + negativeOutput = triangle_mesh.getUnifiedOutput(negatives) + return solid.getGeometryOutputByManipulation(elementNode, negativeOutput) + return solid.getGeometryOutputByManipulation(elementNode, {'difference' : {'shapes' : [positiveOutput] + negatives}}) + +def getGeometryOutputByNestedRing(derivation, nestedRing, portionDirections): + 'Get geometry output by sorted, nested loops.' + loopLists = getLoopListsByPath(derivation, None, nestedRing.vector3Loop, portionDirections) + outsideOutput = triangle_mesh.getPillarsOutput(loopLists) + if len(nestedRing.innerNestedRings) < 1: + return outsideOutput + shapes = [outsideOutput] + for nestedRing.innerNestedRing in nestedRing.innerNestedRings: + loopLists = getLoopListsByPath(derivation, 1.000001, nestedRing.innerNestedRing.vector3Loop, portionDirections) + shapes.append(triangle_mesh.getPillarsOutput(loopLists)) + return {'difference' : {'shapes' : shapes}} + +def getLoopListsByPath(derivation, endMultiplier, path, portionDirections): + 'Get loop lists from path.' + vertexes = [] + loopLists = [[]] + derivation.oldProjectiveSpace = None + for portionDirectionIndex in xrange(len(portionDirections)): + addLoop(derivation, endMultiplier, loopLists, path, portionDirectionIndex, portionDirections, vertexes) + return loopLists + +def getNewDerivation(elementNode): + 'Get new derivation.' + return ExtrudeDerivation(elementNode) + +def getNormalAverage(normals): + 'Get normal.' + if len(normals) < 2: + return normals[0] + return (normals[0] + normals[1]).getNormalized() + +def getNormals( interpolationOffset, offset, portionDirection ): + 'Get normals.' + normals = [] + portionFrom = portionDirection.portion - 0.0001 + portionTo = portionDirection.portion + 0.0001 + if portionFrom >= 0.0: + normals.append( ( offset - interpolationOffset.getVector3ByPortion( PortionDirection( portionFrom ) ) ).getNormalized() ) + if portionTo <= 1.0: + normals.append( ( interpolationOffset.getVector3ByPortion( PortionDirection( portionTo ) ) - offset ).getNormalized() ) + return normals + +def getSpacedPortionDirections( interpolationDictionary ): + 'Get sorted portion directions.' + portionDirections = [] + for interpolationDictionaryValue in interpolationDictionary.values(): + portionDirections += interpolationDictionaryValue.portionDirections + portionDirections.sort( comparePortionDirection ) + if len( portionDirections ) < 1: + return [] + spacedPortionDirections = [ portionDirections[0] ] + for portionDirection in portionDirections[1 :]: + addSpacedPortionDirection( portionDirection, spacedPortionDirections ) + return spacedPortionDirections + +def insertTwistPortions(derivation, elementNode): + 'Insert twist portions and radian the twist.' + interpolationDictionary = derivation.interpolationDictionary + interpolationTwist = Interpolation().getByPrefixX(elementNode, derivation.twistPathDefault, 'twist') + interpolationDictionary['twist'] = interpolationTwist + for point in interpolationTwist.path: + point.y = math.radians(point.y) + remainderPortionDirections = interpolationTwist.portionDirections[1 :] + interpolationTwist.portionDirections = [interpolationTwist.portionDirections[0]] + if elementNode != None: + twistPrecision = setting.getTwistPrecisionRadians(elementNode) + for remainderPortionDirection in remainderPortionDirections: + addTwistPortions(interpolationTwist, remainderPortionDirection, twistPrecision) + interpolationTwist.portionDirections.append(remainderPortionDirection) + +def processElementNode(elementNode): + 'Process the xml element.' + solid.processElementNodeByGeometry(elementNode, getGeometryOutput(None, elementNode)) + +def setElementNodeToEndStart(elementNode, end, start): + 'Set elementNode attribute dictionary to a tilt following path from the start to end.' + elementNode.attributes['path'] = [start, end] + elementNode.attributes['tiltFollow'] = 'true' + elementNode.attributes['tiltTop'] = Vector3(0.0, 0.0, 1.0) + +def setOffsetByMultiplier(begin, end, multiplier, offset): + 'Set the offset by the multiplier.' + segment = end - begin + delta = segment * multiplier - segment + offset.setToVector3(offset + delta) + + +class ExtrudeDerivation: + 'Class to hold extrude variables.' + def __init__(self, elementNode): + 'Initialize.' + self.elementNode = elementNode + self.interpolationDictionary = {} + self.tiltFollow = evaluate.getEvaluatedBoolean(True, elementNode, 'tiltFollow') + self.tiltTop = evaluate.getVector3ByPrefix(None, elementNode, 'tiltTop') + self.maximumUnbuckling = evaluate.getEvaluatedFloat(5.0, elementNode, 'maximumUnbuckling') + scalePathDefault = [Vector3(1.0, 1.0, 0.0), Vector3(1.0, 1.0, 1.0)] + self.interpolationDictionary['scale'] = Interpolation().getByPrefixZ(elementNode, scalePathDefault, 'scale') + self.target = evaluate.getTransformedPathsByKey([], elementNode, 'target') + if self.tiltTop == None: + offsetPathDefault = [Vector3(), Vector3(0.0, 0.0, 1.0)] + self.interpolationDictionary['offset'] = Interpolation().getByPrefixZ(elementNode, offsetPathDefault, '') + tiltPathDefault = [Vector3(), Vector3(0.0, 0.0, 1.0)] + self.interpolationDictionary['tilt'] = Interpolation().getByPrefixZ(elementNode, tiltPathDefault, 'tilt') + for point in self.interpolationDictionary['tilt'].path: + point.x = math.radians(point.x) + point.y = math.radians(point.y) + else: + offsetAlongDefault = [Vector3(), Vector3(1.0, 0.0, 0.0)] + self.interpolationDictionary['offset'] = Interpolation().getByPrefixAlong(elementNode, offsetAlongDefault, '') + self.twist = evaluate.getEvaluatedFloat(0.0, elementNode, 'twist') + self.twistPathDefault = [Vector3(), Vector3(1.0, self.twist) ] + insertTwistPortions(self, elementNode) + + +class Interpolation: + 'Class to interpolate a path.' + def __init__(self): + 'Set index.' + self.interpolationIndex = 0 + + def __repr__(self): + 'Get the string representation of this Interpolation.' + return str(self.__dict__) + + def getByDistances(self): + 'Get by distances.' + beginDistance = self.distances[0] + self.interpolationLength = self.distances[-1] - beginDistance + self.close = abs(0.000001 * self.interpolationLength) + self.portionDirections = [] + oldDistance = -self.interpolationLength # so the difference should not be close + for distance in self.distances: + deltaDistance = distance - beginDistance + portionDirection = PortionDirection(deltaDistance / self.interpolationLength) + if abs(deltaDistance - oldDistance) < self.close: + portionDirection.directionReversed = True + self.portionDirections.append(portionDirection) + oldDistance = deltaDistance + return self + + def getByPrefixAlong(self, elementNode, path, prefix): + 'Get interpolation from prefix and xml element along the path.' + if len(path) < 2: + print('Warning, path is too small in evaluate in Interpolation.') + return + if elementNode == None: + self.path = path + else: + self.path = evaluate.getTransformedPathByPrefix(elementNode, path, prefix) + self.distances = [0.0] + previousPoint = self.path[0] + for point in self.path[1 :]: + distanceDifference = abs(point - previousPoint) + self.distances.append(self.distances[-1] + distanceDifference) + previousPoint = point + return self.getByDistances() + + def getByPrefixX(self, elementNode, path, prefix): + 'Get interpolation from prefix and xml element in the z direction.' + if len(path) < 2: + print('Warning, path is too small in evaluate in Interpolation.') + return + if elementNode == None: + self.path = path + else: + self.path = evaluate.getTransformedPathByPrefix(elementNode, path, prefix) + self.distances = [] + for point in self.path: + self.distances.append(point.x) + return self.getByDistances() + + def getByPrefixZ(self, elementNode, path, prefix): + 'Get interpolation from prefix and xml element in the z direction.' + if len(path) < 2: + print('Warning, path is too small in evaluate in Interpolation.') + return + if elementNode == None: + self.path = path + else: + self.path = evaluate.getTransformedPathByPrefix(elementNode, path, prefix) + self.distances = [] + for point in self.path: + self.distances.append(point.z) + return self.getByDistances() + + def getComparison( self, first, second ): + 'Compare the first with the second.' + if abs( second - first ) < self.close: + return 0 + if second > first: + return 1 + return - 1 + + def getComplexByPortion( self, portionDirection ): + 'Get complex from z portion.' + self.setInterpolationIndexFromTo( portionDirection ) + return self.oneMinusInnerPortion * self.startVertex.dropAxis() + self.innerPortion * self.endVertex.dropAxis() + + def getInnerPortion(self): + 'Get inner x portion.' + fromDistance = self.distances[ self.interpolationIndex ] + innerLength = self.distances[ self.interpolationIndex + 1 ] - fromDistance + if abs( innerLength ) == 0.0: + return 0.0 + return ( self.absolutePortion - fromDistance ) / innerLength + + def getVector3ByPortion( self, portionDirection ): + 'Get vector3 from z portion.' + self.setInterpolationIndexFromTo( portionDirection ) + return self.oneMinusInnerPortion * self.startVertex + self.innerPortion * self.endVertex + + def getYByPortion( self, portionDirection ): + 'Get y from x portion.' + self.setInterpolationIndexFromTo( portionDirection ) + return self.oneMinusInnerPortion * self.startVertex.y + self.innerPortion * self.endVertex.y + + def setInterpolationIndex( self, portionDirection ): + 'Set the interpolation index.' + self.absolutePortion = self.distances[0] + self.interpolationLength * portionDirection.portion + interpolationIndexes = range( 0, len( self.distances ) - 1 ) + if portionDirection.directionReversed: + interpolationIndexes.reverse() + for self.interpolationIndex in interpolationIndexes: + begin = self.distances[ self.interpolationIndex ] + end = self.distances[ self.interpolationIndex + 1 ] + if self.getComparison( begin, self.absolutePortion ) != self.getComparison( end, self.absolutePortion ): + return + + def setInterpolationIndexFromTo( self, portionDirection ): + 'Set the interpolation index, the start vertex and the end vertex.' + self.setInterpolationIndex( portionDirection ) + self.innerPortion = self.getInnerPortion() + self.oneMinusInnerPortion = 1.0 - self.innerPortion + self.startVertex = self.path[ self.interpolationIndex ] + self.endVertex = self.path[ self.interpolationIndex + 1 ] + + +class PortionDirection: + 'Class to hold a portion and direction.' + def __init__( self, portion ): + 'Initialize.' + self.directionReversed = False + self.portion = portion + + def __repr__(self): + 'Get the string representation of this PortionDirection.' + return '%s: %s' % ( self.portion, self.directionReversed ) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/creation/gear.py b/SkeinPyPy/fabmetheus_utilities/geometry/creation/gear.py new file mode 100644 index 0000000..67a4ff6 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/creation/gear.py @@ -0,0 +1,1152 @@ +""" +This page is in the table of contents. +The gear script can generate a spur gear couple, a bevel gear couple, a ring gear couple and a rack & pinion couple. + +A helix pattern can be added to each gear type. All the gear types have a clearance and all the teeth can be beveled. A keyway, shaft and lightening holes can be added to all the round gears, and rack holes can be added to the rack. The script can output solid gears or only the gear profiles. Both gears of the couple can be generated or just one. + +The couple has a pinion gear and a complement. + +==Examples== +The link text includes the distinguishing parameters. Each svg page was generated from an xml page of the same root name using carve. For example, gear.svg was generated by clicking 'Carve' on the carve tool panel and choosing gear.xml in the file chooser. + +Each generated svg file has the xml fabmetheus element without comments towards the end of the file. To see it, open the svg file in a text editor and search for 'fabmetheus' If you copy that into a new text document, add the line '' at the beginning and then give it a file name with the extension '.xml', you could then generate another svg file using carve. + +===Bevel=== +Bevel gear couple. + +gear operatingAngle=90 + +===Collar=== +Spur gear couple and each gear has a collar. + +gear complementCollarLengthOverFaceWidth='1' pinionCollarLengthOverFaceWidth='1' shaftRadius='5' + +===Gear=== +Default spur gear with no parameters. + +gear + +===Keyway=== +Spur gear couple and each gear has a collar and defined keyway. + +gear complementCollarLengthOverFaceWidth='1' keywayRadius='2' pinionCollarLengthOverFaceWidth='1' shaftRadius='5' + +===Rack=== +Rack and pinion couple. + +gear teethComplement='0' + +===Rack Hole=== +Rack and pinion couple, with holes in the rack. + +gear rackHoleRadiusOverWidth='0.2' rackWidthOverFaceWidth='2' teethComplement='0' + +===Ring=== +Pinion and ring gear. + +gear teethComplement='-23' + +===Shaft=== +Spur gear couple and each gear has a square shaft hole. + +gear shaftRadius='5' + +===Shaft Top=== +Spur gear couple and each gear has a round shaft hole, truncated on top. + +gear shaftRadius='5' shaftSides='13' shaftDepthTop='2' + +===Spur Helix=== +Spur gear couple with the gear teeth following a helix path. + +gear helixAngle='45' + +===Spur Herringbone=== +Spur gear couple with the gear teeth following a herringbone path. + +gear helixAngle='45' helixType='herringbone' + +===Spur Parabolic=== +Spur gear couple with the gear teeth following a parabolic path. + +gear helixAngle='45' helixType='parabolic' + +===Spur Profile=== +Spur gear couple profile. Since this is just a horizontal path, it can not be sliced, so the path is then extruded to create a solid which can be sliced and viewed. + +gear id='spurProfile' faceWidth='0' | extrude target='=document.getElementByID(spurProfile) + +==Parameters== +===Center Distance=== +Default is such that the pitch radius works out to twenty. + +Defines the distance between the gear centers. + +===Clearance Couplet=== +====Clearance Over Wavelength==== +Default is 0.1. + +Defines the ratio of the clearance over the wavelength of the gear profile. The wavelength is the arc distance between the gear teeth. + +====Clearance==== +Default is the 'Clearance Over Wavelength' times the wavelength. + +Defines the clearance between the gear tooth and the other gear of the couple. If the clearance is zero, the outside of the gear tooth will touch the other gear. If the clearance is too high, the gear teeth will be long and weak. + +===Collar Addendum Couplet=== +====Collar Addendum Over Radius==== +Default is one. + +Defines the ratio of the collar addendum over the shaft radius. + +====Collar Addendum==== +Default is the 'Collar Addendum Over Radius' times the shaft radius. + +Defines the collar addendum. + +===Complement Collar Length Couplet=== +====Complement Collar Length Over Face Width==== +Default is zero. + +Defines the ratio of the complement collar length over the face width. + +====Complement Collar Length==== +Default is the 'Complement Collar Length Over Face Width' times the face width. + +Defines the complement collar length. If the complement collar length is zero, there will not be a collar on the complement gear. + +===Creation Type=== +Default is 'both'. + +====Both==== +When selected, the pinion and complement will be generated. + +====Complement==== +When selected, only the complement gear or rack will be generated. + +====Pinion==== +When selected, only the pinion will be generated. + +===Face Width=== +Default is ten. + +Defines the face width. + +===Gear Hole Paths=== +Default is empty. + +Defines the centers of the gear holes. If the gear hole paths parameter is the default empty, then the centers of the gear holes will be generated from other parameters. + +===Helix Angle=== +Default is zero. + +===Helix Path=== +Default is empty. + +Defines the helix path of the gear teeth. If the helix path is the default empty, then the helix will be generated from the helix angle and helix type. + +===Helix Type=== +Default is 'basic'. + +====Basic==== +When selected, the helix will be basic. + +====Herringbone==== +When selected, the helix will have a herringbone pattern. + +====Parabolic==== +When selected, the helix will have a parabolic pattern. + +===Keyway Radius Couplet=== +====Keyway Radius Over Radius==== +Default is half. + +Defines the ratio of the keyway radius over the shaft radius. + +====Keyway Radius==== +Default is the 'Keyway Radius Over Radius' times the shaft radius. + +Defines the keyway radius. If the keyway radius is zero, there will not be a keyway on the collar. + +===Lightening Hole Margin Couplet=== +====Lightening Hole Margin Over Rim Dedendum==== +Default is one. + +Defines the ratio of the lightening hole margin over the rim dedendum. + +====Lightening Hole Margin==== +Default is the 'Lightening Hole Margin Over Rim Dedendum' times the rim dedendum. + +Defines the minimum margin between lightening holes. + +===Lightening Hole Minimum Radius=== +Default is one. + +Defines the minimum radius of the lightening holes. + +===Move Type=== +Default is 'separate'. + +====None==== +When selected, the gears will be not be moved and will therefore overlap. Afterwards the write plugin could be used to write each gear to a different file, so they can be fabricated in separate operations. + +====Mesh==== +When selected, the gears will be separated horizontally so that they just mesh. This is useful to test if the gears mesh properly. + +====Separate==== +When selected, the gears will be separated horizontally with a gap between them. + +====Vertical==== +When selected, the gears will be separated vertically. + +===Operating Angle=== +Default is 180 degrees. + +Defines the operating angle between the gear axes. If the operating angle is not 180 degrees, a bevel gear couple will be generated. + +===Pinion Collar Length Couplet=== +====Pinion Collar Length Over Face Width==== +Default is zero. + +Defines the ratio of the pinion collar length over the face width. + +====Pinion Collar Length==== +Default is the 'Pinion Collar Length Over Face Width' times the face width. + +Defines the pinion collar length. If the pinion collar length is zero, there will not be a collar on the pinion gear. + +===Pitch Radius=== +Default is twenty if the pitch radius has not been set. If the center distance is set, the default pitch radius is the center distance times the number of pinion teeth divided by the total number of gear teeth. + +Defines the pinion pitch radius. + +===Plate Clearance Couplet=== +====Plate Clearance Over Length==== +Default is 0.2. + +Defines the ratio of the plate clearance over the plate length. + +====Plate Clearance==== +Default is the 'Plate Clearance Over Length' times the plate length. + +Defines the clearance between the pinion and the plate of the ring gear. If the clearance is zero, they will touch. + +===Plate Length Couplet=== +====Plate Length Over Face Width==== +Default is half. + +Defines the ratio of the plate length over the face width. + +====Plate Length==== +Default is the 'Plate Length Over Face Width' times the face width. + +Defines the length of the plate of the ring gear. + +===Pressure Angle=== +Default is twenty degrees. + +Defines the pressure angle of the gear couple. + +===Profile Surfaces=== +Default is eleven. + +Defines the number of profile surfaces. + +===Rack Hole Below Over Width Couplet=== +====Rack Hole Below Over Width==== +Default is 0.6. + +Defines the ratio of the distance below the pitch of the rack holes over the rack width. + +====Rack Hole Below==== +Default is the 'Rack Hole Below Over Width' times the rack width. + +Defines the the distance below the pitch of the rack holes. + +===Rack Hole Radius Couplet=== +====Rack Hole Radius Over Width==== +Default is zero. + +Defines the ratio of the rack hole radius over the rack width. + +====Rack Hole Radius==== +Default is the 'Rack Hole Radius Over Width' times the rack width. + +Defines the radius of the rack holes. If the rack hole radius is zero, there won't be any rack holes. + +===Rack Hole Step Over Width Couplet=== +====Rack Hole Step Over Width==== +Default is one. + +Defines the ratio of the rack hole step over the rack width. + +====Rack Hole Step==== +Default is the 'Rack Hole Step Over Width' times the rack width. + +Defines the horizontal step distance between the rack holes. + +===Rack Length Over Radius Couplet=== +====Rack Length Over Radius==== +Default is two times pi. + +Defines the ratio of the rack length over the pitch radius. + +====Rack Length==== +Default is the 'Rack Length Over Radius' times the pitch radius. + +Defines the rack length. + +===Rack Width Couplet=== +====Rack Width Over Face Width==== +Default is one. + +Defines the ratio of the rack width over the face width. + +====Rack Width==== +Default is the 'Rack Width Over Face Width' times the face width. + +Defines the rack width. + +===Rim Dedendum Couplet=== +====Rim Dedendum Over Radius==== +Default is 0.2. + +Defines the ratio of the rim dedendum over the pitch radius. + +====Rim Dedendum==== +Default is the 'Rim Dedendum Over Radius' times the pitch radius. + +Defines the rim dedendum of the gear. + +===Root Bevel Couplet=== +====Root Bevel Over Clearance==== +Default is half. + +Defines the ratio of the root bevel over the clearance. + +====Root Bevel==== +Default is the 'Root Bevel Over Clearance' times the clearance. + +Defines the bevel at the root of the gear tooth. + +===Shaft Depth Bottom Couplet=== +====Shaft Depth Bottom Over Radius==== +Default is zero. + +Defines the ratio of the bottom shaft depth over the shaft radius. + +====Shaft Depth Bottom==== +Default is the 'Shaft Depth Bottom Over Radius' times the shaft radius. + +Defines the bottom shaft depth. + +===Shaft Depth Top Couplet=== +====Shaft Depth Top Over Radius==== +Default is zero. + +Defines the ratio of the top shaft depth over the shaft radius. + +====Shaft Depth Top==== +Default is the 'Shaft Depth Top Over Radius' times the shaft radius. + +Defines the top shaft depth. + +===Shaft Path=== +Default is empty. + +Defines the path of the shaft hole. If the shaft path is the default empty, then the shaft path will be generated from the shaft depth bottom, shaft depth top, shaft radius and shaft sides. + +===Shaft Radius Couplet=== +====Shaft Radius Over Pitch Radius==== +Default is zero. + +Defines the ratio of the shaft radius over the pitch radius. + +====Shaft Radius==== +Default is the 'Shaft Radius Over Pitch Radius' times the pitch radius. + +Defines the shaft radius. If the shaft radius is zero there will not be a shaft hole. + +===Shaft Sides=== +Default is four. + +Defines the number of shaft sides. + +===Teeth Pinion=== +Default is seven. + +Defines the number of teeth in the pinion. + +===Teeth Complement=== +Default is seventeen. + +Defines the number of teeth in the complement of the gear couple. If the number of teeth is positive, the gear couple will be a spur or bevel type. If the number of teeth is zero, the gear couple will be a rack and pinion. If the number of teeth is negative, the gear couple will be a spur and ring. + +===Tip Bevel Couplet=== +====Tip Bevel Over Clearance==== +Default is 0.1. + +Defines the ratio of the tip bevel over the clearance. + +====Tip Bevel==== +Default is the 'Tip Bevel Over Clearance' times the clearance. + +Defines the bevel at the tip of the gear tooth. + +===Tooth Thickness Multiplier=== +Default is 0.99999. + +Defines the amount the thickness of the tooth will multiplied. If when the gears are produced, they mesh too tightly, you can reduce the tooth thickness multiplier so that they mesh with reasonable tightness. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import extrude +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.creation import shaft +from fabmetheus_utilities.geometry.creation import solid +from fabmetheus_utilities.geometry.creation import teardrop +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.geometry.geometry_utilities import matrix +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 ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def addBevelGear(derivation, extrudeDerivation, pitchRadius, positives, teeth, vector3GearProfile): + "Get extrude output for a cylinder gear." + totalPitchRadius = derivation.pitchRadiusComplement + derivation.pitchRadius + totalTeeth = derivation.teethPinion + derivation.teethComplement + portionDirections = extrude.getSpacedPortionDirections(extrudeDerivation.interpolationDictionary) + loopLists = extrude.getLoopListsByPath(extrudeDerivation, None, vector3GearProfile[0], portionDirections) + firstLoopList = loopLists[0] + gearOverPinion = float(totalTeeth - teeth) / float(teeth) + thirdLayerHeight = 0.33333333333 * setting.getLayerHeight(derivation.elementNode) + pitchRadian = math.atan(math.sin(derivation.operatingRadian) / (gearOverPinion + math.cos(derivation.operatingRadian))) + coneDistance = pitchRadius / math.sin(pitchRadian) + apex = Vector3(0.0, 0.0, math.sqrt(coneDistance * coneDistance - pitchRadius * pitchRadius)) + cosPitch = apex.z / coneDistance + sinPitch = math.sin(pitchRadian) + for loop in firstLoopList: + for point in loop: + alongWay = point.z / coneDistance + oneMinusAlongWay = 1.0 - alongWay + pointComplex = point.dropAxis() + pointComplexLength = abs(pointComplex) + deltaRadius = pointComplexLength - pitchRadius + cosDeltaRadius = cosPitch * deltaRadius + sinDeltaRadius = sinPitch * deltaRadius + pointComplex *= (cosDeltaRadius + pitchRadius) / pointComplexLength + point.x = pointComplex.real + point.y = pointComplex.imag + point.z += sinDeltaRadius + point.x *= oneMinusAlongWay + point.y *= oneMinusAlongWay + addBottomLoop(-thirdLayerHeight, firstLoopList) + topLoop = firstLoopList[-1] + topAddition = [] + topZ = euclidean.getTopPath(topLoop) + thirdLayerHeight + oldIndex = topLoop[-1].index + for point in topLoop: + oldIndex += 1 + topAddition.append(Vector3Index(oldIndex, 0.8 * point.x, 0.8 * point.y, topZ)) + firstLoopList.append(topAddition) + translation = Vector3(0.0, 0.0, -euclidean.getBottomByPaths(firstLoopList)) + euclidean.translateVector3Paths(firstLoopList, translation) + geometryOutput = triangle_mesh.getPillarsOutput(loopLists) + positives.append(geometryOutput) + +def addBottomLoop(deltaZ, loops): + "Add bottom loop to loops." + bottomLoop = loops[0] + bottomAddition = [] + bottomZ = euclidean.getBottomByPath(bottomLoop) + deltaZ + for point in bottomLoop: + bottomAddition.append(Vector3Index(len(bottomAddition), point.x, point.y, bottomZ)) + loops.insert(0, bottomAddition) + numberOfVertexes = 0 + for loop in loops: + for point in loop: + point.index = numberOfVertexes + numberOfVertexes += 1 + +def addCollarShaft(collarLength, derivation, elementNode, negatives, positives): + 'Add collar.' + if collarLength <= 0.0: + addShaft(derivation, negatives, positives) + return + connectionEnd = Vector3(0.0, 0.0, derivation.faceWidth + collarLength) + copyShallow = derivation.elementNode.getCopyShallow() + copyShallow.attributes['path'] = [Vector3(0.0, 0.0, derivation.faceWidth), connectionEnd] + collarDerivation = extrude.ExtrudeDerivation(copyShallow) + addCollarShaftSetDerivation(collarDerivation, collarLength, derivation, elementNode, negatives, positives) + +def addCollarShaftSetDerivation(collarDerivation, collarLength, derivation, elementNode, negatives, positives): + 'Add collar and shaft.' + collarSides = evaluate.getSidesMinimumThreeBasedOnPrecision(elementNode, derivation.shaftRimRadius) + collarProfile = euclidean.getComplexPolygon(complex(), derivation.shaftRimRadius, collarSides) + vector3CollarProfile = euclidean.getVector3Path(collarProfile) + extrude.addPositives(collarDerivation, [vector3CollarProfile], positives) + addShaft(derivation, negatives, positives) + drillZ = derivation.faceWidth + 0.5 * collarLength + drillEnd = Vector3(0.0, derivation.shaftRimRadius, drillZ) + drillStart = Vector3(0.0, 0.0, drillZ) + teardrop.addNegativesByRadius(elementNode, drillEnd, negatives, derivation.keywayRadius, drillStart) + +def addLighteningHoles(derivation, gearHolePaths, negatives, pitchRadius, positives): + "Add lightening holes." + positiveVertexes = matrix.getVertexes(positives) + bottomPath = euclidean.getTopPath(positiveVertexes) + topPath = euclidean.getBottomByPath(positiveVertexes) + copyShallow = derivation.elementNode.getCopyShallow() + copyShallow.attributes['path'] = [Vector3(0.0, 0.0, bottomPath), Vector3(0.0, 0.0, topPath)] + extrudeDerivation = extrude.ExtrudeDerivation(copyShallow) + vector3LighteningHoles = getLighteningHoles(derivation, gearHolePaths, pitchRadius) + extrude.addNegativesPositives(extrudeDerivation, negatives, vector3LighteningHoles, positives) + +def addRackHole(derivation, elementNode, vector3RackProfiles, x): + "Add rack hole to vector3RackProfiles." + rackHole = euclidean.getComplexPolygon(complex(x, -derivation.rackHoleBelow), derivation.rackHoleRadius, -13) + vector3RackProfiles.append(euclidean.getVector3Path(rackHole)) + +def addRackHoles(derivation, elementNode, vector3RackProfiles): + "Add rack holes to vector3RackProfiles." + if len(derivation.gearHolePaths) > 0: + vector3RackProfiles += derivation.gearHolePaths + return + if derivation.rackHoleRadius <= 0.0: + return + addRackHole(derivation, elementNode, vector3RackProfiles, 0.0) + rackHoleMargin = derivation.rackHoleRadius + derivation.rackHoleRadius + rackHoleSteps = int(math.ceil((derivation.rackDemilength - rackHoleMargin) / derivation.rackHoleStep)) + for rackHoleIndex in xrange(1, rackHoleSteps): + x = float(rackHoleIndex) * derivation.rackHoleStep + addRackHole(derivation, elementNode, vector3RackProfiles, -x) + addRackHole(derivation, elementNode, vector3RackProfiles, x) + +def addShaft(derivation, negatives, positives): + "Add shaft." + if len(derivation.shaftPath) < 3: + return + positiveVertexes = matrix.getVertexes(positives) + bottomPath = euclidean.getTopPath(positiveVertexes) + topPath = euclidean.getBottomByPath(positiveVertexes) + copyShallow = derivation.elementNode.getCopyShallow() + copyShallow.attributes['path'] = [Vector3(0.0, 0.0, bottomPath), Vector3(0.0, 0.0, topPath)] + extrudeDerivation = extrude.ExtrudeDerivation(copyShallow) + extrude.addNegativesPositives(extrudeDerivation, negatives, [derivation.shaftPath], positives) + +def getAxialMargin(circleRadius, numberOfSides, polygonRadius): + 'Get axial margin.' + return polygonRadius * math.sin(math.pi / float(numberOfSides)) - circleRadius + +def getBevelPath(begin, bevel, center, end): + 'Get bevel path.' + centerMinusBegin = center - begin + centerMinusBeginLength = abs(centerMinusBegin) + endMinusCenter = end - center + endMinusCenterLength = abs(endMinusCenter) + endMinusCenter /= endMinusCenterLength + maximumExtensionLength = 0.333333333 * endMinusCenterLength + if centerMinusBeginLength <= bevel * 1.5: + extensionLength = min(maximumExtensionLength, centerMinusBeginLength) + return [complex(center.real, center.imag) + extensionLength * endMinusCenter] + centerMinusBegin *= (centerMinusBeginLength - bevel) / centerMinusBeginLength + extensionLength = min(maximumExtensionLength, bevel) + bevelPath = [complex(center.real, center.imag) + extensionLength * endMinusCenter] + bevelPath.append(begin + centerMinusBegin) + return bevelPath + +def getGearPaths(derivation, pitchRadius, teeth, toothProfile): + 'Get gear paths.' + if teeth < 0: + return getGearProfileAnnulus(derivation, pitchRadius, teeth, toothProfile) + if teeth == 0: + return [getGearProfileRack(derivation, toothProfile)] + return [getGearProfileCylinder(teeth, toothProfile)] + +def getGearProfileAnnulus(derivation, pitchRadius, teeth, toothProfile): + 'Get gear profile for an annulus gear.' + gearProfileCylinder = getGearProfileCylinder(teeth, toothProfile) + annulusRadius = derivation.dedendum + derivation.rimDedendum - pitchRadius + return [euclidean.getComplexPolygon(complex(), annulusRadius, -teeth, 0.5 * math.pi), gearProfileCylinder] + +def getGearProfileCylinder(teeth, toothProfile): + 'Get gear profile for a cylinder gear.' + gearProfile = [] + toothAngleRadian = 2.0 * math.pi / float(teeth) + totalToothAngle = 0.0 + for toothIndex in xrange(abs(teeth)): + for toothPoint in toothProfile: + gearProfile.append(toothPoint * euclidean.getWiddershinsUnitPolar(totalToothAngle)) + totalToothAngle += toothAngleRadian + return gearProfile + +def getGearProfileRack(derivation, toothProfile): + 'Get gear profile for rack.' + derivation.extraRackDemilength = 0.0 + for complexPoint in derivation.helixPath: + derivation.extraRackDemilength = max(abs(derivation.helixHeight * complexPoint.imag), derivation.extraRackDemilength) + rackDemilengthPlus = derivation.rackDemilength + if derivation.faceWidth > 0.0: + derivation.extraRackDemilength *= 1.1 + rackDemilengthPlus += derivation.extraRackDemilength + teethRack = int(math.ceil(rackDemilengthPlus / derivation.wavelength)) + gearProfile = [] + for toothIndex in xrange(-teethRack, teethRack + 1): + translateComplex = complex(-toothIndex * derivation.wavelength, 0.0) + translatedPath = euclidean.getTranslatedComplexPath(toothProfile, translateComplex) + gearProfile += translatedPath + gearProfile = euclidean.getHorizontallyBoundedPath(rackDemilengthPlus, -rackDemilengthPlus, gearProfile) + firstPoint = gearProfile[0] + lastPoint = gearProfile[-1] + rackWidth = derivation.rackWidth + minimumRackWidth = 1.1 * derivation.dedendum + if rackWidth < minimumRackWidth: + rackWidth = minimumRackWidth + print('Warning, rackWidth is too small in getGearProfileRack in gear.') + print('RackWidth will be set to a bit more than the dedendum.') + gearProfile += [complex(lastPoint.real, -rackWidth),complex(firstPoint.real, -rackWidth)] + return gearProfile + +def getGeometryOutput(derivation, elementNode): + "Get vector3 vertexes from attribute dictionary." + if derivation == None: + derivation = GearDerivation(elementNode) + creationFirst = derivation.creationType.lower()[: 1] + toothProfileComplement = getToothProfile(derivation, derivation.pitchRadiusComplement, derivation.teethComplement) + pinionProfile = getGearProfileCylinder(derivation.teethPinion, derivation.pinionToothProfile) + complementPaths = getGearPaths( + derivation, derivation.pitchRadiusComplement, derivation.teethComplement, toothProfileComplement) + vector3PinionProfile = euclidean.getVector3Path(pinionProfile) + vector3ComplementPaths = euclidean.getVector3Paths(complementPaths) + translation = Vector3() + moveFirst = derivation.moveType.lower()[: 1] + if moveFirst != 'n': + distance = derivation.pitchRadius + if moveFirst == 'm': + distance += derivation.pitchRadiusComplement + else: + distance += abs(derivation.pitchRadiusComplement) + decimalPlaces = 1 - int(math.floor(math.log10(distance))) + distance += derivation.halfWavelength + derivation.halfWavelength + distance = round(1.15 * distance, decimalPlaces) + translation = Vector3(0.0, -distance) + if derivation.faceWidth <=0.0: + return getPathOutput( + creationFirst, derivation, elementNode, translation, vector3ComplementPaths, vector3PinionProfile) + pitchRadius = derivation.pitchRadius + teeth = derivation.teethPinion + twist = derivation.helixHeight / derivation.pitchRadius + extrudeOutputPinion = getOutputCylinder( + derivation.pinionCollarLength, derivation, elementNode, None, pitchRadius, teeth, twist, [vector3PinionProfile]) + if creationFirst == 'p': + return extrudeOutputPinion + teeth = derivation.teethComplement + extrudeOutputSecond = None + if teeth == 0: + extrudeOutputSecond = getOutputRack(derivation, elementNode, vector3ComplementPaths[0]) + else: + twist = -derivation.helixHeight / derivation.pitchRadiusComplement + extrudeOutputSecond = getOutputCylinder( + derivation.complementCollarLength, + derivation, + elementNode, + derivation.gearHolePaths, + derivation.pitchRadiusComplement, + teeth, + twist, + vector3ComplementPaths) + if creationFirst == 'c': + return extrudeOutputSecond + gearVertexes = matrix.getVertexes(extrudeOutputSecond) + if moveFirst == 'v': + translation = Vector3(0.0, 0.0, euclidean.getTopPath(gearVertexes)) + euclidean.translateVector3Path(matrix.getVertexes(extrudeOutputPinion), translation) + else: + euclidean.translateVector3Path(gearVertexes, translation) + return {'group' : {'shapes' : [extrudeOutputPinion, extrudeOutputSecond]}} + +def getGeometryOutputByArguments(arguments, elementNode): + "Get vector3 vertexes from attribute dictionary by arguments." + return getGeometryOutput(None, elementNode) + +def getHalfwave(pitchRadius, teeth): + 'Get tooth halfwave.' + return pitchRadius * math.pi / float(teeth) + +def getHelixComplexPath(derivation, elementNode): + 'Set gear helix path.' + helixTypeFirstCharacter = derivation.helixType.lower()[: 1] + if helixTypeFirstCharacter == 'b': + return [complex(), complex(1.0, 1.0)] + if helixTypeFirstCharacter == 'h': + return [complex(), complex(0.5, 0.5), complex(1.0, 0.0)] + if helixTypeFirstCharacter == 'p': + helixComplexPath = [] + x = 0.0 + xStep = setting.getLayerHeight(elementNode) / derivation.faceWidth + justBelowOne = 1.0 - 0.5 * xStep + while x < justBelowOne: + distanceFromCenter = 0.5 - x + parabolicTwist = 0.25 - distanceFromCenter * distanceFromCenter + helixComplexPath.append(complex(x, parabolicTwist)) + x += xStep + helixComplexPath.append(complex(1.0, 0.0)) + return helixComplexPath + print('Warning, the helix type was not one of (basic, herringbone or parabolic) in getHelixComplexPath in gear for:') + print(derivation.helixType) + print(derivation.elementNode) + +def getLiftedOutput(derivation, geometryOutput): + "Get extrude output for a rack." + if derivation.moveType.lower()[: 1] == 'm': + return geometryOutput + geometryOutputVertexes = matrix.getVertexes(geometryOutput) + translation = Vector3(0.0, 0.0, -euclidean.getBottomByPath(geometryOutputVertexes)) + euclidean.translateVector3Path(geometryOutputVertexes, translation) + return geometryOutput + +def getLighteningHoles(derivation, gearHolePaths, pitchRadius): + 'Get cutout circles.' + if gearHolePaths != None: + if len(gearHolePaths) > 0: + return gearHolePaths + innerRadius = abs(pitchRadius) - derivation.dedendum + lighteningHoleOuterRadius = innerRadius - derivation.rimDedendum + shaftRimRadius = max(derivation.shaftRimRadius, (lighteningHoleOuterRadius) * (0.5 - math.sqrt(0.1875))) + lighteningHoleRadius = 0.5 * (lighteningHoleOuterRadius - derivation.shaftRimRadius) + if lighteningHoleRadius < derivation.lighteningHoleMinimumRadius: + return [] + lighteningHoles = [] + numberOfLighteningHoles = 3 + polygonRadius = lighteningHoleOuterRadius - lighteningHoleRadius + rimDemiwidth = 0.5 * derivation.lighteningHoleMargin + axialMargin = getAxialMargin(lighteningHoleRadius, numberOfLighteningHoles, polygonRadius) + if axialMargin < rimDemiwidth: + while axialMargin < rimDemiwidth: + lighteningHoleRadius *= 0.999 + if lighteningHoleRadius < derivation.lighteningHoleMinimumRadius: + return [] + axialMargin = getAxialMargin(lighteningHoleRadius, numberOfLighteningHoles, polygonRadius) + else: + newNumberOfLighteningHoles = numberOfLighteningHoles + while axialMargin > rimDemiwidth: + numberOfLighteningHoles = newNumberOfLighteningHoles + newNumberOfLighteningHoles += 2 + axialMargin = getAxialMargin(lighteningHoleRadius, newNumberOfLighteningHoles, polygonRadius) + sideAngle = 2.0 * math.pi / float(numberOfLighteningHoles) + startAngle = 0.0 + for lighteningHoleIndex in xrange(numberOfLighteningHoles): + unitPolar = euclidean.getWiddershinsUnitPolar(startAngle) + lighteningHole = euclidean.getComplexPolygon(unitPolar * polygonRadius, lighteningHoleRadius, -13) + lighteningHoles.append(lighteningHole) + startAngle += sideAngle + return euclidean.getVector3Paths(lighteningHoles) + +def getNewDerivation(elementNode): + 'Get new derivation.' + return GearDerivation(elementNode) + +def getOutputCylinder( + collarLength, derivation, elementNode, gearHolePaths, pitchRadius, teeth, twist, vector3GearProfile): + "Get extrude output for a cylinder gear." + copyShallow = derivation.elementNode.getCopyShallow() + copyShallow.attributes['path'] = [Vector3(), Vector3(0.0, 0.0, derivation.faceWidth)] + extrudeDerivation = extrude.ExtrudeDerivation(copyShallow) + negatives = [] + positives = [] + if twist != 0.0: + twistDegrees = math.degrees(twist) + extrudeDerivation.twistPathDefault = [] + for complexPoint in derivation.helixPath: + extrudeDerivation.twistPathDefault.append(Vector3(complexPoint.real, twistDegrees * complexPoint.imag)) + extrude.insertTwistPortions(extrudeDerivation, elementNode) + if derivation.operatingAngle != 180.0: + addBevelGear(derivation, extrudeDerivation, pitchRadius, positives, teeth, vector3GearProfile) + addCollarShaft(collarLength, derivation, elementNode, negatives, positives) + return extrude.getGeometryOutputByNegativesPositives(elementNode, negatives, positives) + if pitchRadius > 0: + extrude.addNegativesPositives(extrudeDerivation, negatives, vector3GearProfile, positives) + addLighteningHoles(derivation, gearHolePaths, negatives, pitchRadius, positives) + addCollarShaft(collarLength, derivation, elementNode, negatives, positives) + return extrude.getGeometryOutputByNegativesPositives(elementNode, negatives, positives) + if derivation.plateLength <= 0.0: + extrude.addNegativesPositives(extrudeDerivation, negatives, vector3GearProfile, positives) + return extrude.getGeometryOutputByNegativesPositives(elementNode, negatives, positives) + portionDirections = extrude.getSpacedPortionDirections(extrudeDerivation.interpolationDictionary) + outerGearProfile = vector3GearProfile[0] + outerLoopLists = extrude.getLoopListsByPath(extrudeDerivation, None, outerGearProfile, portionDirections) + addBottomLoop(-derivation.plateClearance, outerLoopLists[0]) + geometryOutput = triangle_mesh.getPillarsOutput(outerLoopLists) + positives.append(geometryOutput) + innerLoopLists = extrude.getLoopListsByPath(extrudeDerivation, None, vector3GearProfile[1], portionDirections) + addBottomLoop(-derivation.plateClearance, innerLoopLists[0]) + geometryOutput = triangle_mesh.getPillarsOutput(innerLoopLists) + negatives.append(geometryOutput) + connectionStart = Vector3(0.0, 0.0, -derivation.plateLength) + copyShallow = derivation.elementNode.getCopyShallow() + copyShallow.attributes['path'] = [connectionStart, Vector3(0.0, 0.0, -derivation.plateClearance)] + plateDerivation = extrude.ExtrudeDerivation(copyShallow) + extrude.addNegativesPositives(plateDerivation, negatives, [outerGearProfile], positives) + vector3LighteningHoles = getLighteningHoles(derivation, gearHolePaths, pitchRadius) + extrude.addNegativesPositives(plateDerivation, negatives, vector3LighteningHoles, positives) + addShaft(derivation, negatives, positives) + positiveOutput = triangle_mesh.getUnifiedOutput(positives) + annulusPlateOutput = {'difference' : {'shapes' : [positiveOutput] + negatives}} + if collarLength <= 0.0: + outputCylinder = solid.getGeometryOutputByManipulation(elementNode, annulusPlateOutput) + return getLiftedOutput(derivation, outputCylinder) + negatives = [] + positives = [] + connectionEnd = Vector3(0.0, 0.0, derivation.faceWidth + collarLength) + copyShallow = derivation.elementNode.getCopyShallow() + copyShallow.attributes['path'] = [Vector3(0.0, 0.0, -derivation.plateClearance), connectionEnd] + collarDerivation = extrude.ExtrudeDerivation(copyShallow) + addCollarShaftSetDerivation(collarDerivation, collarLength, derivation, elementNode, negatives, positives) + collarOutput = {'difference' : {'shapes' : positives + negatives}} + cylinderOutput = {'union' : {'shapes' : [annulusPlateOutput, collarOutput]}} + outputCylinder = solid.getGeometryOutputByManipulation(elementNode, cylinderOutput) + return getLiftedOutput(derivation, outputCylinder) + +def getOutputRack(derivation, elementNode, vector3GearProfile): + "Get extrude output for a rack." + path = [] + for complexPoint in derivation.helixPath: + point = Vector3(derivation.helixHeight * complexPoint.imag, 0.0, derivation.faceWidth * complexPoint.real) + path.append(point) + copyShallow = derivation.elementNode.getCopyShallow() + copyShallow.attributes['path'] = path + extrudeDerivation = extrude.ExtrudeDerivation(copyShallow) + negatives = [] + positives = [] + vector3RackProfiles = [vector3GearProfile] + if derivation.extraRackDemilength > 0.0: + yMaximum = -912345678.0 + yMinimum = 912345678.0 + for point in vector3GearProfile: + yMaximum = max(point.y, yMaximum) + yMinimum = min(point.y, yMinimum) + muchLessThanWidth = 0.01 * derivation.rackWidth + yMaximum += muchLessThanWidth + yMinimum -= muchLessThanWidth + extraRackLength = derivation.extraRackDemilength + derivation.extraRackDemilength + rackDemilengthPlus = derivation.rackDemilength + extraRackLength + leftNegative = [ + Vector3(-derivation.rackDemilength, yMaximum), + Vector3(-derivation.rackDemilength, yMinimum), + Vector3(-rackDemilengthPlus, yMinimum), + Vector3(-rackDemilengthPlus, yMaximum)] + vector3RackProfiles.append(leftNegative) + rightNegative = [ + Vector3(rackDemilengthPlus, yMaximum), + Vector3(rackDemilengthPlus, yMinimum), + Vector3(derivation.rackDemilength, yMinimum), + Vector3(derivation.rackDemilength, yMaximum)] + vector3RackProfiles.append(rightNegative) + addRackHoles(derivation, elementNode, vector3RackProfiles) + extrude.addNegativesPositives(extrudeDerivation, negatives, vector3RackProfiles, positives) + return extrude.getGeometryOutputByNegativesPositives(elementNode, negatives, positives) + +def getPathOutput(creationFirst, derivation, elementNode, translation, vector3ComplementPaths, vector3PinionProfile): + "Get gear path output." + vector3PinionProfile = lineation.getPackedGeometryOutputByLoop(elementNode, lineation.SideLoop(vector3PinionProfile)) + if creationFirst == 'p': + return vector3PinionProfile + packedGearGeometry = [] + for vector3ComplementPath in vector3ComplementPaths: + sideLoop = lineation.SideLoop(vector3ComplementPath) + packedGearGeometry += lineation.getPackedGeometryOutputByLoop(elementNode, sideLoop) + if creationFirst == 'c': + return packedGearGeometry + euclidean.translateVector3Paths(packedGearGeometry, translation) + return vector3PinionProfile + packedGearGeometry + +def getThicknessMultipliedPath(path, thicknessMultiplier): + "Get thickness multiplied path." + for pointIndex, point in enumerate(path): + path[pointIndex] = complex(point.real * thicknessMultiplier, point.imag) + return path + +def getToothProfile(derivation, pitchRadius, teeth): + 'Get profile for one tooth.' + if teeth < 0: + return getToothProfileAnnulus(derivation, pitchRadius, teeth) + if teeth == 0: + return getToothProfileRack(derivation) + return getToothProfileCylinder(derivation, pitchRadius, teeth) + +def getToothProfileAnnulus(derivation, pitchRadius, teeth): + 'Get profile for one tooth of an annulus.' + toothProfileHalf = [] + toothProfileHalfCylinder = getToothProfileHalfCylinder(derivation, pitchRadius) + pitchRadius = -pitchRadius + innerRadius = pitchRadius - derivation.addendum + # tooth is multiplied by 1.02 because at around 1.01 for a 7/-23/20.0 test case, there is intersection since the paths are bending together + for point in getThicknessMultipliedPath(toothProfileHalfCylinder, 1.02 / derivation.toothThicknessMultiplier): + if abs(point) >= innerRadius: + toothProfileHalf.append(point) + profileFirst = toothProfileHalf[0] + profileSecond = toothProfileHalf[1] + firstMinusSecond = profileFirst - profileSecond + remainingAddendum = abs(profileFirst) - innerRadius + firstMinusSecond *= remainingAddendum / abs(firstMinusSecond) + extensionPoint = profileFirst + firstMinusSecond + if derivation.tipBevel > 0.0: + unitPolar = euclidean.getWiddershinsUnitPolar(2.0 / float(teeth) * math.pi) + mirrorPoint = complex(-extensionPoint.real, extensionPoint.imag) * unitPolar + bevelPath = getBevelPath(profileFirst, derivation.tipBevel, extensionPoint, mirrorPoint) + toothProfileHalf = bevelPath + toothProfileHalf + else: + toothProfileHalf.insert(0, extensionPoint) + profileLast = toothProfileHalf[-1] + profilePenultimate = toothProfileHalf[-2] + lastMinusPenultimate = profileLast - profilePenultimate + remainingDedendum = pitchRadius - abs(profileLast) + derivation.dedendum + lastMinusPenultimate *= remainingDedendum / abs(lastMinusPenultimate) + extensionPoint = profileLast + lastMinusPenultimate + if derivation.rootBevel > 0.0: + mirrorPoint = complex(-extensionPoint.real, extensionPoint.imag) + bevelPath = getBevelPath(profileLast, derivation.rootBevel, extensionPoint, mirrorPoint) + bevelPath.reverse() + toothProfileHalf += bevelPath + else: + toothProfileHalf.append(extensionPoint) + toothProfileAnnulus = euclidean.getMirrorPath(toothProfileHalf) + toothProfileAnnulus.reverse() + return toothProfileAnnulus + +def getToothProfileCylinder(derivation, pitchRadius, teeth): + 'Get profile for one tooth of a cylindrical gear.' + toothProfileHalfCylinder = getToothProfileHalfCylinder(derivation, pitchRadius) + toothProfileHalfCylinder = getThicknessMultipliedPath(toothProfileHalfCylinder, derivation.toothThicknessMultiplier) + toothProfileHalf = [] + innerRadius = pitchRadius - derivation.dedendum + for point in toothProfileHalfCylinder: + if abs(point) >= innerRadius: + toothProfileHalf.append(point) + return getToothProfileCylinderByProfile(derivation, pitchRadius, teeth, toothProfileHalf) + +def getToothProfileCylinderByProfile(derivation, pitchRadius, teeth, toothProfileHalf): + 'Get profile for one tooth of a cylindrical gear.' + profileFirst = toothProfileHalf[0] + profileSecond = toothProfileHalf[1] + firstMinusSecond = profileFirst - profileSecond + remainingDedendum = abs(profileFirst) - pitchRadius + derivation.dedendum + firstMinusSecond *= remainingDedendum / abs(firstMinusSecond) + extensionPoint = profileFirst + firstMinusSecond + if derivation.rootBevel > 0.0: + unitPolar = euclidean.getWiddershinsUnitPolar(-2.0 / float(teeth) * math.pi) + mirrorPoint = complex(-extensionPoint.real, extensionPoint.imag) * unitPolar + bevelPath = getBevelPath(profileFirst, derivation.rootBevel, extensionPoint, mirrorPoint) + toothProfileHalf = bevelPath + toothProfileHalf + else: + toothProfileHalf.insert(0, extensionPoint) + if derivation.tipBevel > 0.0: + profileLast = toothProfileHalf[-1] + profilePenultimate = toothProfileHalf[-2] + mirrorPoint = complex(-profileLast.real, profileLast.imag) + bevelPath = getBevelPath(profilePenultimate, derivation.tipBevel, profileLast, mirrorPoint) + bevelPath.reverse() + toothProfileHalf = toothProfileHalf[: -1] + bevelPath + return euclidean.getMirrorPath(toothProfileHalf) + +def getToothProfileHalfCylinder(derivation, pitchRadius): + 'Get profile for half of a one tooth of a cylindrical gear.' + toothProfile=[] +# x = -y * tan(p) + 1 +# x*x + y*y = (2-cos(p))^2 +# y*y*t*t-2yt+1+y*y=4-4c-c*c +# y*y*(t*t+1)-2yt=3-4c-c*c +# y*y*(t*t+1)-2yt-3+4c-c*c=0 +# a=tt+1 +# b=-2t +# c=c(4-c)-3 + a = derivation.tanPressure * derivation.tanPressure + 1.0 + b = -derivation.tanPressure - derivation.tanPressure + cEnd = derivation.cosPressure * (4.0 - derivation.cosPressure) - 3.0 + yEnd = (-b - math.sqrt(b*b - 4 * a * cEnd)) * 0.5 / a + yEnd *= derivation.pitchRadius / abs(pitchRadius) + yEnd -= derivation.clearance / abs(pitchRadius) + # to prevent intersections, yBegin is moved towards the base circle, giving a thinner tooth + yBegin = -yEnd + if pitchRadius > 0.0: + yBegin = 0.5 * derivation.sinPressure + 0.5 * yBegin + beginComplex = complex(1.0 - yBegin * derivation.tanPressure, yBegin) + endComplex = complex(1.0 - yEnd * derivation.tanPressure, yEnd) + endMinusBeginComplex = endComplex - beginComplex + wholeAngle = -abs(endMinusBeginComplex) / derivation.cosPressure + wholeAngleIncrement = wholeAngle / float(derivation.profileSurfaces) + stringStartAngle = abs(beginComplex - complex(1.0, 0.0)) / derivation.cosPressure + wholeDepthIncrementComplex = endMinusBeginComplex / float(derivation.profileSurfaces) + for profileIndex in xrange(derivation.profileSurfaces + 1): + contactPoint = beginComplex + wholeDepthIncrementComplex * float(profileIndex) + stringAngle = stringStartAngle + wholeAngleIncrement * float(profileIndex) + angle = math.atan2(contactPoint.imag, contactPoint.real) - stringAngle + angle += 0.5 * math.pi - derivation.quarterWavelength / abs(pitchRadius) + toothPoint = abs(contactPoint) * euclidean.getWiddershinsUnitPolar(angle) * abs(pitchRadius) + toothProfile.append(toothPoint) + return toothProfile + +def getToothProfileRack(derivation): + 'Get profile for one rack tooth.' + addendumSide = derivation.quarterWavelength - derivation.addendum * derivation.tanPressure + addendumComplex = complex(addendumSide, derivation.addendum) + dedendumSide = derivation.quarterWavelength + derivation.dedendum * derivation.tanPressure + dedendumComplex = complex(dedendumSide, -derivation.dedendum) + toothProfile = [dedendumComplex] + if derivation.rootBevel > 0.0: + mirrorPoint = complex(derivation.wavelength - dedendumSide, -derivation.dedendum) + toothProfile = getBevelPath(addendumComplex, derivation.rootBevel, dedendumComplex, mirrorPoint) + if derivation.tipBevel > 0.0: + mirrorPoint = complex(-addendumComplex.real, addendumComplex.imag) + bevelPath = getBevelPath(dedendumComplex, derivation.tipBevel, addendumComplex, mirrorPoint) + bevelPath.reverse() + toothProfile += bevelPath + else: + toothProfile.append(addendumComplex) + return euclidean.getMirrorPath(getThicknessMultipliedPath(toothProfile, derivation.toothThicknessMultiplier)) + +def processElementNode(elementNode): + "Process the xml element." + geometryOutput = getGeometryOutput(None, elementNode) + if geometryOutput.__class__ == list: + path.convertElementNode(elementNode, geometryOutput) + else: + solid.processElementNodeByGeometry(elementNode, geometryOutput) + + +class GearDerivation: + "Class to hold gear variables." + def __init__(self, elementNode): + 'Set defaults.' + self.clearanceOverWavelength = evaluate.getEvaluatedFloat(0.1, elementNode, 'clearanceOverWavelength') + self.collarAddendumOverRadius = evaluate.getEvaluatedFloat(1.0, elementNode, 'collarAddendumOverRadius') + self.complementCollarLengthOverFaceWidth = evaluate.getEvaluatedFloat( + 0.0, elementNode, 'complementCollarLengthOverFaceWidth') + self.copyShallow = elementNode.getCopyShallow() + self.creationType = evaluate.getEvaluatedString('both', elementNode, 'creationType') + self.creationTypeMenuRadioStrings = 'both complement pinion'.split() + self.elementNode = elementNode + self.faceWidth = evaluate.getEvaluatedFloat(10.0, elementNode, 'faceWidth') + self.helixAngle = evaluate.getEvaluatedFloat(0.0, elementNode, 'helixAngle') + self.helixType = evaluate.getEvaluatedString('basic', elementNode, 'helixType') + self.helixTypeMenuRadioStrings = 'basic herringbone parabolic'.split() + self.keywayRadiusOverRadius = evaluate.getEvaluatedFloat(0.5, elementNode, 'keywayRadiusOverRadius') + self.lighteningHoleMarginOverRimDedendum = evaluate.getEvaluatedFloat( + 1.0, elementNode, 'lighteningHoleMarginOverRimDedendum') + self.lighteningHoleMinimumRadius = evaluate.getEvaluatedFloat( + 1.0, elementNode, 'lighteningHoleMinimumRadius') + self.moveType = evaluate.getEvaluatedString('separate', elementNode, 'moveType') + self.moveTypeMenuRadioStrings = 'mesh none separate vertical'.split() + self.operatingAngle = evaluate.getEvaluatedFloat(180.0, elementNode, 'operatingAngle') + self.pinionCollarLengthOverFaceWidth = evaluate.getEvaluatedFloat( + 0.0, elementNode, 'pinionCollarLengthOverFaceWidth') + self.plateClearanceOverLength = evaluate.getEvaluatedFloat(0.2, elementNode, 'plateClearanceOverLength') + self.plateLengthOverFaceWidth = evaluate.getEvaluatedFloat(0.5, elementNode, 'plateLengthOverFaceWidth') + self.pressureAngle = evaluate.getEvaluatedFloat(20.0, elementNode, 'pressureAngle') + self.profileSurfaces = evaluate.getEvaluatedInt(11, elementNode, 'profileSurfaces') + self.rackHoleBelowOverWidth = evaluate.getEvaluatedFloat(0.6, elementNode, 'rackHoleBelowOverWidth') + self.rackHoleRadiusOverWidth = evaluate.getEvaluatedFloat(0.0, elementNode, 'rackHoleRadiusOverWidth') + self.rackHoleStepOverWidth = evaluate.getEvaluatedFloat(1.0, elementNode, 'rackHoleStepOverWidth') + self.rackLengthOverRadius = evaluate.getEvaluatedFloat(math.pi + math.pi, elementNode, 'rackLengthOverRadius') + self.rackWidthOverFaceWidth = evaluate.getEvaluatedFloat(1.0, elementNode, 'rackWidthOverFaceWidth') + self.rimDedendumOverRadius = evaluate.getEvaluatedFloat(0.2, elementNode, 'rimDedendumOverRadius') + self.rootBevelOverClearance = evaluate.getEvaluatedFloat(0.5, elementNode, 'rootBevelOverClearance') + self.shaftDepthBottomOverRadius = evaluate.getEvaluatedFloat(0.0, elementNode, 'shaftDepthBottomOverRadius') + self.shaftDepthTopOverRadius = evaluate.getEvaluatedFloat(0.0, elementNode, 'shaftDepthOverRadius') + self.shaftRadiusOverPitchRadius = evaluate.getEvaluatedFloat(0.0, elementNode, 'shaftRadiusOverPitchRadius') + self.shaftSides = evaluate.getEvaluatedInt(4, elementNode, 'shaftSides') + self.teethComplement = evaluate.getEvaluatedInt(17, elementNode, 'teethComplement') + self.teethPinion = evaluate.getEvaluatedInt(7, elementNode, 'teeth') + totalTeethOverPinionTeeth = float(self.teethComplement + self.teethPinion) / float(self.teethPinion) + self.centerDistance = evaluate.getEvaluatedFloat(20.0 * totalTeethOverPinionTeeth, elementNode, 'centerDistance') + derivedPitchRadius = self.centerDistance / totalTeethOverPinionTeeth + self.pitchRadius = evaluate.getEvaluatedFloat(derivedPitchRadius, elementNode, 'pitchRadius') + self.tipBevelOverClearance = evaluate.getEvaluatedFloat(0.1, elementNode, 'tipBevelOverClearance') + # tooth multiplied by 0.99999 to avoid an intersection + self.toothThicknessMultiplier = evaluate.getEvaluatedFloat(0.99999, elementNode, 'toothThicknessMultiplier') + # Set derived variables. + self.wavelength = self.pitchRadius * 2.0 * math.pi / float(self.teethPinion) + self.clearance = self.wavelength * self.clearanceOverWavelength + self.clearance = evaluate.getEvaluatedFloat(self.clearance, elementNode, 'clearance') + self.complementCollarLength = self.faceWidth * self.complementCollarLengthOverFaceWidth + self.complementCollarLength = evaluate.getEvaluatedFloat(self.complementCollarLength, elementNode, 'complementCollarLength') + self.gearHolePaths = evaluate.getTransformedPathsByKey([], elementNode, 'gearHolePaths') + self.pinionCollarLength = self.faceWidth * self.pinionCollarLengthOverFaceWidth + self.pinionCollarLength = evaluate.getEvaluatedFloat(self.pinionCollarLength, elementNode, 'pinionCollarLength') + self.plateLength = self.faceWidth * self.plateLengthOverFaceWidth + self.plateLength = evaluate.getEvaluatedFloat(self.plateLength, elementNode, 'plateLength') + self.plateClearance = self.plateLength * self.plateClearanceOverLength + self.plateClearance = evaluate.getEvaluatedFloat(self.plateClearance, elementNode, 'plateClearance') + self.rackLength = self.pitchRadius * self.rackLengthOverRadius + self.rackLength = evaluate.getEvaluatedFloat(self.rackLength, elementNode, 'rackLength') + self.rackDemilength = 0.5 * self.rackLength + self.rackWidth = self.faceWidth * self.rackWidthOverFaceWidth + self.rackWidth = evaluate.getEvaluatedFloat(self.rackWidth, elementNode, 'rackWidth') + self.rimDedendum = self.pitchRadius * self.rimDedendumOverRadius + self.rimDedendum = evaluate.getEvaluatedFloat(self.rimDedendum, elementNode, 'rimDedendum') + self.rootBevel = self.clearance * self.rootBevelOverClearance + self.rootBevel = evaluate.getEvaluatedFloat(self.rootBevel, elementNode, 'rootBevel') + self.shaftRadius = self.pitchRadius * self.shaftRadiusOverPitchRadius + self.shaftRadius = evaluate.getEvaluatedFloat(self.shaftRadius, elementNode, 'shaftRadius') + self.collarAddendum = self.shaftRadius * self.collarAddendumOverRadius + self.collarAddendum = evaluate.getEvaluatedFloat(self.collarAddendum, elementNode, 'collarWidth') + self.keywayRadius = self.shaftRadius * self.keywayRadiusOverRadius + self.keywayRadius = lineation.getFloatByPrefixBeginEnd(elementNode, 'keywayRadius', 'keywayDiameter', self.keywayRadius) + self.lighteningHoleMargin = self.rimDedendum * self.lighteningHoleMarginOverRimDedendum + self.lighteningHoleMargin = evaluate.getEvaluatedFloat( + self.lighteningHoleMargin, elementNode, 'lighteningHoleMargin') + self.rackHoleBelow = self.rackWidth * self.rackHoleBelowOverWidth + self.rackHoleBelow = evaluate.getEvaluatedFloat(self.rackHoleBelow, elementNode, 'rackHoleBelow') + self.rackHoleRadius = self.rackWidth * self.rackHoleRadiusOverWidth + self.rackHoleRadius = lineation.getFloatByPrefixBeginEnd(elementNode, 'rackHoleRadius', 'rackHoleDiameter', self.rackHoleRadius) + self.rackHoleStep = self.rackWidth * self.rackHoleStepOverWidth + self.rackHoleStep = evaluate.getEvaluatedFloat(self.rackHoleStep, elementNode, 'rackHoleStep') + self.shaftDepthBottom = self.shaftRadius * self.shaftDepthBottomOverRadius + self.shaftDepthBottom = evaluate.getEvaluatedFloat(self.shaftDepthBottom, elementNode, 'shaftDepthBottom') + self.shaftDepthTop = self.shaftRadius * self.shaftDepthTopOverRadius + self.shaftDepthTop = evaluate.getEvaluatedFloat(self.shaftDepthTop, elementNode, 'shaftDepthTop') + self.shaftPath = evaluate.getTransformedPathByKey([], elementNode, 'shaftPath') + if len(self.shaftPath) < 3: + self.shaftPath = shaft.getShaftPath(self.shaftDepthBottom, self.shaftDepthTop, self.shaftRadius, -self.shaftSides) + self.tipBevel = self.clearance * self.tipBevelOverClearance + self.tipBevel = evaluate.getEvaluatedFloat(self.tipBevel, elementNode, 'tipBevel') + # Set derived values. + self.helixRadian = math.radians(self.helixAngle) + if self.teethComplement <= 0.0 and self.operatingAngle != 180.0: + print('Warning, an operatingAngle other than 180 degrees can only work with a positive number of gear teeth.') + print('Therefore the operatingAngle will be reset to 180 degrees.') + self.operatingAngle = 180.0 + self.tanHelix = math.tan(self.helixRadian) + self.helixHeight = self.tanHelix * self.faceWidth + self.operatingRadian = math.radians(self.operatingAngle) + self.pitchRadiusComplement = self.pitchRadius * float(self.teethComplement) / float(self.teethPinion) + self.pressureRadian = math.radians(self.pressureAngle) + self.cosPressure = math.cos(self.pressureRadian) + self.sinPressure = math.sin(self.pressureRadian) + self.tanPressure = math.tan(self.pressureRadian) + self.halfWavelength = 0.5 * self.wavelength + self.helixPath = euclidean.getComplexPath(evaluate.getTransformedPathByKey([], elementNode, 'helixPath')) + if len(self.helixPath) < 1: + self.helixPath = getHelixComplexPath(self, elementNode) + self.quarterWavelength = 0.25 * self.wavelength + self.shaftRimRadius = self.shaftRadius + self.collarAddendum + self.toothProfileHalf = getToothProfileHalfCylinder(self, self.pitchRadius) + self.toothProfileHalf = getThicknessMultipliedPath(self.toothProfileHalf, self.toothThicknessMultiplier) + self.addendum = self.toothProfileHalf[-1].imag - self.pitchRadius + self.dedendum = abs(self.toothProfileHalf[-1]) - self.pitchRadius + self.clearance + self.pinionToothProfile = getToothProfileCylinderByProfile(self, self.pitchRadius, self.teethPinion, self.toothProfileHalf) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/creation/grid.py b/SkeinPyPy/fabmetheus_utilities/geometry/creation/grid.py new file mode 100644 index 0000000..78625f6 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/creation/grid.py @@ -0,0 +1,166 @@ +""" +Grid path points. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.geometry_tools import path +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +import math +import random + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def addGridRow(diameter, gridPath, loopsComplex, maximumComplex, rowIndex, x, y, zigzag): + 'Add grid row.' + row = [] + while x < maximumComplex.real: + point = complex(x, y) + if euclidean.getIsInFilledRegion(loopsComplex, point): + row.append(point) + x += diameter.real + if zigzag and rowIndex % 2 == 1: + row.reverse() + gridPath += row + +def getGeometryOutput(elementNode): + 'Get vector3 vertexes from attribute dictionary.' + derivation = GridDerivation(elementNode) + diameter = derivation.radius + derivation.radius + typeStringTwoCharacters = derivation.typeString.lower()[: 2] + typeStringFirstCharacter = typeStringTwoCharacters[: 1] + topRight = complex(derivation.demiwidth, derivation.demiheight) + loopsComplex = [euclidean.getSquareLoopWiddershins(-topRight, topRight)] + if len(derivation.target) > 0: + loopsComplex = euclidean.getComplexPaths(derivation.target) + maximumComplex = euclidean.getMaximumByComplexPaths(loopsComplex) + minimumComplex = euclidean.getMinimumByComplexPaths(loopsComplex) + gridPath = None + if typeStringTwoCharacters == 'he': + gridPath = getHexagonalGrid(diameter, loopsComplex, maximumComplex, minimumComplex, derivation.zigzag) + elif typeStringTwoCharacters == 'ra' or typeStringFirstCharacter == 'a': + gridPath = getRandomGrid(derivation, diameter, elementNode, loopsComplex, maximumComplex, minimumComplex) + elif typeStringTwoCharacters == 're' or typeStringFirstCharacter == 'e': + gridPath = getRectangularGrid(diameter, loopsComplex, maximumComplex, minimumComplex, derivation.zigzag) + if gridPath == None: + print('Warning, the step type was not one of (hexagonal, random or rectangular) in getGeometryOutput in grid for:') + print(derivation.typeString) + print(elementNode) + return [] + loop = euclidean.getVector3Path(gridPath) + elementNode.attributes['closed'] = 'false' + return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop(loop, 0.5 * math.pi)) + +def getGeometryOutputByArguments(arguments, elementNode): + 'Get vector3 vertexes from attribute dictionary by arguments.' + if len(arguments) < 1: + return getGeometryOutput(elementNode) + inradius = 0.5 * euclidean.getFloatFromValue(arguments[0]) + elementNode.attributes['inradius.x'] = str(inradius) + if len(arguments) > 1: + inradius = 0.5 * euclidean.getFloatFromValue(arguments[1]) + elementNode.attributes['inradius.y'] = str(inradius) + return getGeometryOutput(elementNode) + +def getHexagonalGrid(diameter, loopsComplex, maximumComplex, minimumComplex, zigzag): + 'Get hexagonal grid.' + diameter = complex(diameter.real, math.sqrt(0.75) * diameter.imag) + demiradius = 0.25 * diameter + xRadius = 0.5 * diameter.real + xStart = minimumComplex.real - demiradius.real + y = minimumComplex.imag - demiradius.imag + gridPath = [] + rowIndex = 0 + while y < maximumComplex.imag: + x = xStart + if rowIndex % 2 == 1: + x -= xRadius + addGridRow(diameter, gridPath, loopsComplex, maximumComplex, rowIndex, x, y, zigzag) + y += diameter.imag + rowIndex += 1 + return gridPath + +def getIsPointInsideZoneAwayOthers(diameterReciprocal, loopsComplex, point, pixelDictionary): + 'Determine if the point is inside the loops zone and and away from the other points.' + if not euclidean.getIsInFilledRegion(loopsComplex, point): + return False + pointOverDiameter = complex(point.real * diameterReciprocal.real, point.imag * diameterReciprocal.imag) + squareValues = euclidean.getSquareValuesFromPoint(pixelDictionary, pointOverDiameter) + for squareValue in squareValues: + if abs(squareValue - pointOverDiameter) < 1.0: + return False + euclidean.addElementToPixelListFromPoint(pointOverDiameter, pixelDictionary, pointOverDiameter) + return True + +def getNewDerivation(elementNode): + 'Get new derivation.' + return GridDerivation(elementNode) + +def getRandomGrid(derivation, diameter, elementNode, loopsComplex, maximumComplex, minimumComplex): + 'Get rectangular grid.' + gridPath = [] + diameterReciprocal = complex(1.0 / diameter.real, 1.0 / diameter.imag) + diameterSquared = diameter.real * diameter.real + diameter.imag * diameter.imag + elements = int(math.ceil(derivation.density * euclidean.getAreaLoops(loopsComplex) / diameterSquared / math.sqrt(0.75))) + elements = evaluate.getEvaluatedInt(elements, elementNode, 'elements') + failedPlacementAttempts = 0 + pixelDictionary = {} + if derivation.seed != None: + random.seed(derivation.seed) + successfulPlacementAttempts = 0 + while failedPlacementAttempts < 100: + point = euclidean.getRandomComplex(minimumComplex, maximumComplex) + if getIsPointInsideZoneAwayOthers(diameterReciprocal, loopsComplex, point, pixelDictionary): + gridPath.append(point) + euclidean.addElementToPixelListFromPoint(point, pixelDictionary, point) + successfulPlacementAttempts += 1 + else: + failedPlacementAttempts += 1 + if successfulPlacementAttempts >= elements: + return gridPath + return gridPath + +def getRectangularGrid(diameter, loopsComplex, maximumComplex, minimumComplex, zigzag): + 'Get rectangular grid.' + demiradius = 0.25 * diameter + xStart = minimumComplex.real - demiradius.real + y = minimumComplex.imag - demiradius.imag + gridPath = [] + rowIndex = 0 + while y < maximumComplex.imag: + addGridRow(diameter, gridPath, loopsComplex, maximumComplex, rowIndex, xStart, y, zigzag) + y += diameter.imag + rowIndex += 1 + return gridPath + +def processElementNode(elementNode): + 'Process the xml element.' + path.convertElementNode(elementNode, getGeometryOutput(elementNode)) + + +class GridDerivation: + 'Class to hold grid variables.' + def __init__(self, elementNode): + 'Set defaults.' + self.inradius = lineation.getInradius(complex(10.0, 10.0), elementNode) + self.demiwidth = lineation.getFloatByPrefixBeginEnd(elementNode, 'demiwidth', 'width', self.inradius.real) + self.demiheight = lineation.getFloatByPrefixBeginEnd(elementNode, 'demiheight', 'height', self.inradius.imag) + self.density = evaluate.getEvaluatedFloat(0.2, elementNode, 'density') + self.radius = lineation.getComplexByPrefixBeginEnd(elementNode, 'elementRadius', 'elementDiameter', complex(1.0, 1.0)) + self.radius = lineation.getComplexByPrefixBeginEnd(elementNode, 'radius', 'diameter', self.radius) + self.seed = evaluate.getEvaluatedInt(None, elementNode, 'seed') + self.target = evaluate.getTransformedPathsByKey([], elementNode, 'target') + self.typeMenuRadioStrings = 'hexagonal random rectangular'.split() + self.typeString = evaluate.getEvaluatedString('rectangular', elementNode, 'type') + self.zigzag = evaluate.getEvaluatedBoolean(True, elementNode, 'zigzag') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/creation/heightmap.py b/SkeinPyPy/fabmetheus_utilities/geometry/creation/heightmap.py new file mode 100644 index 0000000..1f7c908 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/creation/heightmap.py @@ -0,0 +1,208 @@ +""" +Heightmap. +http://www.cs.otago.ac.nz/graphics/Mirage/node59.html +http://en.wikipedia.org/wiki/Heightmap +http://en.wikipedia.org/wiki/Netpbm_format + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.creation import solid +from fabmetheus_utilities.geometry.geometry_tools import path +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities.vector3index import Vector3Index +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +import math +import random + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def addHeightsByBitmap(heights, textLines): + 'Add heights by bitmap.' + for line in textLines[3:]: + for integerWord in line.split(): + heights.append(float(integerWord)) + +def addHeightsByGraymap(heights, textLines): + 'Add heights by graymap.' + divisor = float(textLines[3]) + for line in textLines[4:]: + for integerWord in line.split(): + heights.append(float(integerWord) / divisor) + +def getAddIndexedHeightGrid(heightGrid, minimumXY, step, top, vertexes): + 'Get and add an indexed heightGrid.' + indexedHeightGrid = [] + for rowIndex, row in enumerate(heightGrid): + indexedRow = [] + indexedHeightGrid.append(indexedRow) + rowOffset = step.imag * float(rowIndex) + minimumXY.imag + for columnIndex, element in enumerate(row): + columnOffset = step.real * float(columnIndex) + minimumXY.real + vector3index = Vector3Index(len(vertexes), columnOffset, rowOffset, top * element) + indexedRow.append(vector3index) + vertexes.append(vector3index) + return indexedHeightGrid + +def getAddIndexedSegmentedPerimeter(heightGrid, maximumXY, minimumXY, step, vertexes, z=0.0): + 'Get and add an indexed segmented perimeter.' + indexedSegmentedPerimeter = [] + firstRow = heightGrid[0] + columnOffset = minimumXY.real + numberOfRowsMinusTwo = len(heightGrid) - 2 + for column in firstRow: + vector3index = Vector3Index(len(vertexes), columnOffset, minimumXY.imag, z) + vertexes.append(vector3index) + indexedSegmentedPerimeter.append(vector3index) + columnOffset += step.real + rowOffset = minimumXY.imag + for rowIndex in xrange(numberOfRowsMinusTwo): + rowOffset += step.imag + vector3index = Vector3Index(len(vertexes), maximumXY.real, rowOffset, z) + vertexes.append(vector3index) + indexedSegmentedPerimeter.append(vector3index) + columnOffset = maximumXY.real + for column in firstRow: + vector3index = Vector3Index(len(vertexes), columnOffset, maximumXY.imag, z) + vertexes.append(vector3index) + indexedSegmentedPerimeter.append(vector3index) + columnOffset -= step.real + rowOffset = maximumXY.imag + for rowIndex in xrange(numberOfRowsMinusTwo): + rowOffset -= step.imag + vector3index = Vector3Index(len(vertexes), minimumXY.real, rowOffset, z) + vertexes.append(vector3index) + indexedSegmentedPerimeter.append(vector3index) + return indexedSegmentedPerimeter + +def getGeometryOutput(elementNode): + 'Get vector3 vertexes from attribute dictionary.' + derivation = HeightmapDerivation(elementNode) + heightGrid = derivation.heightGrid + if derivation.fileName != '': + heightGrid = getHeightGrid(archive.getAbsoluteFolderPath(elementNode.getOwnerDocument().fileName, derivation.fileName)) + return getGeometryOutputByHeightGrid(derivation, elementNode, heightGrid) + +def getGeometryOutputByArguments(arguments, elementNode): + 'Get vector3 vertexes from attribute dictionary by arguments.' + evaluate.setAttributesByArguments(['file', 'start'], arguments, elementNode) + return getGeometryOutput(elementNode) + +def getGeometryOutputByHeightGrid(derivation, elementNode, heightGrid): + 'Get vector3 vertexes from attribute dictionary.' + numberOfColumns = len(heightGrid) + if numberOfColumns < 2: + print('Warning, in getGeometryOutputByHeightGrid in heightmap there are fewer than two rows for:') + print(heightGrid) + print(elementNode) + return None + numberOfRows = len(heightGrid[0]) + if numberOfRows < 2: + print('Warning, in getGeometryOutputByHeightGrid in heightmap there are fewer than two columns for:') + print(heightGrid) + print(elementNode) + return None + for row in heightGrid: + if len(row) != numberOfRows: + print('Warning, in getGeometryOutputByHeightGrid in heightmap the heightgrid is not rectangular for:') + print(heightGrid) + print(elementNode) + return None + inradiusComplex = derivation.inradius.dropAxis() + minimumXY = -inradiusComplex + step = complex(derivation.inradius.x / float(numberOfRows - 1), derivation.inradius.y / float(numberOfColumns - 1)) + step += step + faces = [] + heightGrid = getRaisedHeightGrid(heightGrid, derivation.start) + top = derivation.inradius.z + derivation.inradius.z + vertexes = [] + indexedBottomLoop = getAddIndexedSegmentedPerimeter(heightGrid, inradiusComplex, minimumXY, step, vertexes) + indexedLoops = [indexedBottomLoop] + indexedGridTop = getAddIndexedHeightGrid(heightGrid, minimumXY, step, top, vertexes) + indexedLoops.append(triangle_mesh.getIndexedLoopFromIndexedGrid(indexedGridTop)) + vertexes = triangle_mesh.getUniqueVertexes(indexedLoops + indexedGridTop) + triangle_mesh.addPillarFromConvexLoopsGridTop(faces, indexedGridTop, indexedLoops) + return triangle_mesh.getGeometryOutputByFacesVertexes(faces, vertexes) + +def getHeightGrid(fileName): + 'Get heightGrid by fileName.' + if 'models/' not in fileName: + print('Warning, models/ was not in the absolute file path, so for security nothing will be done for:') + print(fileName) + print('The heightmap tool can only read a file which has models/ in the file path.') + print('To import the file, move the file into a folder called model/ or a subfolder which is inside the model folder tree.') + return + pgmText = archive.getFileText(fileName) + textLines = archive.getTextLines(pgmText) + format = textLines[0].lower() + sizeWords = textLines[2].split() + numberOfColumns = int(sizeWords[0]) + numberOfRows = int(sizeWords[1]) + heights = [] + if format == 'p1': + addHeightsByBitmap(heights, textLines) + elif format == 'p2': + addHeightsByGraymap(heights, textLines) + else: + print('Warning, the file format was not recognized for:') + print(fileName) + print('Heightmap can only read the Netpbm Portable bitmap format and the Netpbm Portable graymap format.') + print('The Netpbm formats are described at:') + print('http://en.wikipedia.org/wiki/Netpbm_format') + return [] + heightGrid = [] + heightIndex = 0 + for rowIndex in xrange(numberOfRows): + row = [] + heightGrid.append(row) + for columnIndex in xrange(numberOfColumns): + row.append(heights[heightIndex]) + heightIndex += 1 + return heightGrid + +def getNewDerivation(elementNode): + 'Get new derivation.' + return HeightmapDerivation(elementNode) + +def getRaisedHeightGrid(heightGrid, start): + 'Get heightGrid raised above start.' + raisedHeightGrid = [] + remainingHeight = 1.0 - start + for row in heightGrid: + raisedRow = [] + raisedHeightGrid.append(raisedRow) + for element in row: + raisedElement = remainingHeight * element + start + raisedRow.append(raisedElement) + return raisedHeightGrid + +def processElementNode(elementNode): + 'Process the xml element.' + solid.processElementNodeByGeometry(elementNode, getGeometryOutput(elementNode)) + + +class HeightmapDerivation: + 'Class to hold heightmap variables.' + def __init__(self, elementNode): + 'Set defaults.' + self.fileName = evaluate.getEvaluatedString('', elementNode, 'file') + self.heightGrid = evaluate.getEvaluatedValue([], elementNode, 'heightGrid') + self.inradius = evaluate.getVector3ByPrefixes(elementNode, ['demisize', 'inradius'], Vector3(10.0, 10.0, 5.0)) + self.inradius = evaluate.getVector3ByMultiplierPrefix(elementNode, 2.0, 'size', self.inradius) + self.start = evaluate.getEvaluatedFloat(0.0, elementNode, 'start') + + def __repr__(self): + 'Get the string representation of this HeightmapDerivation.' + return euclidean.getDictionaryString(self.__dict__) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/creation/lathe.py b/SkeinPyPy/fabmetheus_utilities/geometry/creation/lathe.py new file mode 100644 index 0000000..b61cfaa --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/creation/lathe.py @@ -0,0 +1,176 @@ +""" +Boolean geometry extrusion. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.creation import solid +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + +def addLoopByComplex(derivation, endMultiplier, loopLists, path, pointComplex, vertexes): + "Add an indexed loop to the vertexes." + loops = loopLists[-1] + loop = [] + loops.append(loop) + for point in path: + pointMinusBegin = point - derivation.axisStart + dotVector3 = derivation.axisProjectiveSpace.getDotVector3(pointMinusBegin) + dotVector3Complex = dotVector3.dropAxis() + dotPointComplex = pointComplex * dotVector3Complex + dotPoint = Vector3(dotPointComplex.real, dotPointComplex.imag, dotVector3.z) + projectedVector3 = derivation.axisProjectiveSpace.getVector3ByPoint(dotPoint) + derivation.axisStart + loop.append(projectedVector3) + +def addNegatives(derivation, negatives, paths): + "Add pillars output to negatives." + for path in paths: + loopListsByPath = getLoopListsByPath(derivation, 1.000001, path) + geometryOutput = triangle_mesh.getPillarsOutput(loopListsByPath) + negatives.append(geometryOutput) + +def addNegativesPositives(derivation, negatives, paths, positives): + "Add pillars output to negatives and positives." + for path in paths: + endMultiplier = None + normal = euclidean.getNormalByPath(path) + if normal.dot(derivation.normal) < 0.0: + endMultiplier = 1.000001 + loopListsByPath = getLoopListsByPath(derivation, endMultiplier, path) + geometryOutput = triangle_mesh.getPillarsOutput(loopListsByPath) + if endMultiplier == None: + positives.append(geometryOutput) + else: + negatives.append(geometryOutput) + +def addOffsetAddToLists( loop, offset, vector3Index, vertexes ): + "Add an indexed loop to the vertexes." + vector3Index += offset + loop.append( vector3Index ) + vertexes.append( vector3Index ) + +def addPositives(derivation, paths, positives): + "Add pillars output to positives." + for path in paths: + loopListsByPath = getLoopListsByPath(derivation, None, path) + geometryOutput = triangle_mesh.getPillarsOutput(loopListsByPath) + positives.append(geometryOutput) + +def getGeometryOutput(derivation, elementNode): + "Get triangle mesh from attribute dictionary." + if derivation == None: + derivation = LatheDerivation(elementNode) + if len(euclidean.getConcatenatedList(derivation.target)) == 0: + print('Warning, in lathe there are no paths.') + print(elementNode.attributes) + return None + negatives = [] + positives = [] + addNegativesPositives(derivation, negatives, derivation.target, positives) + return getGeometryOutputByNegativesPositives(derivation, elementNode, negatives, positives) + +def getGeometryOutputByArguments(arguments, elementNode): + "Get triangle mesh from attribute dictionary by arguments." + return getGeometryOutput(None, elementNode) + +def getGeometryOutputByNegativesPositives(derivation, elementNode, negatives, positives): + "Get triangle mesh from derivation, elementNode, negatives and positives." + positiveOutput = triangle_mesh.getUnifiedOutput(positives) + if len(negatives) < 1: + return solid.getGeometryOutputByManipulation(elementNode, positiveOutput) + return solid.getGeometryOutputByManipulation(elementNode, {'difference' : {'shapes' : [positiveOutput] + negatives}}) + +def getLoopListsByPath(derivation, endMultiplier, path): + "Get loop lists from path." + vertexes = [] + loopLists = [[]] + if len(derivation.loop) < 2: + return loopLists + for pointIndex, pointComplex in enumerate(derivation.loop): + if endMultiplier != None and not derivation.isEndCloseToStart: + if pointIndex == 0: + nextPoint = derivation.loop[1] + pointComplex = endMultiplier * (pointComplex - nextPoint) + nextPoint + elif pointIndex == len(derivation.loop) - 1: + previousPoint = derivation.loop[pointIndex - 1] + pointComplex = endMultiplier * (pointComplex - previousPoint) + previousPoint + addLoopByComplex(derivation, endMultiplier, loopLists, path, pointComplex, vertexes) + if derivation.isEndCloseToStart: + loopLists[-1].append([]) + return loopLists + +def getNewDerivation(elementNode): + 'Get new derivation.' + return LatheDerivation(elementNode) + +def processElementNode(elementNode): + "Process the xml element." + solid.processElementNodeByGeometry(elementNode, getGeometryOutput(None, elementNode)) + + +class LatheDerivation: + "Class to hold lathe variables." + def __init__(self, elementNode): + 'Set defaults.' + self.axisEnd = evaluate.getVector3ByPrefix(None, elementNode, 'axisEnd') + self.axisStart = evaluate.getVector3ByPrefix(None, elementNode, 'axisStart') + self.end = evaluate.getEvaluatedFloat(360.0, elementNode, 'end') + self.loop = evaluate.getTransformedPathByKey([], elementNode, 'loop') + self.sides = evaluate.getEvaluatedInt(None, elementNode, 'sides') + self.start = evaluate.getEvaluatedFloat(0.0, elementNode, 'start') + self.target = evaluate.getTransformedPathsByKey([], elementNode, 'target') + if len(self.target) < 1: + print('Warning, no target in derive in lathe for:') + print(elementNode) + return + firstPath = self.target[0] + if len(firstPath) < 3: + print('Warning, firstPath length is less than three in derive in lathe for:') + print(elementNode) + self.target = [] + return + if self.axisStart == None: + if self.axisEnd == None: + self.axisStart = firstPath[0] + self.axisEnd = firstPath[-1] + else: + self.axisStart = Vector3() + self.axis = self.axisEnd - self.axisStart + axisLength = abs(self.axis) + if axisLength <= 0.0: + print('Warning, axisLength is zero in derive in lathe for:') + print(elementNode) + self.target = [] + return + self.axis /= axisLength + firstVector3 = firstPath[1] - self.axisStart + firstVector3Length = abs(firstVector3) + if firstVector3Length <= 0.0: + print('Warning, firstVector3Length is zero in derive in lathe for:') + print(elementNode) + self.target = [] + return + firstVector3 /= firstVector3Length + self.axisProjectiveSpace = euclidean.ProjectiveSpace().getByBasisZFirst(self.axis, firstVector3) + if self.sides == None: + distanceToLine = euclidean.getDistanceToLineByPaths(self.axisStart, self.axisEnd, self.target) + self.sides = evaluate.getSidesMinimumThreeBasedOnPrecisionSides(elementNode, distanceToLine) + endRadian = math.radians(self.end) + startRadian = math.radians(self.start) + self.isEndCloseToStart = euclidean.getIsRadianClose(endRadian, startRadian) + if len(self.loop) < 1: + self.loop = euclidean.getComplexPolygonByStartEnd(endRadian, 1.0, self.sides, startRadian) + self.normal = euclidean.getNormalByPath(firstPath) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/creation/line.py b/SkeinPyPy/fabmetheus_utilities/geometry/creation/line.py new file mode 100644 index 0000000..c0eae8b --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/creation/line.py @@ -0,0 +1,104 @@ +""" +Square path. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.geometry_tools import path +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getGeometryOutput(derivation, elementNode): + "Get vector3 vertexes from attribute dictionary." + if derivation == None: + derivation = LineDerivation(elementNode) + endMinusStart = derivation.end - derivation.start + endMinusStartLength = abs(endMinusStart) + if endMinusStartLength <= 0.0: + print('Warning, end is the same as start in getGeometryOutput in line for:') + print(derivation.start) + print(derivation.end) + print(elementNode) + return None + typeStringTwoCharacters = derivation.typeString.lower()[: 2] + elementNode.attributes['closed'] = str(derivation.closed) + if derivation.step == None and derivation.steps == None: + return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop([derivation.start, derivation.end])) + loop = [derivation.start] + if derivation.step != None and derivation.steps != None: + stepVector = derivation.step / endMinusStartLength * endMinusStart + derivation.end = derivation.start + stepVector * derivation.steps + return getGeometryOutputByStep(elementNode, derivation.end, loop, derivation.steps, stepVector) + if derivation.step == None: + stepVector = endMinusStart / derivation.steps + return getGeometryOutputByStep(elementNode, derivation.end, loop, derivation.steps, stepVector) + endMinusStartLengthOverStep = endMinusStartLength / derivation.step + if typeStringTwoCharacters == 'av': + derivation.steps = max(1.0, round(endMinusStartLengthOverStep)) + stepVector = derivation.step / endMinusStartLength * endMinusStart + derivation.end = derivation.start + stepVector * derivation.steps + return getGeometryOutputByStep(elementNode, derivation.end, loop, derivation.steps, stepVector) + if typeStringTwoCharacters == 'ma': + derivation.steps = math.ceil(endMinusStartLengthOverStep) + if derivation.steps < 1.0: + return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop([derivation.start, derivation.end])) + stepVector = endMinusStart / derivation.steps + return getGeometryOutputByStep(elementNode, derivation.end, loop, derivation.steps, stepVector) + if typeStringTwoCharacters == 'mi': + derivation.steps = math.floor(endMinusStartLengthOverStep) + if derivation.steps < 1.0: + return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop(loop)) + stepVector = endMinusStart / derivation.steps + return getGeometryOutputByStep(elementNode, derivation.end, loop, derivation.steps, stepVector) + print('Warning, the step type was not one of (average, maximum or minimum) in getGeometryOutput in line for:') + print(derivation.typeString) + print(elementNode) + loop.append(derivation.end) + return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop(loop)) + +def getGeometryOutputByArguments(arguments, elementNode): + "Get vector3 vertexes from attribute dictionary by arguments." + evaluate.setAttributesByArguments(['start', 'end', 'step'], arguments, elementNode) + return getGeometryOutput(None, elementNode) + +def getGeometryOutputByStep(elementNode, end, loop, steps, stepVector): + "Get line geometry output by the end, loop, steps and stepVector." + stepsFloor = int(math.floor(abs(steps))) + for stepIndex in xrange(1, stepsFloor): + loop.append(loop[stepIndex - 1] + stepVector) + loop.append(end) + return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop(loop)) + +def getNewDerivation(elementNode): + 'Get new derivation.' + return LineDerivation(elementNode) + +def processElementNode(elementNode): + "Process the xml element." + path.convertElementNode(elementNode, getGeometryOutput(None, elementNode)) + + +class LineDerivation: + "Class to hold line variables." + def __init__(self, elementNode): + 'Set defaults.' + self.closed = evaluate.getEvaluatedBoolean(False, elementNode, 'closed') + self.end = evaluate.getVector3ByPrefix(Vector3(), elementNode, 'end') + self.start = evaluate.getVector3ByPrefix(Vector3(), elementNode, 'start') + self.step = evaluate.getEvaluatedFloat(None, elementNode, 'step') + self.steps = evaluate.getEvaluatedFloat(None, elementNode, 'steps') + self.typeMenuRadioStrings = 'average maximum minimum'.split() + self.typeString = evaluate.getEvaluatedString('minimum', elementNode, 'type') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/creation/linear_bearing_cage.py b/SkeinPyPy/fabmetheus_utilities/geometry/creation/linear_bearing_cage.py new file mode 100644 index 0000000..d972f63 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/creation/linear_bearing_cage.py @@ -0,0 +1,247 @@ +""" +Linear bearing cage. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import extrude +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.creation import peg +from fabmetheus_utilities.geometry.creation import solid +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities.geometry.manipulation_matrix import translate +from fabmetheus_utilities.geometry.solids import cylinder +from fabmetheus_utilities.geometry.solids import sphere +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def addAssemblyCage(derivation, negatives, positives): + 'Add assembly linear bearing cage.' + addCageGroove(derivation, negatives, positives) + for pegCenterX in derivation.pegCenterXs: + addPositivePeg(derivation, positives, pegCenterX, -derivation.pegY) + addPositivePeg(derivation, positives, pegCenterX, derivation.pegY) + translate.translateNegativesPositives(negatives, positives, Vector3(0.0, -derivation.halfSeparationWidth)) + femaleNegatives = [] + femalePositives = [] + addCageGroove(derivation, femaleNegatives, femalePositives) + for pegCenterX in derivation.pegCenterXs: + addNegativePeg(derivation, femaleNegatives, pegCenterX, -derivation.pegY) + addNegativePeg(derivation, femaleNegatives, pegCenterX, derivation.pegY) + translate.translateNegativesPositives(femaleNegatives, femalePositives, Vector3(0.0, derivation.halfSeparationWidth)) + negatives += femaleNegatives + positives += femalePositives + +def addCage(derivation, height, negatives, positives): + 'Add linear bearing cage.' + copyShallow = derivation.elementNode.getCopyShallow() + copyShallow.attributes['path'] = [Vector3(), Vector3(0.0, 0.0, height)] + extrudeDerivation = extrude.ExtrudeDerivation(copyShallow) + roundedExtendedRectangle = getRoundedExtendedRectangle(derivation.demiwidth, derivation.rectangleCenterX, 14) + outsidePath = euclidean.getVector3Path(roundedExtendedRectangle) + extrude.addPositives(extrudeDerivation, [outsidePath], positives) + for bearingCenterX in derivation.bearingCenterXs: + addNegativeSphere(derivation, negatives, bearingCenterX) + +def addCageGroove(derivation, negatives, positives): + 'Add cage and groove.' + addCage(derivation, derivation.demiheight, negatives, positives) + addGroove(derivation, negatives) + +def addGroove(derivation, negatives): + 'Add groove on each side of cage.' + copyShallow = derivation.elementNode.getCopyShallow() + extrude.setElementNodeToEndStart(copyShallow, Vector3(-derivation.demilength), Vector3(derivation.demilength)) + extrudeDerivation = extrude.ExtrudeDerivation(copyShallow) + bottom = derivation.demiheight - 0.5 * derivation.grooveWidth + outside = derivation.demiwidth + top = derivation.demiheight + leftGroove = [ + complex(-outside, bottom), + complex(-derivation.innerDemiwidth, derivation.demiheight), + complex(-outside, top)] + rightGroove = [ + complex(outside, top), + complex(derivation.innerDemiwidth, derivation.demiheight), + complex(outside, bottom)] + extrude.addNegatives(extrudeDerivation, negatives, euclidean.getVector3Paths([leftGroove, rightGroove])) + +def addNegativePeg(derivation, negatives, x, y): + 'Add negative cylinder at x and y.' + negativePegRadius = derivation.pegRadiusArealized + derivation.halfPegClearance + inradius = complex(negativePegRadius, negativePegRadius) + copyShallow = derivation.elementNode.getCopyShallow() + start = Vector3(x, y, derivation.height) + sides = evaluate.getSidesMinimumThreeBasedOnPrecision(copyShallow, negativePegRadius) + cylinder.addCylinderOutputByEndStart(0.0, inradius, negatives, sides, start, derivation.topOverBottom) + +def addNegativeSphere(derivation, negatives, x): + 'Add negative sphere at x.' + radius = Vector3(derivation.radiusPlusClearance, derivation.radiusPlusClearance, derivation.radiusPlusClearance) + sphereOutput = sphere.getGeometryOutput(derivation.elementNode.getCopyShallow(), radius) + euclidean.translateVector3Path(matrix.getVertexes(sphereOutput), Vector3(x, 0.0, derivation.demiheight)) + negatives.append(sphereOutput) + +def addPositivePeg(derivation, positives, x, y): + 'Add positive cylinder at x and y.' + positivePegRadius = derivation.pegRadiusArealized - derivation.halfPegClearance + radiusArealized = complex(positivePegRadius, positivePegRadius) + copyShallow = derivation.elementNode.getCopyShallow() + start = Vector3(x, y, derivation.demiheight) + endZ = derivation.height + peg.addPegOutput(derivation.pegBevel, endZ, positives, radiusArealized, derivation.sides, start, derivation.topOverBottom) + +def getBearingCenterXs(bearingCenterX, numberOfSteps, stepX): + 'Get the bearing center x list.' + bearingCenterXs = [] + for stepIndex in xrange(numberOfSteps + 1): + bearingCenterXs.append(bearingCenterX) + bearingCenterX += stepX + return bearingCenterXs + +def getGeometryOutput(elementNode): + 'Get vector3 vertexes from attribute dictionary.' + derivation = LinearBearingCageDerivation(elementNode) + negatives = [] + positives = [] + if derivation.typeStringFirstCharacter == 'a': + addAssemblyCage(derivation, negatives, positives) + else: + addCage(derivation, derivation.height, negatives, positives) + return extrude.getGeometryOutputByNegativesPositives(elementNode, negatives, positives) + +def getGeometryOutputByArguments(arguments, elementNode): + 'Get vector3 vertexes from attribute dictionary by arguments.' + evaluate.setAttributesByArguments(['length', 'radius'], arguments, elementNode) + return getGeometryOutput(elementNode) + +def getNewDerivation(elementNode): + 'Get new derivation.' + return LinearBearingCageDerivation(elementNode) + +def getPegCenterXs(numberOfSteps, pegCenterX, stepX): + 'Get the peg center x list.' + pegCenterXs = [] + for stepIndex in xrange(numberOfSteps): + pegCenterXs.append(pegCenterX) + pegCenterX += stepX + return pegCenterXs + +def getRoundedExtendedRectangle(radius, rectangleCenterX, sides): + 'Get the rounded extended rectangle.' + roundedExtendedRectangle = [] + halfSides = int(sides / 2) + halfSidesPlusOne = abs(halfSides + 1) + sideAngle = math.pi / float(halfSides) + extensionMultiplier = 1.0 / math.cos(0.5 * sideAngle) + center = complex(rectangleCenterX, 0.0) + startAngle = 0.5 * math.pi + for halfSide in xrange(halfSidesPlusOne): + unitPolar = euclidean.getWiddershinsUnitPolar(startAngle) + unitPolarExtended = complex(unitPolar.real * extensionMultiplier, unitPolar.imag) + roundedExtendedRectangle.append(unitPolarExtended * radius + center) + startAngle += sideAngle + center = complex(-rectangleCenterX, 0.0) + startAngle = -0.5 * math.pi + for halfSide in xrange(halfSidesPlusOne): + unitPolar = euclidean.getWiddershinsUnitPolar(startAngle) + unitPolarExtended = complex(unitPolar.real * extensionMultiplier, unitPolar.imag) + roundedExtendedRectangle.append(unitPolarExtended * radius + center) + startAngle += sideAngle + return roundedExtendedRectangle + +def processElementNode(elementNode): + 'Process the xml element.' + solid.processElementNodeByGeometry(elementNode, getGeometryOutput(elementNode)) + + +class LinearBearingCageDerivation: + 'Class to hold linear bearing cage variables.' + def __init__(self, elementNode): + 'Set defaults.' + self.length = evaluate.getEvaluatedFloat(50.0, elementNode, 'length') + self.demilength = 0.5 * self.length + self.elementNode = elementNode + self.radius = lineation.getFloatByPrefixBeginEnd(elementNode, 'radius', 'diameter', 5.0) + self.cageClearanceOverRadius = evaluate.getEvaluatedFloat(0.05, elementNode, 'cageClearanceOverRadius') + self.cageClearance = self.cageClearanceOverRadius * self.radius + self.cageClearance = evaluate.getEvaluatedFloat(self.cageClearance, elementNode, 'cageClearance') + self.racewayClearanceOverRadius = evaluate.getEvaluatedFloat(0.1, elementNode, 'racewayClearanceOverRadius') + self.racewayClearance = self.racewayClearanceOverRadius * self.radius + self.racewayClearance = evaluate.getEvaluatedFloat(self.racewayClearance, elementNode, 'racewayClearance') + self.typeMenuRadioStrings = 'assembly integral'.split() + self.typeString = evaluate.getEvaluatedString('assembly', elementNode, 'type') + self.typeStringFirstCharacter = self.typeString[: 1 ].lower() + self.wallThicknessOverRadius = evaluate.getEvaluatedFloat(0.5, elementNode, 'wallThicknessOverRadius') + self.wallThickness = self.wallThicknessOverRadius * self.radius + self.wallThickness = evaluate.getEvaluatedFloat(self.wallThickness, elementNode, 'wallThickness') + self.zenithAngle = evaluate.getEvaluatedFloat(45.0, elementNode, 'zenithAngle') + self.zenithRadian = math.radians(self.zenithAngle) + self.demiheight = self.radius * math.cos(self.zenithRadian) - self.racewayClearance + self.height = self.demiheight + self.demiheight + self.radiusPlusClearance = self.radius + self.cageClearance + self.cageRadius = self.radiusPlusClearance + self.wallThickness + self.demiwidth = self.cageRadius + self.bearingCenterX = self.cageRadius - self.demilength + separation = self.cageRadius + self.radiusPlusClearance + bearingLength = -self.bearingCenterX - self.bearingCenterX + self.numberOfSteps = int(math.floor(bearingLength / separation)) + self.stepX = bearingLength / float(self.numberOfSteps) + self.bearingCenterXs = getBearingCenterXs(self.bearingCenterX, self.numberOfSteps, self.stepX) + if self.typeStringFirstCharacter == 'a': + self.setAssemblyCage() + self.rectangleCenterX = self.demiwidth - self.demilength + + def setAssemblyCage(self): + 'Set two piece assembly parameters.' + self.grooveDepthOverRadius = evaluate.getEvaluatedFloat(0.15, self.elementNode, 'grooveDepthOverRadius') + self.grooveDepth = self.grooveDepthOverRadius * self.radius + self.grooveDepth = evaluate.getEvaluatedFloat(self.grooveDepth, self.elementNode, 'grooveDepth') + self.grooveWidthOverRadius = evaluate.getEvaluatedFloat(0.6, self.elementNode, 'grooveWidthOverRadius') + self.grooveWidth = self.grooveWidthOverRadius * self.radius + self.grooveWidth = evaluate.getEvaluatedFloat(self.grooveWidth, self.elementNode, 'grooveWidth') + self.pegClearanceOverRadius = evaluate.getEvaluatedFloat(0.0, self.elementNode, 'pegClearanceOverRadius') + self.pegClearance = self.pegClearanceOverRadius * self.radius + self.pegClearance = evaluate.getEvaluatedFloat(self.pegClearance, self.elementNode, 'pegClearance') + self.halfPegClearance = 0.5 * self.pegClearance + self.pegRadiusOverRadius = evaluate.getEvaluatedFloat(0.5, self.elementNode, 'pegRadiusOverRadius') + self.pegRadius = self.pegRadiusOverRadius * self.radius + self.pegRadius = evaluate.getEvaluatedFloat(self.pegRadius, self.elementNode, 'pegRadius') + self.sides = evaluate.getSidesMinimumThreeBasedOnPrecision(self.elementNode, self.pegRadius) + self.pegRadiusArealized = evaluate.getRadiusArealizedBasedOnAreaRadius(self.elementNode, self.pegRadius, self.sides) + self.pegBevelOverPegRadius = evaluate.getEvaluatedFloat(0.25, self.elementNode, 'pegBevelOverPegRadius') + self.pegBevel = self.pegBevelOverPegRadius * self.pegRadiusArealized + self.pegBevel = evaluate.getEvaluatedFloat(self.pegBevel, self.elementNode, 'pegBevel') + self.pegMaximumRadius = self.pegRadiusArealized + abs(self.halfPegClearance) + self.separationOverRadius = evaluate.getEvaluatedFloat(0.5, self.elementNode, 'separationOverRadius') + self.separation = self.separationOverRadius * self.radius + self.separation = evaluate.getEvaluatedFloat(self.separation, self.elementNode, 'separation') + self.topOverBottom = evaluate.getEvaluatedFloat(0.8, self.elementNode, 'topOverBottom') + peg.setTopOverBottomByRadius(self, 0.0, self.pegRadiusArealized, self.height) + self.quarterHeight = 0.5 * self.demiheight + self.pegY = 0.5 * self.wallThickness + self.pegMaximumRadius + cagePegRadius = self.cageRadius + self.pegMaximumRadius + halfStepX = 0.5 * self.stepX + pegHypotenuse = math.sqrt(self.pegY * self.pegY + halfStepX * halfStepX) + if cagePegRadius > pegHypotenuse: + self.pegY = math.sqrt(cagePegRadius * cagePegRadius - halfStepX * halfStepX) + self.demiwidth = max(self.pegY + self.pegMaximumRadius + self.wallThickness, self.demiwidth) + self.innerDemiwidth = self.demiwidth + self.demiwidth += self.grooveDepth + self.halfSeparationWidth = self.demiwidth + 0.5 * self.separation + if self.pegRadiusArealized <= 0.0: + self.pegCenterXs = [] + else: + self.pegCenterXs = getPegCenterXs(self.numberOfSteps, self.bearingCenterX + halfStepX, self.stepX) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/creation/lineation.py b/SkeinPyPy/fabmetheus_utilities/geometry/creation/lineation.py new file mode 100644 index 0000000..29f7bbb --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/creation/lineation.py @@ -0,0 +1,308 @@ +""" +Polygon path. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_tools import path +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getComplexByDictionary(dictionary, valueComplex): + 'Get complex by dictionary.' + if 'x' in dictionary: + valueComplex = complex(euclidean.getFloatFromValue(dictionary['x']),valueComplex.imag) + if 'y' in dictionary: + valueComplex = complex(valueComplex.real, euclidean.getFloatFromValue(dictionary['y'])) + return valueComplex + +def getComplexByDictionaryListValue(value, valueComplex): + 'Get complex by dictionary, list or value.' + if value.__class__ == complex: + return value + if value.__class__ == dict: + return getComplexByDictionary(value, valueComplex) + if value.__class__ == list: + return getComplexByFloatList(value, valueComplex) + floatFromValue = euclidean.getFloatFromValue(value) + if floatFromValue == None: + return valueComplex + return complex( floatFromValue, floatFromValue ) + +def getComplexByFloatList( floatList, valueComplex ): + 'Get complex by float list.' + if len(floatList) > 0: + valueComplex = complex(euclidean.getFloatFromValue(floatList[0]), valueComplex.imag) + if len(floatList) > 1: + valueComplex = complex(valueComplex.real, euclidean.getFloatFromValue(floatList[1])) + return valueComplex + +def getComplexByMultiplierPrefix(elementNode, multiplier, prefix, valueComplex): + 'Get complex from multiplier, prefix and xml element.' + if multiplier == 0.0: + return valueComplex + oldMultipliedValueComplex = valueComplex * multiplier + complexByPrefix = getComplexByPrefix(elementNode, prefix, oldMultipliedValueComplex) + if complexByPrefix == oldMultipliedValueComplex: + return valueComplex + return complexByPrefix / multiplier + +def getComplexByMultiplierPrefixes(elementNode, multiplier, prefixes, valueComplex): + 'Get complex from multiplier, prefixes and xml element.' + for prefix in prefixes: + valueComplex = getComplexByMultiplierPrefix(elementNode, multiplier, prefix, valueComplex) + return valueComplex + +def getComplexByPrefix(elementNode, prefix, valueComplex): + 'Get complex from prefix and xml element.' + value = evaluate.getEvaluatedValue(None, elementNode, prefix) + if value != None: + valueComplex = getComplexByDictionaryListValue(value, valueComplex) + x = evaluate.getEvaluatedFloat(None, elementNode, prefix + '.x') + if x != None: + valueComplex = complex( x, getComplexIfNone( valueComplex ).imag ) + y = evaluate.getEvaluatedFloat(None, elementNode, prefix + '.y') + if y != None: + valueComplex = complex( getComplexIfNone( valueComplex ).real, y ) + return valueComplex + +def getComplexByPrefixBeginEnd(elementNode, prefixBegin, prefixEnd, valueComplex): + 'Get complex from element node, prefixBegin and prefixEnd.' + valueComplex = getComplexByPrefix(elementNode, prefixBegin, valueComplex) + if prefixEnd in elementNode.attributes: + return 0.5 * getComplexByPrefix(elementNode, valueComplex + valueComplex, prefixEnd) + else: + return valueComplex + +def getComplexByPrefixes(elementNode, prefixes, valueComplex): + 'Get complex from prefixes and xml element.' + for prefix in prefixes: + valueComplex = getComplexByPrefix(elementNode, prefix, valueComplex) + return valueComplex + +def getComplexIfNone( valueComplex ): + 'Get new complex if the original complex is none.' + if valueComplex == None: + return complex() + return valueComplex + +def getFloatByPrefixBeginEnd(elementNode, prefixBegin, prefixEnd, valueFloat): + 'Get float from prefixBegin, prefixEnd and xml element.' + valueFloat = evaluate.getEvaluatedFloat(valueFloat, elementNode, prefixBegin) + if prefixEnd in elementNode.attributes: + return 0.5 * evaluate.getEvaluatedFloat(valueFloat + valueFloat, elementNode, prefixEnd) + return valueFloat + +def getFloatByPrefixSide(defaultValue, elementNode, prefix, side): + 'Get float by prefix and side.' + if elementNode == None: + return defaultValue + if side != None: + key = prefix + 'OverSide' + if key in elementNode.attributes: + defaultValue = euclidean.getFloatFromValue(evaluate.getEvaluatedValueObliviously(elementNode, key)) * side + return evaluate.getEvaluatedFloat(defaultValue, elementNode, prefix) + +def getGeometryOutput(derivation, elementNode): + 'Get geometry output from paths.' + if derivation == None: + derivation = LineationDerivation(elementNode) + geometryOutput = [] + for path in derivation.target: + sideLoop = SideLoop(path) + geometryOutput += getGeometryOutputByLoop(elementNode, sideLoop) + return geometryOutput + +def getGeometryOutputByArguments(arguments, elementNode): + 'Get vector3 vertexes from attribute dictionary by arguments.' + return getGeometryOutput(None, elementNode) + +def getGeometryOutputByLoop(elementNode, sideLoop): + 'Get geometry output by side loop.' + sideLoop.rotate(elementNode) + return getGeometryOutputByManipulation(elementNode, sideLoop) + +def getGeometryOutputByManipulation(elementNode, sideLoop): + 'Get geometry output by manipulation.' + sideLoop.loop = euclidean.getLoopWithoutCloseSequentialPoints( sideLoop.close, sideLoop.loop ) + return sideLoop.getManipulationPluginLoops(elementNode) + +def getInradius(defaultInradius, elementNode): + 'Get inradius.' + defaultInradius = getComplexByPrefixes(elementNode, ['demisize', 'inradius'], defaultInradius) + return getComplexByMultiplierPrefix(elementNode, 2.0, 'size', defaultInradius) + +def getMinimumRadius(beginComplexSegmentLength, endComplexSegmentLength, radius): + 'Get minimum radius.' + return min(abs(radius), 0.5 * min(beginComplexSegmentLength, endComplexSegmentLength)) + +def getNewDerivation(elementNode): + 'Get new derivation.' + return LineationDerivation(elementNode) + +def getNumberOfBezierPoints(begin, elementNode, end): + 'Get the numberOfBezierPoints.' + numberOfBezierPoints = int(math.ceil(0.5 * evaluate.getSidesMinimumThreeBasedOnPrecision(elementNode, abs(end - begin)))) + return evaluate.getEvaluatedInt(numberOfBezierPoints, elementNode, 'sides') + +def getPackedGeometryOutputByLoop(elementNode, sideLoop): + 'Get packed geometry output by side loop.' + sideLoop.rotate(elementNode) + return getGeometryOutputByManipulation(elementNode, sideLoop) + +def getRadiusAverage(radiusComplex): + 'Get average radius from radiusComplex.' + return math.sqrt(radiusComplex.real * radiusComplex.imag) + +def getRadiusComplex(elementNode, radius): + 'Get radius complex for elementNode.' + radius = getComplexByPrefixes(elementNode, ['demisize', 'radius'], radius) + return getComplexByMultiplierPrefixes(elementNode, 2.0, ['diameter', 'size'], radius) + +def getStrokeRadiusByPrefix(elementNode, prefix): + 'Get strokeRadius by prefix.' + strokeRadius = getFloatByPrefixBeginEnd(elementNode, prefix + 'strokeRadius', prefix + 'strokeWidth', 1.0) + return getFloatByPrefixBeginEnd(elementNode, prefix + 'radius', prefix + 'diameter', strokeRadius) + +def processElementNode(elementNode): + 'Process the xml element.' + path.convertElementNode(elementNode, getGeometryOutput(None, elementNode)) + +def processElementNodeByFunction(elementNode, manipulationFunction): + 'Process the xml element by the manipulationFunction.' + elementAttributesCopy = elementNode.attributes.copy() + targets = evaluate.getElementNodesByKey(elementNode, 'target') + for target in targets: + targetAttributesCopy = target.attributes.copy() + target.attributes = elementAttributesCopy + processTargetByFunction(manipulationFunction, target) + target.attributes = targetAttributesCopy + +def processTargetByFunction(manipulationFunction, target): + 'Process the target by the manipulationFunction.' + if target.xmlObject == None: + print('Warning, there is no object in processTargetByFunction in lineation for:') + print(target) + return + geometryOutput = [] + transformedPaths = target.xmlObject.getTransformedPaths() + for transformedPath in transformedPaths: + sideLoop = SideLoop(transformedPath) + sideLoop.rotate(target) + sideLoop.loop = euclidean.getLoopWithoutCloseSequentialPoints( sideLoop.close, sideLoop.loop ) + geometryOutput += manipulationFunction(sideLoop.close, target, sideLoop.loop, '', sideLoop.sideLength) + if len(geometryOutput) < 1: + print('Warning, there is no geometryOutput in processTargetByFunction in lineation for:') + print(target) + return + removeChildNodesFromElementObject(target) + path.convertElementNode(target, geometryOutput) + +def removeChildNodesFromElementObject(elementNode): + 'Process the xml element by manipulationFunction.' + elementNode.removeChildNodesFromIDNameParent() + if elementNode.xmlObject != None: + if elementNode.parentNode.xmlObject != None: + if elementNode.xmlObject in elementNode.parentNode.xmlObject.archivableObjects: + elementNode.parentNode.xmlObject.archivableObjects.remove(elementNode.xmlObject) + +def setClosedAttribute(elementNode, revolutions): + 'Set the closed attribute of the elementNode.' + closedBoolean = evaluate.getEvaluatedBoolean(revolutions <= 1, elementNode, 'closed') + elementNode.attributes['closed'] = str(closedBoolean).lower() + + +class LineationDerivation: + 'Class to hold lineation variables.' + def __init__(self, elementNode): + 'Set defaults.' + self.target = evaluate.getTransformedPathsByKey([], elementNode, 'target') + + +class SideLoop: + 'Class to handle loop, side angle and side length.' + def __init__(self, loop, sideAngle=None, sideLength=None): + 'Initialize.' + if sideAngle == None: + if len(loop) > 0: + sideAngle = 2.0 * math.pi / float(len(loop)) + else: + sideAngle = 1.0 + print('Warning, loop has no sides in SideLoop in lineation.') + if sideLength == None: + if len(loop) > 0: + sideLength = euclidean.getLoopLength(loop) / float(len(loop)) + else: + sideLength = 1.0 + print('Warning, loop has no length in SideLoop in lineation.') + self.loop = loop + self.sideAngle = abs(sideAngle) + self.sideLength = abs(sideLength) + self.close = 0.001 * sideLength + + def getManipulationPluginLoops(self, elementNode): + 'Get loop manipulated by the plugins in the manipulation paths folder.' + xmlProcessor = elementNode.getXMLProcessor() + matchingPlugins = evaluate.getMatchingPlugins(elementNode, xmlProcessor.manipulationMatrixDictionary) + matchingPlugins += evaluate.getMatchingPlugins(elementNode, xmlProcessor.manipulationPathDictionary) + matchingPlugins += evaluate.getMatchingPlugins(elementNode, xmlProcessor.manipulationShapeDictionary) + matchingPlugins.sort(evaluate.compareExecutionOrderAscending) + loops = [self.loop] + for matchingPlugin in matchingPlugins: + matchingLoops = [] + prefix = matchingPlugin.__name__.replace('_', '') + '.' + for loop in loops: + matchingLoops += matchingPlugin.getManipulatedPaths(self.close, elementNode, loop, prefix, self.sideLength) + loops = matchingLoops + return loops + + def rotate(self, elementNode): + 'Rotate.' + rotation = math.radians(evaluate.getEvaluatedFloat(0.0, elementNode, 'rotation')) + rotation += evaluate.getEvaluatedFloat(0.0, elementNode, 'rotationOverSide') * self.sideAngle + if rotation != 0.0: + planeRotation = euclidean.getWiddershinsUnitPolar( rotation ) + for vertex in self.loop: + rotatedComplex = vertex.dropAxis() * planeRotation + vertex.x = rotatedComplex.real + vertex.y = rotatedComplex.imag + if 'clockwise' in elementNode.attributes: + isClockwise = euclidean.getBooleanFromValue(evaluate.getEvaluatedValueObliviously(elementNode, 'clockwise')) + if isClockwise == euclidean.getIsWiddershinsByVector3( self.loop ): + self.loop.reverse() + + +class Spiral: + 'Class to add a spiral.' + def __init__(self, spiral, stepRatio): + 'Initialize.' + self.spiral = spiral + if self.spiral == None: + return + self.spiralIncrement = self.spiral * stepRatio + self.spiralTotal = Vector3() + + def __repr__(self): + 'Get the string representation of this Spiral.' + return self.spiral + + def getSpiralPoint(self, unitPolar, vector3): + 'Add spiral to the vector.' + if self.spiral == None: + return vector3 + vector3 += Vector3(unitPolar.real * self.spiralTotal.x, unitPolar.imag * self.spiralTotal.y, self.spiralTotal.z) + self.spiralTotal += self.spiralIncrement + return vector3 diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/creation/mechaslab.py b/SkeinPyPy/fabmetheus_utilities/geometry/creation/mechaslab.py new file mode 100644 index 0000000..4a9922e --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/creation/mechaslab.py @@ -0,0 +1,259 @@ +""" +Mechaslab. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import extrude +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.creation import peg +from fabmetheus_utilities.geometry.creation import solid +from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities.geometry.manipulation_matrix import translate +from fabmetheus_utilities.geometry.solids import cylinder +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def addAlongWay(begin, distance, end, loop): + 'Get the beveled rectangle.' + endMinusBegin = end - begin + endMinusBeginLength = abs(endMinusBegin) + if endMinusBeginLength <= 0.0: + return + alongWayMultiplier = distance / endMinusBeginLength + loop.append(begin + alongWayMultiplier * endMinusBegin) + +def addGroove(derivation, negatives): + 'Add groove on each side of cage.' + copyShallow = derivation.elementNode.getCopyShallow() + extrude.setElementNodeToEndStart(copyShallow, Vector3(-derivation.demilength), Vector3(derivation.demilength)) + extrudeDerivation = extrude.ExtrudeDerivation(copyShallow) + bottom = derivation.demiheight - 0.5 * derivation.grooveWidth + outside = derivation.demiwidth + top = derivation.demiheight + leftGroove = [ + complex(-outside, bottom), + complex(-derivation.innerDemiwidth, derivation.demiheight), + complex(-outside, top)] + rightGroove = [ + complex(outside, top), + complex(derivation.innerDemiwidth, derivation.demiheight), + complex(outside, bottom)] + groovesComplex = [leftGroove, rightGroove] + groovesVector3 = euclidean.getVector3Paths(groovesComplex) + extrude.addPositives(extrudeDerivation, groovesVector3, negatives) + +def addHollowPegSocket(derivation, hollowPegSocket, negatives, positives): + 'Add the socket and hollow peg.' + pegHeight = derivation.pegHeight + pegRadians = derivation.pegRadians + pegRadiusComplex = complex(derivation.pegRadiusArealized, derivation.pegRadiusArealized) + pegTip = 0.8 * derivation.pegRadiusArealized + sides = derivation.pegSides + start = Vector3(hollowPegSocket.center.real, hollowPegSocket.center.imag, derivation.height) + tinyHeight = 0.0001 * pegHeight + topRadians = 0.25 * math.pi + boltTop = derivation.height + if hollowPegSocket.shouldAddPeg: + boltTop = peg.getTopAddBiconicOutput( + pegRadians, pegHeight, positives, pegRadiusComplex, sides, start, pegTip, topRadians) + sides = derivation.socketSides + socketHeight = 1.05 * derivation.pegHeight + socketRadiusComplex = complex(derivation.socketRadiusArealized, derivation.socketRadiusArealized) + socketTip = 0.5 * derivation.overhangSpan + start = Vector3(hollowPegSocket.center.real, hollowPegSocket.center.imag, -tinyHeight) + topRadians = derivation.interiorOverhangRadians + if hollowPegSocket.shouldAddSocket: + peg.getTopAddBiconicOutput(pegRadians, socketHeight, negatives, socketRadiusComplex, sides, start, socketTip, topRadians) + if derivation.boltRadius <= 0.0: + return + if (not hollowPegSocket.shouldAddPeg) and (not hollowPegSocket.shouldAddSocket): + return + boltRadiusComplex = complex(derivation.boltRadius, derivation.boltRadius) + cylinder.addCylinderOutputByEndStart(boltTop + tinyHeight, boltRadiusComplex, negatives, derivation.boltSides, start) + +def addSlab(derivation, positives): + 'Add slab.' + copyShallow = derivation.elementNode.getCopyShallow() + copyShallow.attributes['path'] = [Vector3(), Vector3(0.0, 0.0, derivation.height)] + extrudeDerivation = extrude.ExtrudeDerivation(copyShallow) + beveledRectangle = getBeveledRectangle(derivation.bevel, -derivation.topRight) + outsidePath = euclidean.getVector3Path(beveledRectangle) + extrude.addPositives(extrudeDerivation, [outsidePath], positives) + +def addXGroove(derivation, negatives, y): + 'Add x groove.' + if derivation.topBevel <= 0.0: + return + bottom = derivation.height - derivation.topBevel + top = derivation.height + groove = [complex(y, bottom), complex(y - derivation.topBevel, top), complex(y + derivation.topBevel, top)] + triangle_mesh.addSymmetricXPath(negatives, groove, 1.0001 * derivation.topRight.real) + +def addYGroove(derivation, negatives, x): + 'Add y groove' + if derivation.topBevel <= 0.0: + return + bottom = derivation.height - derivation.topBevel + top = derivation.height + groove = [complex(x, bottom), complex(x - derivation.topBevel, top), complex(x + derivation.topBevel, top)] + triangle_mesh.addSymmetricYPath(negatives, groove, 1.0001 * derivation.topRight.imag) + +def getBeveledRectangle(bevel, bottomLeft): + 'Get the beveled rectangle.' + bottomRight = complex(-bottomLeft.real, bottomLeft.imag) + rectangle = [bottomLeft, bottomRight, -bottomLeft, -bottomRight] + if bevel <= 0.0: + return rectangle + beveledRectangle = [] + for pointIndex, point in enumerate(rectangle): + begin = rectangle[(pointIndex + len(rectangle) - 1) % len(rectangle)] + end = rectangle[(pointIndex + 1) % len(rectangle)] + addAlongWay(point, bevel, begin, beveledRectangle) + addAlongWay(point, bevel, end, beveledRectangle) + return beveledRectangle + +def getGeometryOutput(elementNode): + 'Get vector3 vertexes from attribute dictionary.' + derivation = MechaslabDerivation(elementNode) + negatives = [] + positives = [] + addSlab(derivation, positives) + for hollowPegSocket in derivation.hollowPegSockets: + addHollowPegSocket(derivation, hollowPegSocket, negatives, positives) + if 's' in derivation.topBevelPositions: + addXGroove(derivation, negatives, -derivation.topRight.imag) + if 'n' in derivation.topBevelPositions: + addXGroove(derivation, negatives, derivation.topRight.imag) + if 'w' in derivation.topBevelPositions: + addYGroove(derivation, negatives, -derivation.topRight.real) + if 'e' in derivation.topBevelPositions: + addYGroove(derivation, negatives, derivation.topRight.real) + return extrude.getGeometryOutputByNegativesPositives(elementNode, negatives, positives) + +def getGeometryOutputByArguments(arguments, elementNode): + 'Get vector3 vertexes from attribute dictionary by arguments.' + evaluate.setAttributesByArguments(['length', 'radius'], arguments, elementNode) + return getGeometryOutput(elementNode) + +def getNewDerivation(elementNode): + 'Get new derivation.' + return MechaslabDerivation(elementNode) + +def processElementNode(elementNode): + 'Process the xml element.' + solid.processElementNodeByGeometry(elementNode, getGeometryOutput(elementNode)) + + +class CellExistence: + 'Class to determine if a cell exists.' + def __init__(self, columns, rows, value): + 'Initialize.' + self.existenceSet = None + if value == None: + return + self.existenceSet = set() + for element in value: + if element.__class__ == int: + columnIndex = (element + columns) % columns + for rowIndex in xrange(rows): + keyTuple = (columnIndex, rowIndex) + self.existenceSet.add(keyTuple) + else: + keyTuple = (element[0], element[1]) + self.existenceSet.add(keyTuple) + + def __repr__(self): + 'Get the string representation of this CellExistence.' + return euclidean.getDictionaryString(self.__dict__) + + def getIsInExistence(self, columnIndex, rowIndex): + 'Detremine if the cell at the column and row exists.' + if self.existenceSet == None: + return True + return (columnIndex, rowIndex) in self.existenceSet + + +class HollowPegSocket: + 'Class to hold hollow peg socket variables.' + def __init__(self, center): + 'Initialize.' + self.center = center + self.shouldAddPeg = True + self.shouldAddSocket = True + + def __repr__(self): + 'Get the string representation of this HollowPegSocket.' + return euclidean.getDictionaryString(self.__dict__) + + +class MechaslabDerivation: + 'Class to hold mechaslab variables.' + def __init__(self, elementNode): + 'Set defaults.' + self.bevelOverRadius = evaluate.getEvaluatedFloat(0.2, elementNode, 'bevelOverRadius') + self.boltRadiusOverRadius = evaluate.getEvaluatedFloat(0.0, elementNode, 'boltRadiusOverRadius') + self.columns = evaluate.getEvaluatedInt(2, elementNode, 'columns') + self.elementNode = elementNode + self.heightOverRadius = evaluate.getEvaluatedFloat(2.0, elementNode, 'heightOverRadius') + self.interiorOverhangRadians = setting.getInteriorOverhangRadians(elementNode) + self.overhangSpan = setting.getOverhangSpan(elementNode) + self.pegClearanceOverRadius = evaluate.getEvaluatedFloat(0.0, elementNode, 'pegClearanceOverRadius') + self.pegRadians = math.radians(evaluate.getEvaluatedFloat(2.0, elementNode, 'pegAngle')) + self.pegHeightOverHeight = evaluate.getEvaluatedFloat(0.4, elementNode, 'pegHeightOverHeight') + self.pegRadiusOverRadius = evaluate.getEvaluatedFloat(0.7, elementNode, 'pegRadiusOverRadius') + self.radius = lineation.getFloatByPrefixBeginEnd(elementNode, 'radius', 'width', 5.0) + self.rows = evaluate.getEvaluatedInt(1, elementNode, 'rows') + self.topBevelOverRadius = evaluate.getEvaluatedFloat(0.2, elementNode, 'topBevelOverRadius') + # Set derived values. + self.bevel = evaluate.getEvaluatedFloat(self.bevelOverRadius * self.radius, elementNode, 'bevel') + self.boltRadius = evaluate.getEvaluatedFloat(self.boltRadiusOverRadius * self.radius, elementNode, 'boltRadius') + self.boltSides = evaluate.getSidesMinimumThreeBasedOnPrecision(elementNode, self.boltRadius) + self.bottomLeftCenter = complex(-float(self.columns - 1), -float(self.rows - 1)) * self.radius + self.height = evaluate.getEvaluatedFloat(self.heightOverRadius * self.radius, elementNode, 'height') + self.hollowPegSockets = [] + centerY = self.bottomLeftCenter.imag + diameter = self.radius + self.radius + self.pegExistence = CellExistence(self.columns, self.rows, evaluate.getEvaluatedValue(None, elementNode, 'pegs')) + self.socketExistence = CellExistence(self.columns, self.rows, evaluate.getEvaluatedValue(None, elementNode, 'sockets')) + for rowIndex in xrange(self.rows): + centerX = self.bottomLeftCenter.real + for columnIndex in xrange(self.columns): + hollowPegSocket = HollowPegSocket(complex(centerX, centerY)) + hollowPegSocket.shouldAddPeg = self.pegExistence.getIsInExistence(columnIndex, rowIndex) + hollowPegSocket.shouldAddSocket = self.socketExistence.getIsInExistence(columnIndex, rowIndex) + self.hollowPegSockets.append(hollowPegSocket) + centerX += diameter + centerY += diameter + self.pegClearance = evaluate.getEvaluatedFloat(self.pegClearanceOverRadius * self.radius, elementNode, 'pegClearance') + halfPegClearance = 0.5 * self.pegClearance + self.pegHeight = evaluate.getEvaluatedFloat(self.pegHeightOverHeight * self.height, elementNode, 'pegHeight') + self.pegRadius = evaluate.getEvaluatedFloat(self.pegRadiusOverRadius * self.radius, elementNode, 'pegRadius') + sides = 24 * max(1, math.floor(evaluate.getSidesBasedOnPrecision(elementNode, self.pegRadius) / 24)) + self.socketRadius = self.pegRadius + halfPegClearance + self.pegSides = evaluate.getEvaluatedInt(sides, elementNode, 'pegSides') + self.pegRadius -= halfPegClearance + self.pegRadiusArealized = evaluate.getRadiusArealizedBasedOnAreaRadius(elementNode, self.pegRadius, self.pegSides) + self.socketSides = evaluate.getEvaluatedInt(sides, elementNode, 'socketSides') + self.socketRadiusArealized = evaluate.getRadiusArealizedBasedOnAreaRadius(elementNode, self.socketRadius, self.socketSides) + self.topBevel = evaluate.getEvaluatedFloat(self.topBevelOverRadius * self.radius, elementNode, 'topBevel') + self.topBevelPositions = evaluate.getEvaluatedString('nwse', elementNode, 'topBevelPositions').lower() + self.topRight = complex(float(self.columns), float(self.rows)) * self.radius + + def __repr__(self): + 'Get the string representation of this MechaslabDerivation.' + return euclidean.getDictionaryString(self.__dict__) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/creation/peg.py b/SkeinPyPy/fabmetheus_utilities/geometry/creation/peg.py new file mode 100644 index 0000000..01f3126 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/creation/peg.py @@ -0,0 +1,103 @@ +""" +Peg. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import extrude +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.creation import solid +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.solids import cylinder +from fabmetheus_utilities.vector3 import Vector3 +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + + +def addPegOutput(bevel, endZ, outputs, radiusArealized, sides, start, topOverBottom): + 'Add beveled cylinder to outputs given bevel, endZ, radiusArealized and start.' + height = abs(start.z - endZ) + bevelStartRatio = max(1.0 - bevel / height, 0.5) + oneMinusBevelStartRatio = 1.0 - bevelStartRatio + trunkEndZ = bevelStartRatio * endZ + oneMinusBevelStartRatio * start.z + trunkTopOverBottom = bevelStartRatio * topOverBottom + oneMinusBevelStartRatio + cylinder.addCylinderOutputByEndStart(trunkEndZ, radiusArealized, outputs, sides, start, trunkTopOverBottom) + capRadius = radiusArealized * trunkTopOverBottom + capStart = bevelStartRatio * Vector3(start.x, start.y, endZ) + oneMinusBevelStartRatio * start + radiusMaximum = max(radiusArealized.real, radiusArealized.imag) + endRadiusMaximum = radiusMaximum * topOverBottom - bevel + trunkRadiusMaximum = radiusMaximum * trunkTopOverBottom + capTopOverBottom = endRadiusMaximum / trunkRadiusMaximum + cylinder.addCylinderOutputByEndStart(endZ, capRadius, outputs, sides, capStart, capTopOverBottom) + +def getGeometryOutput(derivation, elementNode): + 'Get vector3 vertexes from attribute dictionary.' + if derivation == None: + derivation = PegDerivation(elementNode) + positives = [] + radiusArealized = complex(derivation.radiusArealized, derivation.radiusArealized) + addPegOutput(derivation.bevel, derivation.endZ, positives, radiusArealized, derivation.sides, derivation.start, derivation.topOverBottom) + return extrude.getGeometryOutputByNegativesPositives(elementNode, [], positives) + +def getGeometryOutputByArguments(arguments, elementNode): + 'Get vector3 vertexes from attribute dictionary by arguments.' + evaluate.setAttributesByArguments(['radius', 'endZ', 'start'], arguments, elementNode) + return getGeometryOutput(None, elementNode) + +def getNewDerivation(elementNode): + 'Get new derivation.' + return PegDerivation(elementNode) + +def getTopAddBiconicOutput(bottomRadians, height, outputs, radius, sides, start, tipRadius, topRadians): + 'Get top and add biconic cylinder to outputs.' + radiusMaximum = max(radius.real, radius.imag) + topRadiusMaximum = radiusMaximum - height * math.tan(bottomRadians) + trunkEndZ = start.z + height + trunkTopOverBottom = topRadiusMaximum / radiusMaximum + topRadiusComplex = trunkTopOverBottom * radius + cylinder.addCylinderOutputByEndStart(trunkEndZ, radius, outputs, sides, start, trunkTopOverBottom) + tipOverTop = tipRadius / topRadiusMaximum + if tipOverTop >= 1.0: + return trunkEndZ + capStart = Vector3(start.x, start.y, trunkEndZ) + capEndZ = trunkEndZ + (topRadiusMaximum - tipRadius) / math.tan(topRadians) + cylinder.addCylinderOutputByEndStart(capEndZ, topRadiusComplex, outputs, sides, capStart, tipOverTop) + return capEndZ + +def processElementNode(elementNode): + 'Process the xml element.' + solid.processElementNodeByGeometry(elementNode, getGeometryOutput(None, elementNode)) + +def setTopOverBottomByRadius(derivation, endZ, radius, startZ): + 'Set the derivation topOverBottom by the angle of the elementNode, the endZ, float radius and startZ.' + angleDegrees = evaluate.getEvaluatedFloat(None, derivation.elementNode, 'angle') + if angleDegrees != None: + derivation.topOverBottom = cylinder.getTopOverBottom(math.radians(angleDegrees), endZ, complex(radius, radius), startZ) + + +class PegDerivation: + 'Class to hold peg variables.' + def __init__(self, elementNode): + 'Set defaults.' + self.bevelOverRadius = evaluate.getEvaluatedFloat(0.25, elementNode, 'bevelOverRadius') + self.clearanceOverRadius = evaluate.getEvaluatedFloat(0.0, elementNode, 'clearanceOverRadius') + self.elementNode = elementNode + self.endZ = evaluate.getEvaluatedFloat(10.0, elementNode, 'endZ') + self.start = evaluate.getVector3ByPrefix(Vector3(), elementNode, 'start') + self.radius = lineation.getFloatByPrefixBeginEnd(elementNode, 'radius', 'diameter', 2.0) + self.sides = evaluate.getSidesMinimumThreeBasedOnPrecision(elementNode, max(self.radius.real, self.radius.imag)) + self.radiusArealized = evaluate.getRadiusArealizedBasedOnAreaRadius(elementNode, self.radius, self.sides) + self.topOverBottom = evaluate.getEvaluatedFloat(0.8, elementNode, 'topOverBottom') + setTopOverBottomByRadius(self, self.endZ, self.radiusArealized, self.start.z) + # Set derived variables. + self.bevel = evaluate.getEvaluatedFloat(self.bevelOverRadius * self.radiusArealized, elementNode, 'bevel') + self.clearance = evaluate.getEvaluatedFloat(self.clearanceOverRadius * self.radiusArealized, elementNode, 'clearance') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/creation/polygon.py b/SkeinPyPy/fabmetheus_utilities/geometry/creation/polygon.py new file mode 100644 index 0000000..26e4dc9 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/creation/polygon.py @@ -0,0 +1,69 @@ +""" +Polygon path. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.geometry_tools import path +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getGeometryOutput(derivation, elementNode): + "Get vector3 vertexes from attribute dictionary." + if derivation == None: + derivation = PolygonDerivation(elementNode) + loop = [] + spiral = lineation.Spiral(derivation.spiral, 0.5 * derivation.sideAngle / math.pi) + for side in xrange(derivation.start, derivation.start + derivation.extent + 1): + angle = float(side) * derivation.sideAngle + unitPolar = euclidean.getWiddershinsUnitPolar(angle) + vertex = spiral.getSpiralPoint(unitPolar, Vector3(unitPolar.real * derivation.radius.real, unitPolar.imag * derivation.radius.imag)) + loop.append(vertex) + loop = euclidean.getLoopWithoutCloseEnds(0.000001 * max(derivation.radius.real, derivation.radius.imag), loop) + lineation.setClosedAttribute(elementNode, derivation.revolutions) + return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop(loop, derivation.sideAngle)) + +def getGeometryOutputByArguments(arguments, elementNode): + "Get vector3 vertexes from attribute dictionary by arguments." + evaluate.setAttributesByArguments(['sides', 'radius'], arguments, elementNode) + return getGeometryOutput(None, elementNode) + +def getNewDerivation(elementNode): + 'Get new derivation.' + return PolygonDerivation(elementNode) + +def processElementNode(elementNode): + "Process the xml element." + path.convertElementNode(elementNode, getGeometryOutput(None, elementNode)) + + +class PolygonDerivation: + "Class to hold polygon variables." + def __init__(self, elementNode): + 'Set defaults.' + self.sides = evaluate.getEvaluatedFloat(4.0, elementNode, 'sides') + self.sideAngle = 2.0 * math.pi / self.sides + cosSide = math.cos(0.5 * self.sideAngle) + self.radius = lineation.getComplexByMultiplierPrefixes(elementNode, cosSide, ['apothem', 'inradius'], complex(1.0, 1.0)) + self.radius = lineation.getComplexByPrefixes(elementNode, ['demisize', 'radius'], self.radius) + self.radius = lineation.getComplexByMultiplierPrefixes(elementNode, 2.0, ['diameter', 'size'], self.radius) + self.sidesCeiling = int(math.ceil(abs(self.sides))) + self.start = evaluate.getEvaluatedInt(0, elementNode, 'start') + end = evaluate.getEvaluatedInt(self.sidesCeiling, elementNode, 'end') + self.revolutions = evaluate.getEvaluatedInt(1, elementNode, 'revolutions') + self.extent = evaluate.getEvaluatedInt(end - self.start, elementNode, 'extent') + self.extent += self.sidesCeiling * (self.revolutions - 1) + self.spiral = evaluate.getVector3ByPrefix(None, elementNode, 'spiral') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/creation/shaft.py b/SkeinPyPy/fabmetheus_utilities/geometry/creation/shaft.py new file mode 100644 index 0000000..457c2de --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/creation/shaft.py @@ -0,0 +1,82 @@ +""" +Shaft path. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.geometry_tools import path +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities import euclidean +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getGeometryOutput(derivation, elementNode): + "Get vector3 vertexes from attribute dictionary." + if derivation == None: + derivation = ShaftDerivation(elementNode) + shaftPath = getShaftPath(derivation.depthBottom, derivation.depthTop, derivation.radius, derivation.sides) + return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop(shaftPath)) + +def getGeometryOutputByArguments(arguments, elementNode): + "Get vector3 vertexes from attribute dictionary by arguments." + evaluate.setAttributesByArguments(['radius', 'sides'], arguments, elementNode) + return getGeometryOutput(None, elementNode) + +def getNewDerivation(elementNode): + 'Get new derivation.' + return ShaftDerivation(elementNode) + +def getShaftPath(depthBottom, depthTop, radius, sides): + 'Get shaft with the option of a flat on the top and/or bottom.' + if radius <= 0.0: + return [] + sideAngle = 2.0 * math.pi / float(abs(sides)) + startAngle = 0.5 * sideAngle + endAngle = math.pi - 0.1 * sideAngle + shaftProfile = [] + while startAngle < endAngle: + unitPolar = euclidean.getWiddershinsUnitPolar(startAngle) + shaftProfile.append(unitPolar * radius) + startAngle += sideAngle + if abs(sides) % 2 == 1: + shaftProfile.append(complex(-radius, 0.0)) + horizontalBegin = radius - depthTop + horizontalEnd = depthBottom - radius + shaftProfile = euclidean.getHorizontallyBoundedPath(horizontalBegin, horizontalEnd, shaftProfile) + for shaftPointIndex, shaftPoint in enumerate(shaftProfile): + shaftProfile[shaftPointIndex] = complex(shaftPoint.imag, shaftPoint.real) + shaftPath = euclidean.getVector3Path(euclidean.getMirrorPath(shaftProfile)) + if sides > 0: + shaftPath.reverse() + return shaftPath + +def processElementNode(elementNode): + "Process the xml element." + path.convertElementNode(elementNode, getGeometryOutput(None, elementNode)) + + +class ShaftDerivation: + "Class to hold shaft variables." + def __init__(self, elementNode): + 'Set defaults.' + self.depthBottomOverRadius = evaluate.getEvaluatedFloat(0.0, elementNode, 'depthBottomOverRadius') + self.depthTopOverRadius = evaluate.getEvaluatedFloat(0.0, elementNode, 'depthOverRadius') + self.depthTopOverRadius = evaluate.getEvaluatedFloat( + self.depthTopOverRadius, elementNode, 'depthTopOverRadius') + self.radius = evaluate.getEvaluatedFloat(1.0, elementNode, 'radius') + self.sides = evaluate.getEvaluatedInt(4, elementNode, 'sides') + self.depthBottom = self.radius * self.depthBottomOverRadius + self.depthBottom = evaluate.getEvaluatedFloat(self.depthBottom, elementNode, 'depthBottom') + self.depthTop = self.radius * self.depthTopOverRadius + self.depthTop = evaluate.getEvaluatedFloat(self.depthTop, elementNode, 'depth') + self.depthTop = evaluate.getEvaluatedFloat(self.depthTop, elementNode, 'depthTop') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/creation/solid.py b/SkeinPyPy/fabmetheus_utilities/geometry/creation/solid.py new file mode 100644 index 0000000..4defa04 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/creation/solid.py @@ -0,0 +1,178 @@ +""" +Solid has functions for 3D shapes. + +Solid has some of the same functions as lineation, however you can not define geometry by dictionary string in the target because there is no getGeometryOutputByArguments function. You would have to define a shape by making the shape element. Also, you can not define geometry by 'get, because the target only gets element. Instead you would have the shape element, and set the target in solid to that element. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.geometry_tools import path +from fabmetheus_utilities.geometry.geometry_utilities import boolean_geometry +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getGeometryOutputByFunction(elementNode, geometryFunction): + 'Get geometry output by manipulationFunction.' + if elementNode.xmlObject == None: + print('Warning, there is no object in getGeometryOutputByFunction in solid for:') + print(elementNode) + return None + geometryOutput = elementNode.xmlObject.getGeometryOutput() + if geometryOutput == None: + print('Warning, there is no geometryOutput in getGeometryOutputByFunction in solid for:') + print(elementNode) + return None + return geometryFunction(elementNode, geometryOutput, '') + +def getGeometryOutputByManipulation(elementNode, geometryOutput): + 'Get geometryOutput manipulated by the plugins in the manipulation shapes & solids folders.' + xmlProcessor = elementNode.getXMLProcessor() + matchingPlugins = getSolidMatchingPlugins(elementNode) + matchingPlugins.sort(evaluate.compareExecutionOrderAscending) + for matchingPlugin in matchingPlugins: + prefix = matchingPlugin.__name__.replace('_', '') + '.' + geometryOutput = matchingPlugin.getManipulatedGeometryOutput(elementNode, geometryOutput, prefix) + return geometryOutput + +def getLoopLayersSetCopy(elementNode, geometryOutput, importRadius, radius): + 'Get the loop layers and set the copyShallow.' + halfLayerHeight = 0.5 * radius + copyShallow = elementNode.getCopyShallow() + processElementNodeByGeometry(copyShallow, geometryOutput) + targetMatrix = matrix.getBranchMatrixSetElementNode(elementNode) + matrix.setElementNodeDictionaryMatrix(copyShallow, targetMatrix) + transformedVertexes = copyShallow.xmlObject.getTransformedVertexes() + minimumZ = boolean_geometry.getMinimumZ(copyShallow.xmlObject) + if minimumZ == None: + copyShallow.parentNode.xmlObject.archivableObjects.remove(copyShallow.xmlObject) + return [] + maximumZ = euclidean.getTopPath(transformedVertexes) + copyShallow.attributes['visible'] = True + copyShallowObjects = [copyShallow.xmlObject] + bottomLoopLayer = euclidean.LoopLayer(minimumZ) + z = minimumZ + 0.1 * radius + zoneArrangement = triangle_mesh.ZoneArrangement(radius, transformedVertexes) + bottomLoopLayer.loops = boolean_geometry.getEmptyZLoops(copyShallowObjects, importRadius, False, z, zoneArrangement) + loopLayers = [bottomLoopLayer] + z = minimumZ + halfLayerHeight + loopLayers += boolean_geometry.getLoopLayers(copyShallowObjects, importRadius, halfLayerHeight, maximumZ, False, z, zoneArrangement) + copyShallow.parentNode.xmlObject.archivableObjects.remove(copyShallow.xmlObject) + return loopLayers + +def getLoopOrEmpty(loopIndex, loopLayers): + 'Get the loop, or if the loopIndex is out of range, get an empty list.' + if loopIndex < 0 or loopIndex >= len(loopLayers): + return [] + return loopLayers[loopIndex].loops[0] + +def getNewDerivation(elementNode): + 'Get new derivation.' + return SolidDerivation(elementNode) + +def getSolidMatchingPlugins(elementNode): + 'Get solid plugins in the manipulation matrix, shapes & solids folders.' + xmlProcessor = elementNode.getXMLProcessor() + matchingPlugins = evaluate.getMatchingPlugins(elementNode, xmlProcessor.manipulationMatrixDictionary) + return matchingPlugins + evaluate.getMatchingPlugins(elementNode, xmlProcessor.manipulationShapeDictionary) + +def processArchiveRemoveSolid(elementNode, geometryOutput): + 'Process the target by the manipulationFunction.' + solidMatchingPlugins = getSolidMatchingPlugins(elementNode) + if len(solidMatchingPlugins) == 0: + elementNode.parentNode.xmlObject.archivableObjects.append(elementNode.xmlObject) + matrix.getBranchMatrixSetElementNode(elementNode) + return + processElementNodeByGeometry(elementNode, getGeometryOutputByManipulation(elementNode, geometryOutput)) + +def processElementNode(elementNode): + 'Process the xml element.' + processElementNodeByDerivation(None, elementNode) + +def processElementNodeByDerivation(derivation, elementNode): + 'Process the xml element by derivation.' + if derivation == None: + derivation = SolidDerivation(elementNode) + elementAttributesCopy = elementNode.attributes.copy() + for target in derivation.targets: + targetAttributesCopy = target.attributes.copy() + target.attributes = elementAttributesCopy + processTarget(target) + target.attributes = targetAttributesCopy + +def processElementNodeByFunction(elementNode, manipulationFunction): + 'Process the xml element.' + if 'target' not in elementNode.attributes: + print('Warning, there was no target in processElementNodeByFunction in solid for:') + print(elementNode) + return + target = evaluate.getEvaluatedLinkValue(elementNode, str(elementNode.attributes['target']).strip()) + if target.__class__.__name__ == 'ElementNode': + manipulationFunction(elementNode, target) + return + path.convertElementNode(elementNode, target) + manipulationFunction(elementNode, elementNode) + +def processElementNodeByFunctionPair(elementNode, geometryFunction, pathFunction): + 'Process the xml element by the appropriate manipulationFunction.' + elementAttributesCopy = elementNode.attributes.copy() + targets = evaluate.getElementNodesByKey(elementNode, 'target') + for target in targets: + targetAttributesCopy = target.attributes.copy() + target.attributes = elementAttributesCopy + processTargetByFunctionPair(geometryFunction, pathFunction, target) + target.attributes = targetAttributesCopy + +def processElementNodeByGeometry(elementNode, geometryOutput): + 'Process the xml element by geometryOutput.' + if geometryOutput != None: + elementNode.getXMLProcessor().convertElementNode(elementNode, geometryOutput) + +def processTarget(target): + 'Process the target.' + if target.xmlObject == None: + print('Warning, there is no object in processElementNode in solid for:') + print(target) + return + geometryOutput = target.xmlObject.getGeometryOutput() + if geometryOutput == None: + print('Warning, there is no geometryOutput in processElementNode in solid for:') + print(target.xmlObject) + return + geometryOutput = getGeometryOutputByManipulation(target, geometryOutput) + lineation.removeChildNodesFromElementObject(target) + target.getXMLProcessor().convertElementNode(target, geometryOutput) + +def processTargetByFunctionPair(geometryFunction, pathFunction, target): + 'Process the target by the manipulationFunction.' + if target.xmlObject == None: + print('Warning, there is no object in processTargetByFunctions in solid for:') + print(target) + return + if len(target.xmlObject.getPaths()) > 0: + lineation.processTargetByFunction(pathFunction, target) + return + geometryOutput = getGeometryOutputByFunction(target, geometryFunction) + lineation.removeChildNodesFromElementObject(target) + target.getXMLProcessor().convertElementNode(target, geometryOutput) + + +class SolidDerivation: + 'Class to hold solid variables.' + def __init__(self, elementNode): + 'Set defaults.' + self.targets = evaluate.getElementNodesByKey(elementNode, 'target') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/creation/sponge_slice.py b/SkeinPyPy/fabmetheus_utilities/geometry/creation/sponge_slice.py new file mode 100644 index 0000000..21f6337 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/creation/sponge_slice.py @@ -0,0 +1,157 @@ +""" +Sponge slice. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.geometry_tools import path +from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +import math +import random +import time + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getGeometryOutput(derivation, elementNode): + "Get vector3 vertexes from attribute dictionary." + if derivation == None: + derivation = SpongeSliceDerivation(elementNode) + awayPoints = [] + vector3Path = euclidean.getVector3Path(euclidean.getSquareLoopWiddershins(-derivation.inradius, derivation.inradius)) + geometryOutput = lineation.SideLoop(vector3Path).getManipulationPluginLoops(elementNode) + minimumDistanceFromOther = derivation.wallThickness + derivation.minimumRadius + derivation.minimumRadius + if derivation.inradiusMinusRadiusThickness.real <= 0.0 or derivation.inradiusMinusRadiusThickness.imag <= 0.0: + return geometryOutput + for point in derivation.path: + if abs(point.x) <= derivation.inradiusMinusRadiusThickness.real and abs(point.y) <= derivation.inradiusMinusRadiusThickness.imag: + awayPoints.append(point) + awayCircles = [] + for point in awayPoints: + if getIsPointAway(minimumDistanceFromOther, point, awayCircles): + awayCircles.append(SpongeCircle(point, derivation.minimumRadius)) + averagePotentialBubbleArea = derivation.potentialBubbleArea / float(len(awayCircles)) + averageBubbleRadius = math.sqrt(averagePotentialBubbleArea / math.pi) - 0.5 * derivation.wallThickness + sides = -4 * (max(evaluate.getSidesBasedOnPrecision(elementNode, averageBubbleRadius), 4) / 4) + sideAngle = math.pi / sides + cosSide = math.cos(sideAngle) + overlapArealRatio = (1 - cosSide) / cosSide + for circleIndex, circle in enumerate(awayCircles): + otherCircles = awayCircles[: circleIndex] + awayCircles[circleIndex + 1 :] + circle.radius = circle.getRadius(circle.center, derivation, otherCircles, overlapArealRatio) + if derivation.searchAttempts > 0: + for circleIndex, circle in enumerate(awayCircles): + otherCircles = awayCircles[: circleIndex] + awayCircles[circleIndex + 1 :] + circle.moveCircle(derivation, otherCircles, overlapArealRatio) + for circle in awayCircles: + vector3Path = euclidean.getVector3Path(euclidean.getComplexPolygon(circle.center.dropAxis(), circle.radius, sides, sideAngle)) + geometryOutput += lineation.SideLoop(vector3Path).getManipulationPluginLoops(elementNode) + return geometryOutput + +def getGeometryOutputByArguments(arguments, elementNode): + "Get vector3 vertexes from attribute dictionary by arguments." + return getGeometryOutput(None, elementNode) + +def getIsPointAway(minimumDistance, point, spongeCircles): + 'Determine if the point is at least the minimumDistance away from other points.' + for otherSpongeCircle in spongeCircles: + if abs(otherSpongeCircle.center - point) < minimumDistance: + return False + return True + +def getNewDerivation(elementNode): + 'Get new derivation.' + return SpongeSliceDerivation(elementNode) + +def processElementNode(elementNode): + "Process the xml element." + path.convertElementNode(elementNode, getGeometryOutput(None, elementNode)) + + +class SpongeCircle: + "Class to hold sponge circle." + def __init__(self, center, radius=0.0): + 'Initialize.' + self.center = center + self.radius = radius + + def getRadius(self, center, derivation, otherCircles, overlapArealRatio): + 'Get sponge bubble radius.' + radius = 987654321.0 + for otherSpongeCircle in otherCircles: + distance = abs(otherSpongeCircle.center.dropAxis() - center.dropAxis()) + radius = min(distance - derivation.wallThickness - otherSpongeCircle.radius, radius) + overlapAreal = overlapArealRatio * radius + radius = min(derivation.inradiusMinusThickness.real + overlapAreal - abs(center.x), radius) + return min(derivation.inradiusMinusThickness.imag + overlapAreal - abs(center.y), radius) + + def moveCircle(self, derivation, otherCircles, overlapArealRatio): + 'Move circle into an open spot.' + angle = (abs(self.center) + self.radius) % euclidean.globalTau + movedCenter = self.center + searchRadius = derivation.searchRadiusOverRadius * self.radius + distanceIncrement = searchRadius / float(derivation.searchAttempts) + distance = 0.0 + greatestRadius = self.radius + searchCircles = [] + searchCircleDistance = searchRadius + searchRadius + self.radius + derivation.wallThickness + for otherCircle in otherCircles: + if abs(self.center - otherCircle.center) <= searchCircleDistance + otherCircle.radius: + searchCircles.append(otherCircle) + for attemptIndex in xrange(derivation.searchAttempts): + angle += euclidean.globalGoldenAngle + distance += distanceIncrement + offset = distance * euclidean.getWiddershinsUnitPolar(angle) + attemptCenter = self.center + Vector3(offset.real, offset.imag) + radius = self.getRadius(attemptCenter, derivation, searchCircles, overlapArealRatio) + if radius > greatestRadius: + greatestRadius = radius + movedCenter = attemptCenter + self.center = movedCenter + self.radius = greatestRadius + + +class SpongeSliceDerivation: + "Class to hold sponge slice variables." + def __init__(self, elementNode): + 'Initialize.' + elementNode.attributes['closed'] = 'true' + self.density = evaluate.getEvaluatedFloat(1.0, elementNode, 'density') + self.minimumRadiusOverThickness = evaluate.getEvaluatedFloat(1.0, elementNode, 'minimumRadiusOverThickness') + self.mobile = evaluate.getEvaluatedBoolean(False, elementNode, 'mobile') + self.inradius = lineation.getInradius(complex(10.0, 10.0), elementNode) + self.path = None + if 'path' in elementNode.attributes: + self.path = evaluate.getPathByKey([], elementNode, 'path') + self.searchAttempts = evaluate.getEvaluatedInt(0, elementNode, 'searchAttempts') + self.searchRadiusOverRadius = evaluate.getEvaluatedFloat(1.0, elementNode, 'searchRadiusOverRadius') + self.seed = evaluate.getEvaluatedInt(None, elementNode, 'seed') + self.wallThickness = evaluate.getEvaluatedFloat(2.0 * setting.getEdgeWidth(elementNode), elementNode, 'wallThickness') + # Set derived variables. + self.halfWallThickness = 0.5 * self.wallThickness + self.inradiusMinusThickness = self.inradius - complex(self.wallThickness, self.wallThickness) + self.minimumRadius = evaluate.getEvaluatedFloat(self.minimumRadiusOverThickness * self.wallThickness, elementNode, 'minimumRadius') + self.inradiusMinusRadiusThickness = self.inradiusMinusThickness - complex(self.minimumRadius, self.minimumRadius) + self.potentialBubbleArea = 4.0 * self.inradiusMinusThickness.real * self.inradiusMinusThickness.imag + if self.path == None: + radiusPlusHalfThickness = self.minimumRadius + self.halfWallThickness + numberOfPoints = int(math.ceil(self.density * self.potentialBubbleArea / math.pi / radiusPlusHalfThickness / radiusPlusHalfThickness)) + self.path = [] + if self.seed == None: + self.seed = time.time() + print('Sponge slice seed used was: %s' % self.seed) + random.seed(self.seed) + for pointIndex in xrange(numberOfPoints): + point = euclidean.getRandomComplex(-self.inradiusMinusRadiusThickness, self.inradiusMinusRadiusThickness) + self.path.append(Vector3(point.real, point.imag)) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/creation/square.py b/SkeinPyPy/fabmetheus_utilities/geometry/creation/square.py new file mode 100644 index 0000000..68b31bc --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/creation/square.py @@ -0,0 +1,80 @@ +""" +Square path. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.geometry_tools import path +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getGeometryOutput(derivation, elementNode): + "Get vector3 vertexes from attribute dictionary." + if derivation == None: + derivation = SquareDerivation(elementNode) + topRight = complex(derivation.topDemiwidth, derivation.demiheight) + topLeft = complex(-derivation.topDemiwidth, derivation.demiheight) + bottomLeft = complex(-derivation.bottomDemiwidth, -derivation.demiheight) + bottomRight = complex(derivation.bottomDemiwidth, -derivation.demiheight) + if derivation.interiorAngle != 90.0: + interiorPlaneAngle = euclidean.getWiddershinsUnitPolar(math.radians(derivation.interiorAngle - 90.0)) + topRight = (topRight - bottomRight) * interiorPlaneAngle + bottomRight + topLeft = (topLeft - bottomLeft) * interiorPlaneAngle + bottomLeft + lineation.setClosedAttribute(elementNode, derivation.revolutions) + complexLoop = [topRight, topLeft, bottomLeft, bottomRight] + originalLoop = complexLoop[:] + for revolution in xrange(1, derivation.revolutions): + complexLoop += originalLoop + spiral = lineation.Spiral(derivation.spiral, 0.25) + loop = [] + loopCentroid = euclidean.getLoopCentroid(originalLoop) + for point in complexLoop: + unitPolar = euclidean.getNormalized(point - loopCentroid) + loop.append(spiral.getSpiralPoint(unitPolar, Vector3(point.real, point.imag))) + return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop(loop, 0.5 * math.pi)) + +def getGeometryOutputByArguments(arguments, elementNode): + "Get vector3 vertexes from attribute dictionary by arguments." + if len(arguments) < 1: + return getGeometryOutput(None, elementNode) + inradius = 0.5 * euclidean.getFloatFromValue(arguments[0]) + elementNode.attributes['inradius.x'] = str(inradius) + if len(arguments) > 1: + inradius = 0.5 * euclidean.getFloatFromValue(arguments[1]) + elementNode.attributes['inradius.y'] = str(inradius) + return getGeometryOutput(None, elementNode) + +def getNewDerivation(elementNode): + 'Get new derivation.' + return SquareDerivation(elementNode) + +def processElementNode(elementNode): + "Process the xml element." + path.convertElementNode(elementNode, getGeometryOutput(None, elementNode)) + + +class SquareDerivation: + "Class to hold square variables." + def __init__(self, elementNode): + 'Set defaults.' + self.inradius = lineation.getInradius(complex(1.0, 1.0), elementNode) + self.demiwidth = lineation.getFloatByPrefixBeginEnd(elementNode, 'demiwidth', 'width', self.inradius.real) + self.demiheight = lineation.getFloatByPrefixBeginEnd(elementNode, 'demiheight', 'height', self.inradius.imag) + self.bottomDemiwidth = lineation.getFloatByPrefixBeginEnd(elementNode, 'bottomdemiwidth', 'bottomwidth', self.demiwidth) + self.topDemiwidth = lineation.getFloatByPrefixBeginEnd(elementNode, 'topdemiwidth', 'topwidth', self.demiwidth) + self.interiorAngle = evaluate.getEvaluatedFloat(90.0, elementNode, 'interiorangle') + self.revolutions = evaluate.getEvaluatedInt(1, elementNode, 'revolutions') + self.spiral = evaluate.getVector3ByPrefix(None, elementNode, 'spiral') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/creation/teardrop.py b/SkeinPyPy/fabmetheus_utilities/geometry/creation/teardrop.py new file mode 100644 index 0000000..8761619 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/creation/teardrop.py @@ -0,0 +1,116 @@ +""" +Teardrop path. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import extrude +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.geometry_tools import path +from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def addNegativesByRadius(elementNode, end, negatives, radius, start): + "Add teardrop drill hole to negatives." + if radius <= 0.0: + return + copyShallow = elementNode.getCopyShallow() + extrude.setElementNodeToEndStart(copyShallow, end, start) + extrudeDerivation = extrude.ExtrudeDerivation(copyShallow) + extrude.addNegatives(extrudeDerivation, negatives, [getTeardropPathByEndStart(elementNode, end, radius, start)]) + +def getGeometryOutput(derivation, elementNode): + "Get vector3 vertexes from attribute dictionary." + if derivation == None: + derivation = TeardropDerivation(elementNode) + teardropPath = getTeardropPath( + derivation.inclination, derivation.overhangRadians, derivation.overhangSpan, derivation.radiusArealized, derivation.sides) + return lineation.getGeometryOutputByLoop(elementNode, lineation.SideLoop(teardropPath)) + +def getGeometryOutputByArguments(arguments, elementNode): + "Get vector3 vertexes from attribute dictionary by arguments." + evaluate.setAttributesByArguments(['radius', 'inclination'], arguments, elementNode) + return getGeometryOutput(None, elementNode) + +def getInclination(end, start): + "Get inclination." + if end == None or start == None: + return 0.0 + endMinusStart = end - start + return math.atan2(endMinusStart.z, abs(endMinusStart.dropAxis())) + +def getNewDerivation(elementNode): + 'Get new derivation.' + return TeardropDerivation(elementNode) + +def getTeardropPath(inclination, overhangRadians, overhangSpan, radiusArealized, sides): + "Get vector3 teardrop path." + sideAngle = 2.0 * math.pi / float(sides) + overhangPlaneAngle = euclidean.getWiddershinsUnitPolar(overhangRadians) + overhangRadians = math.atan2(overhangPlaneAngle.imag, overhangPlaneAngle.real * math.cos(inclination)) + tanOverhangAngle = math.tan(overhangRadians) + beginAngle = overhangRadians + beginMinusEndAngle = math.pi + overhangRadians + overhangRadians + withinSides = int(math.ceil(beginMinusEndAngle / sideAngle)) + withinSideAngle = -beginMinusEndAngle / float(withinSides) + teardropPath = [] + for side in xrange(withinSides + 1): + unitPolar = euclidean.getWiddershinsUnitPolar(beginAngle) + teardropPath.append(unitPolar * radiusArealized) + beginAngle += withinSideAngle + firstPoint = teardropPath[0] + if overhangSpan <= 0.0: + teardropPath.append(complex(0.0, firstPoint.imag + firstPoint.real / tanOverhangAngle)) + else: + deltaX = (radiusArealized - firstPoint.imag) * tanOverhangAngle + overhangPoint = complex(firstPoint.real - deltaX, radiusArealized) + remainingDeltaX = max(0.0, overhangPoint.real - 0.5 * overhangSpan ) + overhangPoint += complex(-remainingDeltaX, remainingDeltaX / tanOverhangAngle) + teardropPath.append(complex(-overhangPoint.real, overhangPoint.imag)) + teardropPath.append(overhangPoint) + return euclidean.getVector3Path(teardropPath) + +def getTeardropPathByEndStart(elementNode, end, radius, start): + "Get vector3 teardrop path by end and start." + inclination = getInclination(end, start) + sides = evaluate.getSidesMinimumThreeBasedOnPrecisionSides(elementNode, radius) + radiusArealized = evaluate.getRadiusArealizedBasedOnAreaRadius(elementNoderadius, sides) + return getTeardropPath(inclination, setting.getOverhangRadians(elementNode), setting.getOverhangSpan(elementNode), radiusArealized, sides) + +def processElementNode(elementNode): + "Process the xml element." + path.convertElementNode(elementNode, getGeometryOutput(None, elementNode)) + + +class TeardropDerivation: + "Class to hold teardrop variables." + def __init__(self, elementNode): + 'Set defaults.' + end = evaluate.getVector3ByPrefix(None, elementNode, 'end') + start = evaluate.getVector3ByPrefix(Vector3(), elementNode, 'start') + inclinationDegree = math.degrees(getInclination(end, start)) + self.elementNode = elementNode + self.inclination = math.radians(evaluate.getEvaluatedFloat(inclinationDegree, elementNode, 'inclination')) + self.overhangRadians = setting.getOverhangRadians(elementNode) + self.overhangSpan = setting.getOverhangSpan(elementNode) + self.radius = lineation.getFloatByPrefixBeginEnd(elementNode, 'radius', 'diameter', 1.0) + size = evaluate.getEvaluatedFloat(None, elementNode, 'size') + if size != None: + self.radius = 0.5 * size + self.sides = evaluate.getEvaluatedFloat(None, elementNode, 'sides') + if self.sides == None: + self.sides = evaluate.getSidesMinimumThreeBasedOnPrecisionSides(elementNode, self.radius) + self.radiusArealized = evaluate.getRadiusArealizedBasedOnAreaRadius(elementNode, self.radius, self.sides) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/creation/text.py b/SkeinPyPy/fabmetheus_utilities/geometry/creation/text.py new file mode 100644 index 0000000..2545d7d --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/creation/text.py @@ -0,0 +1,63 @@ +""" +Text vertexes. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.geometry_tools import path +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import svg_reader + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getGeometryOutput(derivation, elementNode): + "Get vector3 vertexes from attributes." + if derivation == None: + derivation = TextDerivation(elementNode) + if derivation.textString == '': + print('Warning, textString is empty in getGeometryOutput in text for:') + print(elementNode) + return [] + geometryOutput = [] + for textComplexLoop in svg_reader.getTextComplexLoops(derivation.fontFamily, derivation.fontSize, derivation.textString): + textComplexLoop.reverse() + vector3Path = euclidean.getVector3Path(textComplexLoop) + sideLoop = lineation.SideLoop(vector3Path) + sideLoop.rotate(elementNode) + geometryOutput += lineation.getGeometryOutputByManipulation(elementNode, sideLoop) + return geometryOutput + +def getGeometryOutputByArguments(arguments, elementNode): + "Get vector3 vertexes from attribute dictionary by arguments." + evaluate.setAttributesByArguments(['text', 'fontSize', 'fontFamily'], arguments, elementNode) + return getGeometryOutput(None, elementNode) + +def getNewDerivation(elementNode): + 'Get new derivation.' + return TextDerivation(elementNode) + +def processElementNode(elementNode): + "Process the xml element." + path.convertElementNode(elementNode, getGeometryOutput(None, elementNode)) + + +class TextDerivation: + "Class to hold text variables." + def __init__(self, elementNode): + 'Set defaults.' + self.fontFamily = evaluate.getEvaluatedString('Gentium Basic Regular', elementNode, 'font-family') + self.fontFamily = evaluate.getEvaluatedString(self.fontFamily, elementNode, 'fontFamily') + self.fontSize = evaluate.getEvaluatedFloat(12.0, elementNode, 'font-size') + self.fontSize = evaluate.getEvaluatedFloat(self.fontSize, elementNode, 'fontSize') + self.textString = elementNode.getTextContent() + self.textString = evaluate.getEvaluatedString(self.textString, elementNode, 'text') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/__init__.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/__init__.py new file mode 100644 index 0000000..1121e8a --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 3 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/dictionary.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/dictionary.py new file mode 100644 index 0000000..f3714d8 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/dictionary.py @@ -0,0 +1,178 @@ +""" +Boolean geometry dictionary object. + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import xml_simple_writer +import cStringIO + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Nophead \nArt of Illusion ' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getAllPaths(paths, xmlObject): + 'Get all paths.' + for archivableObject in xmlObject.archivableObjects: + paths += archivableObject.getPaths() + return paths + +def getAllTransformedPaths(transformedPaths, xmlObject): + 'Get all transformed paths.' + for archivableObject in xmlObject.archivableObjects: + transformedPaths += archivableObject.getTransformedPaths() + return transformedPaths + +def getAllTransformedVertexes(transformedVertexes, xmlObject): + 'Get all transformed vertexes.' + for archivableObject in xmlObject.archivableObjects: + transformedVertexes += archivableObject.getTransformedVertexes() + return transformedVertexes + +def getAllVertexes(vertexes, xmlObject): + 'Get all vertexes.' + for archivableObject in xmlObject.archivableObjects: + vertexes += archivableObject.getVertexes() + return vertexes + +def processElementNode(elementNode): + 'Process the xml element.' + evaluate.processArchivable( Dictionary, elementNode) + + +class Dictionary: + 'A dictionary object.' + def __init__(self): + 'Add empty lists.' + self.archivableObjects = [] + self.elementNode = None + + def __repr__(self): + 'Get the string representation of this object info.' + output = xml_simple_writer.getBeginGeometryXMLOutput(self.elementNode) + self.addXML( 1, output ) + return xml_simple_writer.getEndGeometryXMLString(output) + + def addXML(self, depth, output): + 'Add xml for this object.' + attributeCopy = {} + if self.elementNode != None: + attributeCopy = evaluate.getEvaluatedDictionaryByCopyKeys(['paths', 'target', 'vertexes'], self.elementNode) + euclidean.removeElementsFromDictionary(attributeCopy, matrix.getKeysM()) + euclidean.removeTrueFromDictionary(attributeCopy, 'visible') + innerOutput = cStringIO.StringIO() + self.addXMLInnerSection(depth + 1, innerOutput) + self.addXMLArchivableObjects(depth + 1, innerOutput) + xml_simple_writer.addBeginEndInnerXMLTag(attributeCopy, depth, innerOutput.getvalue(), self.getXMLLocalName(), output) + + def addXMLArchivableObjects(self, depth, output): + 'Add xml for this object.' + xml_simple_writer.addXMLFromObjects( depth, self.archivableObjects, output ) + + def addXMLInnerSection(self, depth, output): + 'Add xml section for this object.' + pass + + def createShape(self): + 'Create the shape.' + pass + + def getAttributes(self): + 'Get attribute table.' + if self.elementNode == None: + return {} + return self.elementNode.attributes + + def getComplexTransformedPathLists(self): + 'Get complex transformed path lists.' + complexTransformedPathLists = [] + for archivableObject in self.archivableObjects: + complexTransformedPathLists.append(euclidean.getComplexPaths(archivableObject.getTransformedPaths())) + return complexTransformedPathLists + + def getFabricationExtension(self): + 'Get fabrication extension.' + return 'xml' + + def getFabricationText(self, addLayerTemplate): + 'Get fabrication text.' + return self.__repr__() + + def getGeometryOutput(self): + 'Get geometry output dictionary.' + shapeOutput = [] + for visibleObject in evaluate.getVisibleObjects(self.archivableObjects): + geometryOutput = visibleObject.getGeometryOutput() + if geometryOutput != None: + visibleObject.transformGeometryOutput(geometryOutput) + shapeOutput.append(geometryOutput) + if len(shapeOutput) < 1: + return None + return {self.getXMLLocalName() : {'shapes' : shapeOutput}} + + def getMatrix4X4(self): + "Get the matrix4X4." + return None + + def getMatrixChainTetragrid(self): + 'Get the matrix chain tetragrid.' + return self.elementNode.parentNode.xmlObject.getMatrixChainTetragrid() + + def getMinimumZ(self): + 'Get the minimum z.' + return None + + def getPaths(self): + 'Get all paths.' + return getAllPaths([], self) + + def getTransformedPaths(self): + 'Get all transformed paths.' + return getAllTransformedPaths([], self) + + def getTransformedVertexes(self): + 'Get all transformed vertexes.' + return getAllTransformedVertexes([], self) + + def getTriangleMeshes(self): + 'Get all triangleMeshes.' + triangleMeshes = [] + for archivableObject in self.archivableObjects: + triangleMeshes += archivableObject.getTriangleMeshes() + return triangleMeshes + + def getType(self): + 'Get type.' + return self.__class__.__name__ + + def getVertexes(self): + 'Get all vertexes.' + return getAllVertexes([], self) + + def getVisible(self): + 'Get visible.' + return False + + def getXMLLocalName(self): + 'Get xml local name.' + return self.__class__.__name__.lower() + + def setToElementNode(self, elementNode): + 'Set the shape of this carvable object info.' + self.elementNode = elementNode + elementNode.parentNode.xmlObject.archivableObjects.append(self) + + def transformGeometryOutput(self, geometryOutput): + 'Transform the geometry output by the local matrix4x4.' + if self.getMatrix4X4() != None: + matrix.transformVector3sByMatrix(self.getMatrix4X4().tetragrid, matrix.getVertexes(geometryOutput)) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/face.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/face.py new file mode 100644 index 0000000..e0e6e36 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/face.py @@ -0,0 +1,171 @@ +""" +Face of a triangle mesh. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities.vector3index import Vector3Index +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import xml_simple_reader +from fabmetheus_utilities import xml_simple_writer +import cStringIO +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def addFaces(geometryOutput, faces): + 'Add the faces.' + if geometryOutput.__class__ == list: + for element in geometryOutput: + addFaces(element, faces) + return + if geometryOutput.__class__ != dict: + return + for geometryOutputKey in geometryOutput.keys(): + geometryOutputValue = geometryOutput[geometryOutputKey] + if geometryOutputKey == 'face': + for face in geometryOutputValue: + faces.append(face) + else: + addFaces(geometryOutputValue, faces) + +def addGeometryList(elementNode, faces): + "Add vertex elements to an xml element." + for face in faces: + faceElement = xml_simple_reader.ElementNode() + face.addToAttributes( faceElement.attributes ) + faceElement.localName = 'face' + faceElement.parentNode = elementNode + elementNode.childNodes.append( faceElement ) + +def getCommonVertexIndex( edgeFirst, edgeSecond ): + "Get the vertex index that both edges have in common." + for edgeFirstVertexIndex in edgeFirst.vertexIndexes: + if edgeFirstVertexIndex == edgeSecond.vertexIndexes[0] or edgeFirstVertexIndex == edgeSecond.vertexIndexes[1]: + return edgeFirstVertexIndex + print("Inconsistent GNU Triangulated Surface") + print(edgeFirst) + print(edgeSecond) + return 0 + +def getFaces(geometryOutput): + 'Get the faces.' + faces = [] + addFaces(geometryOutput, faces) + return faces + +def processElementNode(elementNode): + "Process the xml element." + face = Face() + face.index = len(elementNode.parentNode.xmlObject.faces) + for vertexIndexIndex in xrange(3): + face.vertexIndexes.append(evaluate.getEvaluatedInt(None, elementNode, 'vertex' + str(vertexIndexIndex))) + elementNode.parentNode.xmlObject.faces.append(face) + + +class Edge: + "An edge of a triangle mesh." + def __init__(self): + "Set the face indexes to None." + self.faceIndexes = [] + self.vertexIndexes = [] + self.zMaximum = None + self.zMinimum = None + + def __repr__(self): + "Get the string representation of this Edge." + return str( self.index ) + ' ' + str( self.faceIndexes ) + ' ' + str(self.vertexIndexes) + + def addFaceIndex( self, faceIndex ): + "Add first None face index to input face index." + self.faceIndexes.append( faceIndex ) + + def getFromVertexIndexes( self, edgeIndex, vertexIndexes ): + "Initialize from two vertex indices." + self.index = edgeIndex + self.vertexIndexes = vertexIndexes[:] + self.vertexIndexes.sort() + return self + + +class Face: + "A face of a triangle mesh." + def __init__(self): + "Initialize." + self.edgeIndexes = [] + self.index = None + self.vertexIndexes = [] + + def __repr__(self): + "Get the string representation of this object info." + output = cStringIO.StringIO() + self.addXML( 2, output ) + return output.getvalue() + + def addToAttributes(self, attributes): + "Add to the attribute dictionary." + for vertexIndexIndex in xrange(len(self.vertexIndexes)): + vertexIndex = self.vertexIndexes[vertexIndexIndex] + attributes['vertex' + str(vertexIndexIndex)] = str(vertexIndex) + + def addXML(self, depth, output): + "Add the xml for this object." + attributes = {} + self.addToAttributes(attributes) + xml_simple_writer.addClosedXMLTag( attributes, depth, 'face', output ) + + def copy(self): + 'Get the copy of this face.' + faceCopy = Face() + faceCopy.edgeIndexes = self.edgeIndexes[:] + faceCopy.index = self.index + faceCopy.vertexIndexes = self.vertexIndexes[:] + return faceCopy + + def getFromEdgeIndexes( self, edgeIndexes, edges, faceIndex ): + "Initialize from edge indices." + if len(self.vertexIndexes) > 0: + return + self.index = faceIndex + self.edgeIndexes = edgeIndexes + for edgeIndex in edgeIndexes: + edges[ edgeIndex ].addFaceIndex( faceIndex ) + for triangleIndex in xrange(3): + indexFirst = ( 3 - triangleIndex ) % 3 + indexSecond = ( 4 - triangleIndex ) % 3 + self.vertexIndexes.append( getCommonVertexIndex( edges[ edgeIndexes[ indexFirst ] ], edges[ edgeIndexes[ indexSecond ] ] ) ) + return self + + def setEdgeIndexesToVertexIndexes( self, edges, edgeTable ): + "Set the edge indexes to the vertex indexes." + if len(self.edgeIndexes) > 0: + return + for triangleIndex in xrange(3): + indexFirst = ( 3 - triangleIndex ) % 3 + indexSecond = ( 4 - triangleIndex ) % 3 + vertexIndexFirst = self.vertexIndexes[ indexFirst ] + vertexIndexSecond = self.vertexIndexes[ indexSecond ] + vertexIndexPair = [ vertexIndexFirst, vertexIndexSecond ] + vertexIndexPair.sort() + edgeIndex = len( edges ) + if str( vertexIndexPair ) in edgeTable: + edgeIndex = edgeTable[ str( vertexIndexPair ) ] + else: + edgeTable[ str( vertexIndexPair ) ] = edgeIndex + edge = Edge().getFromVertexIndexes( edgeIndex, vertexIndexPair ) + edges.append( edge ) + edges[ edgeIndex ].addFaceIndex( self.index ) + self.edgeIndexes.append( edgeIndex ) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/path.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/path.py new file mode 100644 index 0000000..8ec3db1 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/path.py @@ -0,0 +1,204 @@ +""" +Path. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_tools import dictionary +from fabmetheus_utilities.geometry.geometry_tools import vertex +from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import svg_writer +from fabmetheus_utilities import xml_simple_reader +from fabmetheus_utilities import xml_simple_writer + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def convertElementNode(elementNode, geometryOutput): + 'Convert the xml element by geometryOutput.' + if geometryOutput == None: + return + if len(geometryOutput) < 1: + return + if len(geometryOutput) == 1: + firstLoop = geometryOutput[0] + if firstLoop.__class__ == list: + geometryOutput = firstLoop + firstElement = geometryOutput[0] + if firstElement.__class__ == list: + if len(firstElement) > 1: + convertElementNodeRenameByPaths(elementNode, geometryOutput) + else: + convertElementNodeByPath(elementNode, firstElement) + else: + convertElementNodeByPath(elementNode, geometryOutput) + +def convertElementNodeByPath(elementNode, geometryOutput): + 'Convert the xml element to a path xml element.' + createLinkPath(elementNode) + elementNode.xmlObject.vertexes = geometryOutput + vertex.addGeometryList(elementNode, geometryOutput) + +def convertElementNodeRenameByPaths(elementNode, geometryOutput): + 'Convert the xml element to a path xml element and add paths.' + createLinkPath(elementNode) + for geometryOutputChild in geometryOutput: + pathElement = xml_simple_reader.ElementNode() + pathElement.setParentAddToChildNodes(elementNode) + convertElementNodeByPath(pathElement, geometryOutputChild) + +def createLinkPath(elementNode): + 'Create and link a path object.' + elementNode.localName = 'path' + elementNode.linkObject(Path()) + +def processElementNode(elementNode): + 'Process the xml element.' + evaluate.processArchivable(Path, elementNode) + + +class Path(dictionary.Dictionary): + 'A path.' + def __init__(self): + 'Add empty lists.' + dictionary.Dictionary.__init__(self) + self.matrix4X4 = matrix.Matrix() + self.oldChainTetragrid = None + self.transformedPath = None + self.vertexes = [] + + def addXMLInnerSection(self, depth, output): + 'Add the xml section for this object.' + if self.matrix4X4 != None: + self.matrix4X4.addXML(depth, output) + xml_simple_writer.addXMLFromVertexes(depth, output, self.vertexes) + + def getFabricationExtension(self): + 'Get fabrication extension.' + return 'svg' + + def getFabricationText(self, addLayerTemplate): + 'Get fabrication text.' + carving = SVGFabricationCarving(addLayerTemplate, self.elementNode) + carving.setCarveLayerHeight(setting.getSheetThickness(self.elementNode)) + carving.processSVGElement(self.elementNode.getOwnerDocument().fileName) + return str(carving) + + def getMatrix4X4(self): + "Get the matrix4X4." + return self.matrix4X4 + + def getMatrixChainTetragrid(self): + 'Get the matrix chain tetragrid.' + return matrix.getTetragridTimesOther(self.elementNode.parentNode.xmlObject.getMatrixChainTetragrid(), self.matrix4X4.tetragrid) + + def getPaths(self): + 'Get all paths.' + self.transformedPath = None + if len(self.vertexes) > 0: + return dictionary.getAllPaths([self.vertexes], self) + return dictionary.getAllPaths([], self) + + def getTransformedPaths(self): + 'Get all transformed paths.' + if self.elementNode == None: + return dictionary.getAllPaths([self.vertexes], self) + chainTetragrid = self.getMatrixChainTetragrid() + if self.oldChainTetragrid != chainTetragrid: + self.oldChainTetragrid = chainTetragrid + self.transformedPath = None + if self.transformedPath == None: + self.transformedPath = matrix.getTransformedVector3s(chainTetragrid, self.vertexes) + if len(self.transformedPath) > 0: + return dictionary.getAllTransformedPaths([self.transformedPath], self) + return dictionary.getAllTransformedPaths([], self) + + +class SVGFabricationCarving: + 'An svg carving.' + def __init__(self, addLayerTemplate, elementNode): + 'Add empty lists.' + self.addLayerTemplate = addLayerTemplate + self.elementNode = elementNode + self.layerHeight = 1.0 + self.loopLayers = [] + + def __repr__(self): + 'Get the string representation of this carving.' + return self.getCarvedSVG() + + def addXML(self, depth, output): + 'Add xml for this object.' + xml_simple_writer.addXMLFromObjects(depth, self.loopLayers, output) + + def getCarveBoundaryLayers(self): + 'Get the boundary layers.' + return self.loopLayers + + def getCarveCornerMaximum(self): + 'Get the corner maximum of the vertexes.' + return self.cornerMaximum + + def getCarveCornerMinimum(self): + 'Get the corner minimum of the vertexes.' + return self.cornerMinimum + + def getCarvedSVG(self): + 'Get the carved svg text.' + return svg_writer.getSVGByLoopLayers(self.addLayerTemplate, self, self.loopLayers) + + def getCarveLayerHeight(self): + 'Get the layer height.' + return self.layerHeight + + def getFabmetheusXML(self): + 'Return the fabmetheus XML.' + return self.elementNode.getOwnerDocument().getOriginalRoot() + + def getInterpretationSuffix(self): + 'Return the suffix for a carving.' + return 'svg' + + def processSVGElement(self, fileName): + 'Parse SVG element and store the layers.' + self.fileName = fileName + paths = self.elementNode.xmlObject.getPaths() + oldZ = None + self.loopLayers = [] + loopLayer = None + for path in paths: + if len(path) > 0: + z = path[0].z + if z != oldZ: + loopLayer = euclidean.LoopLayer(z) + self.loopLayers.append(loopLayer) + oldZ = z + loopLayer.loops.append(euclidean.getComplexPath(path)) + if len(self.loopLayers) < 1: + return + self.cornerMaximum = Vector3(-987654321.0, -987654321.0, -987654321.0) + self.cornerMinimum = Vector3(987654321.0, 987654321.0, 987654321.0) + svg_writer.setSVGCarvingCorners(self.cornerMaximum, self.cornerMinimum, self.layerHeight, self.loopLayers) + + def setCarveImportRadius( self, importRadius ): + 'Set the import radius.' + pass + + def setCarveIsCorrectMesh( self, isCorrectMesh ): + 'Set the is correct mesh flag.' + pass + + def setCarveLayerHeight( self, layerHeight ): + 'Set the layer height.' + self.layerHeight = layerHeight diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/path_elements/__init__.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/path_elements/__init__.py new file mode 100644 index 0000000..58ec332 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/path_elements/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 4 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/path_elements/arc.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/path_elements/arc.py new file mode 100644 index 0000000..0adb453 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/path_elements/arc.py @@ -0,0 +1,51 @@ +""" +Arc vertexes. + +From: +http://www.w3.org/TR/SVG/paths.html#PathDataEllipticalArcCommands + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import svg_reader +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__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) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/path_elements/cubic.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/path_elements/cubic.py new file mode 100644 index 0000000..853729f --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/path_elements/cubic.py @@ -0,0 +1,58 @@ +""" +Cubic vertexes. + +From: +http://www.w3.org/TR/SVG/paths.html#PathDataCubicBezierCommands + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import svg_reader + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__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) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/path_elements/quadratic.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/path_elements/quadratic.py new file mode 100644 index 0000000..d233d4c --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/path_elements/quadratic.py @@ -0,0 +1,45 @@ +""" +Quadratic vertexes. + +From: +http://www.w3.org/TR/SVG/paths.html#PathDataQuadraticBezierCommands + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import svg_reader + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__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) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/vertex.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/vertex.py new file mode 100644 index 0000000..b002ed6 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_tools/vertex.py @@ -0,0 +1,45 @@ +""" +Vertex of a triangle mesh. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities import xml_simple_reader + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__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)) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/__init__.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/__init__.py new file mode 100644 index 0000000..cefa3e7 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/__init__.py @@ -0,0 +1,12 @@ +""" +This page is in the table of contents. +This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +""" +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 3 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/boolean_geometry.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/boolean_geometry.py new file mode 100644 index 0000000..ede07a4 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/boolean_geometry.py @@ -0,0 +1,196 @@ +""" +This page is in the table of contents. +The xml.py script is an import translator plugin to get a carving from an Art of Illusion xml file. + +An import plugin is a script in the interpret_plugins folder which has the function getCarving. It is meant to be run from the interpret tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name. + +The getCarving function takes the file name of an xml file and returns the carving. + +An xml file can be exported from Art of Illusion by going to the "File" menu, then going into the "Export" menu item, then picking the XML choice. This will bring up the XML file chooser window, choose a place to save the file then click "OK". Leave the "compressFile" checkbox unchecked. All the objects from the scene will be exported, this plugin will ignore the light and camera. If you want to fabricate more than one object at a time, you can have multiple objects in the Art of Illusion scene and they will all be carved, then fabricated together. + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting +from fabmetheus_utilities.geometry.geometry_utilities import boolean_solid +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import settings +from fabmetheus_utilities import xml_simple_writer +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Nophead \nArt of Illusion ' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getEmptyZLoops(archivableObjects, importRadius, shouldPrintWarning, z, zoneArrangement): + 'Get loops at empty z level.' + emptyZ = zoneArrangement.getEmptyZ(z) + visibleObjects = evaluate.getVisibleObjects(archivableObjects) + visibleObjectLoopsList = boolean_solid.getVisibleObjectLoopsList(importRadius, visibleObjects, emptyZ) + loops = euclidean.getConcatenatedList(visibleObjectLoopsList) + if euclidean.isLoopListIntersecting(loops): + loops = boolean_solid.getLoopsUnion(importRadius, visibleObjectLoopsList) + if shouldPrintWarning: + print('Warning, the triangle mesh slice intersects itself in getExtruderPaths in boolean_geometry.') + print('Something will still be printed, but there is no guarantee that it will be the correct shape.') + print('Once the gcode is saved, you should check over the layer with a z of:') + print(z) + return loops + +def getLoopLayers(archivableObjects, importRadius, layerHeight, maximumZ, shouldPrintWarning, z, zoneArrangement): + 'Get loop layers.' + loopLayers = [] + while z <= maximumZ: + triangle_mesh.getLoopLayerAppend(loopLayers, z).loops = getEmptyZLoops(archivableObjects, importRadius, True, z, zoneArrangement) + z += layerHeight + return loopLayers + +def getMinimumZ(geometryObject): + 'Get the minimum of the minimum z of the archivableObjects and the object.' + booleanGeometry = BooleanGeometry() + booleanGeometry.archivableObjects = geometryObject.archivableObjects + booleanGeometry.importRadius = setting.getImportRadius(geometryObject.elementNode) + booleanGeometry.layerHeight = setting.getLayerHeight(geometryObject.elementNode) + archivableMinimumZ = booleanGeometry.getMinimumZ() + geometryMinimumZ = geometryObject.getMinimumZ() + if archivableMinimumZ == None: + return geometryMinimumZ + if geometryMinimumZ == None: + return archivableMinimumZ + return min(archivableMinimumZ, geometryMinimumZ) + + +class BooleanGeometry: + 'A boolean geometry scene.' + def __init__(self): + 'Add empty lists.' + self.archivableObjects = [] + self.belowLoops = [] + self.importRadius = 0.6 + self.layerHeight = 0.4 + self.loopLayers = [] + + def __repr__(self): + 'Get the string representation of this carving.' + elementNode = None + if len(self.archivableObjects) > 0: + elementNode = self.archivableObjects[0].elementNode + output = xml_simple_writer.getBeginGeometryXMLOutput(elementNode) + self.addXML( 1, output ) + return xml_simple_writer.getEndGeometryXMLString(output) + + def addXML(self, depth, output): + 'Add xml for this object.' + xml_simple_writer.addXMLFromObjects( depth, self.archivableObjects, output ) + + def getCarveBoundaryLayers(self): + 'Get the boundary layers.' + if self.getMinimumZ() == None: + return [] + z = self.minimumZ + 0.5 * self.layerHeight + self.loopLayers = getLoopLayers(self.archivableObjects, self.importRadius, self.layerHeight, self.maximumZ, True, z, self.zoneArrangement) + self.cornerMaximum = Vector3(-912345678.0, -912345678.0, -912345678.0) + self.cornerMinimum = Vector3(912345678.0, 912345678.0, 912345678.0) + for loopLayer in self.loopLayers: + for loop in loopLayer.loops: + for point in loop: + pointVector3 = Vector3(point.real, point.imag, loopLayer.z) + self.cornerMaximum.maximize(pointVector3) + self.cornerMinimum.minimize(pointVector3) + self.cornerMaximum.z += self.halfHeight + self.cornerMinimum.z -= self.halfHeight + for loopLayerIndex in xrange(len(self.loopLayers) -1, -1, -1): + loopLayer = self.loopLayers[loopLayerIndex] + if len(loopLayer.loops) > 0: + return self.loopLayers[: loopLayerIndex + 1] + return [] + + def getCarveCornerMaximum(self): + 'Get the corner maximum of the vertexes.' + return self.cornerMaximum + + def getCarveCornerMinimum(self): + 'Get the corner minimum of the vertexes.' + return self.cornerMinimum + + def getCarveLayerHeight(self): + 'Get the layer height.' + return self.layerHeight + + def getFabmetheusXML(self): + 'Return the fabmetheus XML.' + if len(self.archivableObjects) > 0: + return self.archivableObjects[0].elementNode.getOwnerDocument().getOriginalRoot() + return None + + def getInterpretationSuffix(self): + 'Return the suffix for a boolean carving.' + return 'xml' + + def getMatrix4X4(self): + 'Get the matrix4X4.' + return None + + def getMatrixChainTetragrid(self): + 'Get the matrix chain tetragrid.' + return None + + def getMinimumZ(self): + 'Get the minimum z.' + vertexes = [] + for visibleObject in evaluate.getVisibleObjects(self.archivableObjects): + vertexes += visibleObject.getTransformedVertexes() + if len(vertexes) < 1: + return None + self.maximumZ = -912345678.0 + self.minimumZ = 912345678.0 + for vertex in vertexes: + self.maximumZ = max(self.maximumZ, vertex.z) + self.minimumZ = min(self.minimumZ, vertex.z) + self.zoneArrangement = triangle_mesh.ZoneArrangement(self.layerHeight, vertexes) + self.halfHeight = 0.5 * self.layerHeight + self.setActualMinimumZ() + return self.minimumZ + + def getNumberOfEmptyZLoops(self, z): + 'Get number of empty z loops.' + return len(getEmptyZLoops(self.archivableObjects, self.importRadius, False, z, self.zoneArrangement)) + + def setActualMinimumZ(self): + 'Get the actual minimum z at the lowest rotated boundary layer.' + halfHeightOverMyriad = 0.0001 * self.halfHeight + while self.minimumZ < self.maximumZ: + if self.getNumberOfEmptyZLoops(self.minimumZ + halfHeightOverMyriad) > 0: + if self.getNumberOfEmptyZLoops(self.minimumZ - halfHeightOverMyriad) < 1: + return + increment = -self.halfHeight + while abs(increment) > halfHeightOverMyriad: + self.minimumZ += increment + increment = 0.5 * abs(increment) + if self.getNumberOfEmptyZLoops(self.minimumZ) > 0: + increment = -increment + self.minimumZ = round(self.minimumZ, -int(round(math.log10(halfHeightOverMyriad) + 1.5))) + return + self.minimumZ += self.layerHeight + + def setCarveImportRadius( self, importRadius ): + 'Set the import radius.' + self.importRadius = importRadius + + def setCarveIsCorrectMesh( self, isCorrectMesh ): + 'Set the is correct mesh flag.' + self.isCorrectMesh = isCorrectMesh + + def setCarveLayerHeight( self, layerHeight ): + 'Set the layer height.' + self.layerHeight = layerHeight diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/boolean_solid.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/boolean_solid.py new file mode 100644 index 0000000..1b2c303 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/boolean_solid.py @@ -0,0 +1,235 @@ +""" +This page is in the table of contents. +The xml.py script is an import translator plugin to get a carving from an Art of Illusion xml file. + +An import plugin is a script in the interpret_plugins folder which has the function getCarving. It is meant to be run from the interpret tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name. + +The getCarving function takes the file name of an xml file and returns the carving. + +An xml file can be exported from Art of Illusion by going to the "File" menu, then going into the "Export" menu item, then picking the XML choice. This will bring up the XML file chooser window, choose a place to save the file then click "OK". Leave the "compressFile" checkbox unchecked. All the objects from the scene will be exported, this plugin will ignore the light and camera. If you want to fabricate more than one object at a time, you can have multiple objects in the Art of Illusion scene and they will all be carved, then fabricated together. + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.solids import group +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Nophead \nArt of Illusion ' +__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') : ] diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate.py new file mode 100644 index 0000000..0c968ce --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate.py @@ -0,0 +1,1940 @@ +""" +Evaluate expressions. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +import math +import os +import sys +import traceback + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalModuleFunctionsDictionary = {} + + +def addPrefixDictionary(dictionary, keys, value): + 'Add prefixed key values to dictionary.' + for key in keys: + dictionary[key.lstrip('_')] = value + +def addQuoteWord(evaluatorWords, word): + 'Add quote word and remainder if the word starts with a quote character or dollar sign, otherwise add the word.' + if len(word) < 2: + evaluatorWords.append(word) + return + firstCharacter = word[0] + if firstCharacter == '$': + dotIndex = word.find('.', 1) + if dotIndex > -1: + evaluatorWords.append(word[: dotIndex]) + evaluatorWords.append(word[dotIndex :]) + return + if firstCharacter != '"' and firstCharacter != "'": + evaluatorWords.append(word) + return + nextQuoteIndex = word.find(firstCharacter, 1) + if nextQuoteIndex < 0 or nextQuoteIndex == len(word) - 1: + evaluatorWords.append(word) + return + nextQuoteIndex += 1 + evaluatorWords.append(word[: nextQuoteIndex]) + evaluatorWords.append(word[nextQuoteIndex :]) + +def addToPathsRecursively(paths, vector3Lists): + 'Add to vector3 paths recursively.' + if vector3Lists.__class__ == Vector3 or vector3Lists.__class__ .__name__ == 'Vector3Index': + paths.append([ vector3Lists ]) + return + path = [] + for vector3List in vector3Lists: + if vector3List.__class__ == list: + addToPathsRecursively(paths, vector3List) + elif vector3List.__class__ == Vector3: + path.append(vector3List) + if len(path) > 0: + paths.append(path) + +def addValueToEvaluatedDictionary(elementNode, evaluatedDictionary, key): + 'Get the evaluated dictionary.' + value = getEvaluatedValueObliviously(elementNode, key) + if value == None: + valueString = str(elementNode.attributes[key]) + print('Warning, addValueToEvaluatedDictionary in evaluate can not get a value for:') + print(valueString) + evaluatedDictionary[key + '__Warning__'] = 'Can not evaluate: ' + valueString.replace('"', ' ').replace( "'", ' ') + else: + evaluatedDictionary[key] = value + +def addVector3ToElementNode(elementNode, key, vector3): + 'Add vector3 to xml element.' + elementNode.attributes[key] = '[%s,%s,%s]' % (vector3.x, vector3.y, vector3.z) + +def compareExecutionOrderAscending(module, otherModule): + 'Get comparison in order to sort modules in ascending execution order.' + if module.globalExecutionOrder < otherModule.globalExecutionOrder: + return -1 + if module.globalExecutionOrder > otherModule.globalExecutionOrder: + return 1 + if module.__name__ < otherModule.__name__: + return -1 + return int(module.__name__ > otherModule.__name__) + +def convertToPaths(dictionary): + 'Recursively convert any ElementNodes to paths.' + if dictionary.__class__ == Vector3 or dictionary.__class__.__name__ == 'Vector3Index': + return + keys = getKeys(dictionary) + if keys == None: + return + for key in keys: + value = dictionary[key] + if value.__class__.__name__ == 'ElementNode': + if value.xmlObject != None: + dictionary[key] = getFloatListListsByPaths(value.xmlObject.getPaths()) + else: + convertToPaths(dictionary[key]) + +def convertToTransformedPaths(dictionary): + 'Recursively convert any ElementNodes to paths.' + if dictionary.__class__ == Vector3 or dictionary.__class__.__name__ == 'Vector3Index': + return + keys = getKeys(dictionary) + if keys == None: + return + for key in keys: + value = dictionary[key] + if value.__class__.__name__ == 'ElementNode': + if value.xmlObject != None: + dictionary[key] = value.xmlObject.getTransformedPaths() + else: + convertToTransformedPaths(dictionary[key]) + +def executeLeftOperations( evaluators, operationLevel ): + 'Evaluate the expression value from the numeric and operation evaluators.' + for negativeIndex in xrange( - len(evaluators), - 1 ): + evaluatorIndex = negativeIndex + len(evaluators) + evaluators[evaluatorIndex].executeLeftOperation( evaluators, evaluatorIndex, operationLevel ) + +def executeNextEvaluatorArguments(evaluator, evaluators, evaluatorIndex, nextEvaluator): + 'Execute the nextEvaluator arguments.' + if evaluator.value == None: + print('Warning, executeNextEvaluatorArguments in evaluate can not get a evaluator.value for:') + print(evaluatorIndex) + print(evaluators) + print(evaluator) + return + nextEvaluator.value = evaluator.value(*nextEvaluator.arguments) + del evaluators[evaluatorIndex] + +def executePairOperations(evaluators, operationLevel): + 'Evaluate the expression value from the numeric and operation evaluators.' + for negativeIndex in xrange(1 - len(evaluators), - 1): + evaluatorIndex = negativeIndex + len(evaluators) + evaluators[evaluatorIndex].executePairOperation(evaluators, evaluatorIndex, operationLevel) + +def getBracketEvaluators(bracketBeginIndex, bracketEndIndex, evaluators): + 'Get the bracket evaluators.' + return getEvaluatedExpressionValueEvaluators(evaluators[bracketBeginIndex + 1 : bracketEndIndex]) + +def getBracketsExist(evaluators): + 'Evaluate the expression value.' + bracketBeginIndex = None + for negativeIndex in xrange( - len(evaluators), 0 ): + bracketEndIndex = negativeIndex + len(evaluators) + evaluatorEnd = evaluators[ bracketEndIndex ] + evaluatorWord = evaluatorEnd.word + if evaluatorWord in ['(', '[', '{']: + bracketBeginIndex = bracketEndIndex + elif evaluatorWord in [')', ']', '}']: + if bracketBeginIndex == None: + print('Warning, bracketBeginIndex in evaluateBrackets in evaluate is None.') + print('This may be because the brackets are not balanced.') + print(evaluators) + del evaluators[ bracketEndIndex ] + return + evaluators[ bracketBeginIndex ].executeBracket(bracketBeginIndex, bracketEndIndex, evaluators) + evaluators[ bracketBeginIndex ].word = None + return True + return False + +def getBracketValuesDeleteEvaluator(bracketBeginIndex, bracketEndIndex, evaluators): + 'Get the bracket values and delete the evaluator.' + evaluatedExpressionValueEvaluators = getBracketEvaluators(bracketBeginIndex, bracketEndIndex, evaluators) + bracketValues = [] + for evaluatedExpressionValueEvaluator in evaluatedExpressionValueEvaluators: + bracketValues.append( evaluatedExpressionValueEvaluator.value ) + del evaluators[ bracketBeginIndex + 1: bracketEndIndex + 1 ] + return bracketValues + +def getCapitalizedSuffixKey(prefix, suffix): + 'Get key with capitalized suffix.' + if prefix == '' or prefix.endswith('.'): + return prefix + suffix + return prefix + suffix[:1].upper()+suffix[1:] + +def getDictionarySplitWords(dictionary, value): + 'Get split line for evaluators.' + if getIsQuoted(value): + return [value] + for dictionaryKey in dictionary.keys(): + value = value.replace(dictionaryKey, ' ' + dictionaryKey + ' ') + dictionarySplitWords = [] + for word in value.split(): + dictionarySplitWords.append(word) + return dictionarySplitWords + +def getElementNodeByKey(elementNode, key): + 'Get the xml element by key.' + if key not in elementNode.attributes: + return None + word = str(elementNode.attributes[key]).strip() + evaluatedLinkValue = getEvaluatedLinkValue(elementNode, word) + if evaluatedLinkValue.__class__.__name__ == 'ElementNode': + return evaluatedLinkValue + print('Warning, could not get ElementNode in getElementNodeByKey in evaluate for:') + print(key) + print(evaluatedLinkValue) + print(elementNode) + return None + +def getElementNodeObject(evaluatedLinkValue): + 'Get ElementNodeObject.' + if evaluatedLinkValue.__class__.__name__ != 'ElementNode': + print('Warning, could not get ElementNode in getElementNodeObject in evaluate for:') + print(evaluatedLinkValue.__class__.__name__) + print(evaluatedLinkValue) + return None + if evaluatedLinkValue.xmlObject == None: + print('Warning, evaluatedLinkValue.xmlObject is None in getElementNodeObject in evaluate for:') + print(evaluatedLinkValue) + return None + return evaluatedLinkValue.xmlObject + +def getElementNodesByKey(elementNode, key): + 'Get the xml elements by key.' + if key not in elementNode.attributes: + return [] + word = str(elementNode.attributes[key]).strip() + evaluatedLinkValue = getEvaluatedLinkValue(elementNode, word) + if evaluatedLinkValue.__class__.__name__ == 'ElementNode': + return [evaluatedLinkValue] + if evaluatedLinkValue.__class__ == list: + return evaluatedLinkValue + print('Warning, could not get ElementNodes in getElementNodesByKey in evaluate for:') + print(key) + print(evaluatedLinkValue) + print(elementNode) + return [] + +def getEndIndexConvertEquationValue( bracketEndIndex, evaluatorIndex, evaluators ): + 'Get the bracket end index and convert the equation value evaluators into a string.' + evaluator = evaluators[evaluatorIndex] + if evaluator.__class__ != EvaluatorValue: + return bracketEndIndex + if not evaluator.word.startswith('equation.'): + return bracketEndIndex + if evaluators[ evaluatorIndex + 1 ].word != ':': + return bracketEndIndex + valueBeginIndex = evaluatorIndex + 2 + equationValueString = '' + for valueEvaluatorIndex in xrange( valueBeginIndex, len(evaluators) ): + valueEvaluator = evaluators[ valueEvaluatorIndex ] + if valueEvaluator.word == ',' or valueEvaluator.word == '}': + if equationValueString == '': + return bracketEndIndex + else: + evaluators[ valueBeginIndex ] = EvaluatorValue( equationValueString ) + valueDeleteIndex = valueBeginIndex + 1 + del evaluators[ valueDeleteIndex : valueEvaluatorIndex ] + return bracketEndIndex - valueEvaluatorIndex + valueDeleteIndex + equationValueString += valueEvaluator.word + return bracketEndIndex + +def getEvaluatedBoolean(defaultValue, elementNode, key): + 'Get the evaluated boolean.' + if elementNode == None: + return defaultValue + if key in elementNode.attributes: + return euclidean.getBooleanFromValue(getEvaluatedValueObliviously(elementNode, key)) + return defaultValue + +def getEvaluatedDictionaryByCopyKeys(copyKeys, elementNode): + 'Get the evaluated dictionary by copyKeys.' + evaluatedDictionary = {} + for key in elementNode.attributes.keys(): + if key in copyKeys: + evaluatedDictionary[key] = elementNode.attributes[key] + else: + addValueToEvaluatedDictionary(elementNode, evaluatedDictionary, key) + return evaluatedDictionary + +def getEvaluatedDictionaryByEvaluationKeys(elementNode, evaluationKeys): + 'Get the evaluated dictionary.' + evaluatedDictionary = {} + for key in elementNode.attributes.keys(): + if key in evaluationKeys: + addValueToEvaluatedDictionary(elementNode, evaluatedDictionary, key) + return evaluatedDictionary + +def getEvaluatedExpressionValue(elementNode, value): + 'Evaluate the expression value.' + try: + return getEvaluatedExpressionValueBySplitLine(elementNode, getEvaluatorSplitWords(value)) + except: + print('Warning, in getEvaluatedExpressionValue in evaluate could not get a value for:') + print(value) + traceback.print_exc(file=sys.stdout) + return None + +def getEvaluatedExpressionValueBySplitLine(elementNode, words): + 'Evaluate the expression value.' + evaluators = [] + for wordIndex, word in enumerate(words): + nextWord = '' + nextWordIndex = wordIndex + 1 + if nextWordIndex < len(words): + nextWord = words[nextWordIndex] + evaluator = getEvaluator(elementNode, evaluators, nextWord, word) + if evaluator != None: + evaluators.append(evaluator) + while getBracketsExist(evaluators): + pass + evaluatedExpressionValueEvaluators = getEvaluatedExpressionValueEvaluators(evaluators) + if len( evaluatedExpressionValueEvaluators ) > 0: + return evaluatedExpressionValueEvaluators[0].value + return None + +def getEvaluatedExpressionValueEvaluators(evaluators): + 'Evaluate the expression value from the numeric and operation evaluators.' + for evaluatorIndex, evaluator in enumerate(evaluators): + evaluator.executeCenterOperation(evaluators, evaluatorIndex) + for negativeIndex in xrange(1 - len(evaluators), 0): + evaluatorIndex = negativeIndex + len(evaluators) + evaluators[evaluatorIndex].executeRightOperation(evaluators, evaluatorIndex) + executeLeftOperations(evaluators, 200) + for operationLevel in [80, 60, 40, 20, 15]: + executePairOperations(evaluators, operationLevel) + executeLeftOperations(evaluators, 13) + executePairOperations(evaluators, 12) + for negativeIndex in xrange(-len(evaluators), 0): + evaluatorIndex = negativeIndex + len(evaluators) + evaluators[evaluatorIndex].executePairOperation(evaluators, evaluatorIndex, 10) + for evaluatorIndex in xrange(len(evaluators) - 1, -1, -1): + evaluators[evaluatorIndex].executePairOperation(evaluators, evaluatorIndex, 0) + return evaluators + +def getEvaluatedFloat(defaultValue, elementNode, key): + 'Get the evaluated float.' + if elementNode == None: + return defaultValue + if key in elementNode.attributes: + return euclidean.getFloatFromValue(getEvaluatedValueObliviously(elementNode, key)) + return defaultValue + +def getEvaluatedInt(defaultValue, elementNode, key): + 'Get the evaluated int.' + if elementNode == None: + return None + if key in elementNode.attributes: + try: + return getIntFromFloatString(getEvaluatedValueObliviously(elementNode, key)) + except: + print('Warning, could not evaluate the int.') + print(key) + print(elementNode.attributes[key]) + return defaultValue + +def getEvaluatedIntByKeys(defaultValue, elementNode, keys): + 'Get the evaluated int by keys.' + for key in keys: + defaultValue = getEvaluatedInt(defaultValue, elementNode, key) + return defaultValue + +def getEvaluatedLinkValue(elementNode, word): + 'Get the evaluated link value.' + if word == '': + return '' + if getStartsWithCurlyEqualRoundSquare(word): + return getEvaluatedExpressionValue(elementNode, word) + return word + +def getEvaluatedString(defaultValue, elementNode, key): + 'Get the evaluated string.' + if elementNode == None: + return defaultValue + if key in elementNode.attributes: + return str(getEvaluatedValueObliviously(elementNode, key)) + return defaultValue + +def getEvaluatedValue(defaultValue, elementNode, key): + 'Get the evaluated value.' + if elementNode == None: + return defaultValue + if key in elementNode.attributes: + return getEvaluatedValueObliviously(elementNode, key) + return defaultValue + +def getEvaluatedValueObliviously(elementNode, key): + 'Get the evaluated value.' + value = str(elementNode.attributes[key]).strip() + if key == 'id' or key == 'name' or key == 'tags': + return value + return getEvaluatedLinkValue(elementNode, value) + +def getEvaluator(elementNode, evaluators, nextWord, word): + 'Get the evaluator.' + if word in globalSplitDictionary: + return globalSplitDictionary[word](elementNode, word) + firstCharacter = word[: 1] + if firstCharacter == "'" or firstCharacter == '"': + if len(word) > 1: + if firstCharacter == word[-1]: + return EvaluatorValue(word[1 : -1]) + if firstCharacter == '$': + return EvaluatorValue(word[1 :]) + dotIndex = word.find('.') + functions = elementNode.getXMLProcessor().functions + if dotIndex > -1 and len(word) > 1: + if dotIndex == 0 and word[1].isalpha(): + return EvaluatorAttribute(elementNode, word) + if dotIndex > 0: + untilDot = word[: dotIndex] + if untilDot in globalModuleEvaluatorDictionary: + return globalModuleEvaluatorDictionary[untilDot](elementNode, word) + if len(functions) > 0: + if untilDot in functions[-1].localDictionary: + return EvaluatorLocal(elementNode, word) + if firstCharacter.isalpha() or firstCharacter == '_': + if len(functions) > 0: + if word in functions[-1].localDictionary: + return EvaluatorLocal(elementNode, word) + wordElement = elementNode.getElementNodeByID(word) + if wordElement != None: + if wordElement.getNodeName() == 'class': + return EvaluatorClass(wordElement, word) + if wordElement.getNodeName() == 'function': + return EvaluatorFunction(wordElement, word) + return EvaluatorValue(word) + return EvaluatorNumeric(elementNode, word) + +def getEvaluatorSplitWords(value): + 'Get split words for evaluators.' + if value.startswith('='): + value = value[len('=') :] + if len(value) < 1: + return [] + global globalDictionaryOperatorBegin + uniqueQuoteIndex = 0 + word = '' + quoteString = None + quoteDictionary = {} + for characterIndex in xrange(len(value)): + character = value[characterIndex] + if character == '"' or character == "'": + if quoteString == None: + quoteString = '' + elif quoteString != None: + if character == quoteString[: 1]: + uniqueQuoteIndex = getUniqueQuoteIndex(uniqueQuoteIndex, value) + uniqueToken = getTokenByNumber(uniqueQuoteIndex) + quoteDictionary[uniqueToken] = quoteString + character + character = uniqueToken + quoteString = None + if quoteString == None: + word += character + else: + quoteString += character + beginSplitWords = getDictionarySplitWords(globalDictionaryOperatorBegin, word) + global globalSplitDictionaryOperator + evaluatorSplitWords = [] + for beginSplitWord in beginSplitWords: + if beginSplitWord in globalDictionaryOperatorBegin: + evaluatorSplitWords.append(beginSplitWord) + else: + evaluatorSplitWords += getDictionarySplitWords(globalSplitDictionaryOperator, beginSplitWord) + for evaluatorSplitWordIndex, evaluatorSplitWord in enumerate(evaluatorSplitWords): + for quoteDictionaryKey in quoteDictionary.keys(): + if quoteDictionaryKey in evaluatorSplitWord: + evaluatorSplitWords[evaluatorSplitWordIndex] = evaluatorSplitWord.replace(quoteDictionaryKey, quoteDictionary[quoteDictionaryKey]) + evaluatorTransitionWords = [] + for evaluatorSplitWord in evaluatorSplitWords: + addQuoteWord(evaluatorTransitionWords, evaluatorSplitWord) + return evaluatorTransitionWords + +def getFloatListFromBracketedString( bracketedString ): + 'Get list from a bracketed string.' + if not getIsBracketed( bracketedString ): + return None + bracketedString = bracketedString.strip().replace('[', '').replace(']', '').replace('(', '').replace(')', '') + if len( bracketedString ) < 1: + return [] + splitLine = bracketedString.split(',') + floatList = [] + for word in splitLine: + evaluatedFloat = euclidean.getFloatFromValue(word) + if evaluatedFloat != None: + floatList.append( evaluatedFloat ) + return floatList + +def getFloatListListsByPaths(paths): + 'Get float lists by paths.' + floatListLists = [] + for path in paths: + floatListList = [] + for point in path: + floatListList.append( point.getFloatList() ) + return floatListLists + +def getIntFromFloatString(value): + 'Get the int from the string.' + floatString = str(value).strip() + if floatString == '': + return None + dotIndex = floatString.find('.') + if dotIndex < 0: + return int(value) + return int( round( float(floatString) ) ) + +def getIsBracketed(word): + 'Determine if the word is bracketed.' + if len(word) < 2: + return False + firstCharacter = word[0] + lastCharacter = word[-1] + if firstCharacter == '(' and lastCharacter == ')': + return True + return firstCharacter == '[' and lastCharacter == ']' + +def getIsQuoted(word): + 'Determine if the word is quoted.' + if len(word) < 2: + return False + firstCharacter = word[0] + lastCharacter = word[-1] + if firstCharacter == '"' and lastCharacter == '"': + return True + return firstCharacter == "'" and lastCharacter == "'" + +def getKeys(repository): + 'Get keys for repository.' + repositoryClass = repository.__class__ + if repositoryClass == list or repositoryClass == tuple: + return range(len(repository)) + if repositoryClass == dict: + return repository.keys() + return None + +def getLocalAttributeValueString(key, valueString): + 'Get the local attribute value string with augmented assignment.' + augmentedStatements = '+= -= *= /= %= **='.split() + for augmentedStatement in augmentedStatements: + if valueString.startswith(augmentedStatement): + return key + augmentedStatement[: -1] + valueString[len(augmentedStatement) :] + return valueString + +def getMatchingPlugins(elementNode, namePathDictionary): + 'Get the plugins whose names are in the attribute dictionary.' + matchingPlugins = [] + namePathDictionaryCopy = namePathDictionary.copy() + for key in elementNode.attributes: + dotIndex = key.find('.') + if dotIndex > - 1: + keyUntilDot = key[: dotIndex] + if keyUntilDot in namePathDictionaryCopy: + pluginModule = archive.getModuleWithPath( namePathDictionaryCopy[ keyUntilDot ] ) + del namePathDictionaryCopy[ keyUntilDot ] + if pluginModule != None: + matchingPlugins.append( pluginModule ) + return matchingPlugins + +def getNextChildIndex(elementNode): + 'Get the next childNode index.' + for childNodeIndex, childNode in enumerate( elementNode.parentNode.childNodes ): + if childNode == elementNode: + return childNodeIndex + 1 + return len( elementNode.parentNode.childNodes ) + +def getPathByKey(defaultPath, elementNode, key): + 'Get path from prefix and xml element.' + if key not in elementNode.attributes: + return defaultPath + word = str(elementNode.attributes[key]).strip() + evaluatedLinkValue = getEvaluatedLinkValue(elementNode, word) + if evaluatedLinkValue.__class__ == list: + return getPathByList(evaluatedLinkValue) + elementNodeObject = getElementNodeObject(evaluatedLinkValue) + if elementNodeObject == None: + return defaultPath + return elementNodeObject.getPaths()[0] + +def getPathByList(vertexList): + 'Get the paths by list.' + if len(vertexList) < 1: + return Vector3() + if vertexList[0].__class__ != list: + vertexList = [vertexList] + path = [] + for floatList in vertexList: + vector3 = getVector3ByFloatList(floatList, Vector3()) + path.append(vector3) + return path + +def getPathByPrefix(elementNode, path, prefix): + 'Get path from prefix and xml element.' + if len(path) < 2: + print('Warning, bug, path is too small in evaluate in setPathByPrefix.') + return + pathByKey = getPathByKey([], elementNode, getCapitalizedSuffixKey(prefix, 'path')) + if len( pathByKey ) < len(path): + for pointIndex in xrange( len( pathByKey ) ): + path[pointIndex] = pathByKey[pointIndex] + else: + path = pathByKey + path[0] = getVector3ByPrefix(path[0], elementNode, getCapitalizedSuffixKey(prefix, 'pathStart')) + path[-1] = getVector3ByPrefix(path[-1], elementNode, getCapitalizedSuffixKey(prefix, 'pathEnd')) + return path + +def getPathsByKey(defaultPaths, elementNode, key): + 'Get paths by key.' + if key not in elementNode.attributes: + return defaultPaths + word = str(elementNode.attributes[key]).strip() + evaluatedLinkValue = getEvaluatedLinkValue(elementNode, word) + if evaluatedLinkValue.__class__ == dict or evaluatedLinkValue.__class__ == list: + convertToPaths(evaluatedLinkValue) + return getPathsByLists(evaluatedLinkValue) + elementNodeObject = getElementNodeObject(evaluatedLinkValue) + if elementNodeObject == None: + return defaultPaths + return elementNodeObject.getPaths() + +def getPathsByLists(vertexLists): + 'Get paths by lists.' + vector3Lists = getVector3ListsRecursively(vertexLists) + paths = [] + addToPathsRecursively(paths, vector3Lists) + return paths + +def getRadiusArealizedBasedOnAreaRadius(elementNode, radius, sides): + 'Get the areal radius from the radius, number of sides and cascade radiusAreal.' + if elementNode.getCascadeBoolean(False, 'radiusAreal'): + return radius + return radius * euclidean.getRadiusArealizedMultiplier(sides) + +def getSidesBasedOnPrecision(elementNode, radius): + 'Get the number of polygon sides.' + return int(math.ceil(math.sqrt(0.5 * radius / setting.getPrecision(elementNode)) * math.pi)) + +def getSidesMinimumThreeBasedOnPrecision(elementNode, radius): + 'Get the number of polygon sides, with a minimum of three.' + return max(getSidesBasedOnPrecision(elementNode, radius), 3) + +def getSidesMinimumThreeBasedOnPrecisionSides(elementNode, radius): + 'Get the number of polygon sides, with a minimum of three.' + sides = getSidesMinimumThreeBasedOnPrecision(elementNode, radius) + return getEvaluatedFloat(sides, elementNode, 'sides') + +def getSplitDictionary(): + 'Get split dictionary.' + global globalSplitDictionaryOperator + splitDictionary = globalSplitDictionaryOperator.copy() + global globalDictionaryOperatorBegin + splitDictionary.update( globalDictionaryOperatorBegin ) + splitDictionary['and'] = EvaluatorAnd + splitDictionary['false'] = EvaluatorFalse + splitDictionary['False'] = EvaluatorFalse + splitDictionary['or'] = EvaluatorOr + splitDictionary['not'] = EvaluatorNot + splitDictionary['true'] = EvaluatorTrue + splitDictionary['True'] = EvaluatorTrue + splitDictionary['none'] = EvaluatorNone + splitDictionary['None'] = EvaluatorNone + return splitDictionary + +def getStartsWithCurlyEqualRoundSquare(word): + 'Determine if the word starts with round or square brackets.' + return word.startswith('{') or word.startswith('=') or word.startswith('(') or word.startswith('[') + +def getTokenByNumber(number): + 'Get token by number.' + return '_%s_' % number + +def getTransformedPathByKey(defaultTransformedPath, elementNode, key): + 'Get transformed path from prefix and xml element.' + if key not in elementNode.attributes: + return defaultTransformedPath + value = elementNode.attributes[key] + if value.__class__ == list: + return value + word = str(value).strip() + evaluatedLinkValue = getEvaluatedLinkValue(elementNode, word) + if evaluatedLinkValue.__class__ == list: + return getPathByList(evaluatedLinkValue) + elementNodeObject = getElementNodeObject(evaluatedLinkValueClass) + if elementNodeObject == None: + return defaultTransformedPath + return elementNodeObject.getTransformedPaths()[0] + +def getTransformedPathByPrefix(elementNode, path, prefix): + 'Get path from prefix and xml element.' + if len(path) < 2: + print('Warning, bug, path is too small in evaluate in setPathByPrefix.') + return + pathByKey = getTransformedPathByKey([], elementNode, getCapitalizedSuffixKey(prefix, 'path')) + if len( pathByKey ) < len(path): + for pointIndex in xrange( len( pathByKey ) ): + path[pointIndex] = pathByKey[pointIndex] + else: + path = pathByKey + path[0] = getVector3ByPrefix(path[0], elementNode, getCapitalizedSuffixKey(prefix, 'pathStart')) + path[-1] = getVector3ByPrefix(path[-1], elementNode, getCapitalizedSuffixKey(prefix, 'pathEnd')) + return path + +def getTransformedPathsByKey(defaultTransformedPaths, elementNode, key): + 'Get transformed paths by key.' + if key not in elementNode.attributes: + return defaultTransformedPaths + value = elementNode.attributes[key] + if value.__class__ == list: + return getPathsByLists(value) + word = str(value).strip() + evaluatedLinkValue = getEvaluatedLinkValue(elementNode, word) + if evaluatedLinkValue.__class__ == dict or evaluatedLinkValue.__class__ == list: + convertToTransformedPaths(evaluatedLinkValue) + return getPathsByLists(evaluatedLinkValue) + elementNodeObject = getElementNodeObject(evaluatedLinkValue) + if elementNodeObject == None: + return defaultTransformedPaths + return elementNodeObject.getTransformedPaths() + +def getUniqueQuoteIndex( uniqueQuoteIndex, word ): + 'Get uniqueQuoteIndex.' + uniqueQuoteIndex += 1 + while getTokenByNumber(uniqueQuoteIndex) in word: + uniqueQuoteIndex += 1 + return uniqueQuoteIndex + +def getUniqueToken(word): + 'Get unique token.' + uniqueString = '@#!' + for character in uniqueString: + if character not in word: + return character + uniqueNumber = 0 + while True: + for character in uniqueString: + uniqueToken = character + str(uniqueNumber) + if uniqueToken not in word: + return uniqueToken + uniqueNumber += 1 + +def getVector3ByDictionary( dictionary, vector3 ): + 'Get vector3 by dictionary.' + if 'x' in dictionary: + vector3 = getVector3IfNone(vector3) + vector3.x = euclidean.getFloatFromValue(dictionary['x']) + if 'y' in dictionary: + vector3 = getVector3IfNone(vector3) + vector3.y = euclidean.getFloatFromValue(dictionary['y']) + if 'z' in dictionary: + vector3 = getVector3IfNone(vector3) + vector3.z = euclidean.getFloatFromValue( dictionary['z'] ) + return vector3 + +def getVector3ByDictionaryListValue(value, vector3): + 'Get vector3 by dictionary, list or value.' + if value.__class__ == Vector3 or value.__class__.__name__ == 'Vector3Index': + return value + if value.__class__ == dict: + return getVector3ByDictionary(value, vector3) + if value.__class__ == list: + return getVector3ByFloatList(value, vector3) + floatFromValue = euclidean.getFloatFromValue(value) + if floatFromValue == None: + return vector3 + vector3.setToXYZ(floatFromValue, floatFromValue, floatFromValue) + return vector3 + +def getVector3ByFloatList(floatList, vector3): + 'Get vector3 by float list.' + if len(floatList) > 0: + vector3 = getVector3IfNone(vector3) + vector3.x = euclidean.getFloatFromValue(floatList[0]) + if len(floatList) > 1: + vector3 = getVector3IfNone(vector3) + vector3.y = euclidean.getFloatFromValue(floatList[1]) + if len(floatList) > 2: + vector3 = getVector3IfNone(vector3) + vector3.z = euclidean.getFloatFromValue(floatList[2]) + return vector3 + +def getVector3ByMultiplierPrefix( elementNode, multiplier, prefix, vector3 ): + 'Get vector3 from multiplier, prefix and xml element.' + if multiplier == 0.0: + return vector3 + oldMultipliedValueVector3 = vector3 * multiplier + vector3ByPrefix = getVector3ByPrefix(oldMultipliedValueVector3.copy(), elementNode, prefix) + if vector3ByPrefix == oldMultipliedValueVector3: + return vector3 + return vector3ByPrefix / multiplier + +def getVector3ByMultiplierPrefixes( elementNode, multiplier, prefixes, vector3 ): + 'Get vector3 from multiplier, prefixes and xml element.' + for prefix in prefixes: + vector3 = getVector3ByMultiplierPrefix( elementNode, multiplier, prefix, vector3 ) + return vector3 + +def getVector3ByPrefix(defaultVector3, elementNode, prefix): + 'Get vector3 from prefix and xml element.' + value = getEvaluatedValue(None, elementNode, prefix) + if value != None: + defaultVector3 = getVector3ByDictionaryListValue(value, defaultVector3) + prefix = archive.getUntilDot(prefix) + x = getEvaluatedFloat(None, elementNode, prefix + '.x') + if x != None: + defaultVector3 = getVector3IfNone(defaultVector3) + defaultVector3.x = x + y = getEvaluatedFloat(None, elementNode, prefix + '.y') + if y != None: + defaultVector3 = getVector3IfNone(defaultVector3) + defaultVector3.y = y + z = getEvaluatedFloat(None, elementNode, prefix + '.z') + if z != None: + defaultVector3 = getVector3IfNone(defaultVector3) + defaultVector3.z = z + return defaultVector3 + +def getVector3ByPrefixes( elementNode, prefixes, vector3 ): + 'Get vector3 from prefixes and xml element.' + for prefix in prefixes: + vector3 = getVector3ByPrefix(vector3, elementNode, prefix) + return vector3 + +def getVector3FromElementNode(elementNode): + 'Get vector3 from xml element.' + vector3 = Vector3( + getEvaluatedFloat(0.0, elementNode, 'x'), + getEvaluatedFloat(0.0, elementNode, 'y'), + getEvaluatedFloat(0.0, elementNode, 'z')) + return getVector3ByPrefix(vector3, elementNode, 'cartesian') + +def getVector3IfNone(vector3): + 'Get new vector3 if the original vector3 is none.' + if vector3 == None: + return Vector3() + return vector3 + +def getVector3ListsRecursively(floatLists): + 'Get vector3 lists recursively.' + if len(floatLists) < 1: + return Vector3() + firstElement = floatLists[0] + if firstElement.__class__ == Vector3: + return floatLists + if firstElement.__class__ != list: + return getVector3ByFloatList(floatLists, Vector3()) + vector3ListsRecursively = [] + for floatList in floatLists: + vector3ListsRecursively.append(getVector3ListsRecursively(floatList)) + return vector3ListsRecursively + +def getVisibleObjects(archivableObjects): + 'Get the visible objects.' + visibleObjects = [] + for archivableObject in archivableObjects: + if archivableObject.getVisible(): + visibleObjects.append(archivableObject) + return visibleObjects + +def processArchivable(archivableClass, elementNode): + 'Get any new elements and process the archivable.' + if elementNode == None: + return + elementNode.xmlObject = archivableClass() + elementNode.xmlObject.setToElementNode(elementNode) + elementNode.getXMLProcessor().processChildNodes(elementNode) + +def processCondition(elementNode): + 'Process the xml element condition.' + xmlProcessor = elementNode.getXMLProcessor() + if elementNode.xmlObject == None: + elementNode.xmlObject = ModuleElementNode(elementNode) + if elementNode.xmlObject.conditionSplitWords == None: + return + if len(xmlProcessor.functions ) < 1: + print('Warning, the (in) element is not in a function in processCondition in evaluate for:') + print(elementNode) + return + if int(getEvaluatedExpressionValueBySplitLine(elementNode, elementNode.xmlObject.conditionSplitWords)) > 0: + xmlProcessor.functions[-1].processChildNodes(elementNode) + else: + elementNode.xmlObject.processElse(elementNode) + +def removeIdentifiersFromDictionary(dictionary): + 'Remove the identifier elements from a dictionary.' + euclidean.removeElementsFromDictionary(dictionary, ['id', 'name', 'tags']) + return dictionary + +def setAttributesByArguments(argumentNames, arguments, elementNode): + 'Set the attribute dictionary to the arguments.' + for argumentIndex, argument in enumerate(arguments): + elementNode.attributes[argumentNames[argumentIndex]] = argument + +def setFunctionLocalDictionary(arguments, function): + 'Evaluate the function statement and delete the evaluators.' + function.localDictionary = {'_arguments' : arguments} + if len(arguments) > 0: + firstArgument = arguments[0] + if firstArgument.__class__ == dict: + function.localDictionary = firstArgument + return + if 'parameters' not in function.elementNode.attributes: + return + parameters = function.elementNode.attributes['parameters'].strip() + if parameters == '': + return + parameterWords = parameters.split(',') + for parameterWordIndex, parameterWord in enumerate(parameterWords): + strippedWord = parameterWord.strip() + keyValue = KeyValue().getByEqual(strippedWord) + if parameterWordIndex < len(arguments): + function.localDictionary[keyValue.key] = arguments[parameterWordIndex] + else: + strippedValue = keyValue.value + if strippedValue == None: + print('Warning there is no default parameter in getParameterValue for:') + print(strippedWord) + print(parameterWords) + print(arguments) + print(function.elementNode.attributes) + else: + strippedValue = strippedValue.strip() + function.localDictionary[keyValue.key.strip()] = strippedValue + if len(arguments) > len(parameterWords): + print('Warning there are too many initializeFunction parameters for:') + print(function.elementNode.attributes) + print(parameterWords) + print(arguments) + +def setLocalAttribute(elementNode): + 'Set the local attribute if any.' + if elementNode.xmlObject != None: + return + for key in elementNode.attributes: + if key[: 1].isalpha(): + value = getEvaluatorSplitWords(getLocalAttributeValueString(key, elementNode.attributes[key].strip())) + elementNode.xmlObject = KeyValue(key, value) + return + elementNode.xmlObject = KeyValue() + + +class BaseFunction: + 'Class to get equation results.' + def __init__(self, elementNode): + 'Initialize.' + self.elementNode = elementNode + self.localDictionary = {} + self.xmlProcessor = elementNode.getXMLProcessor() + + def __repr__(self): + 'Get the string representation of this Class.' + return str(self.__dict__) + + def getReturnValue(self): + 'Get return value.' + self.getReturnValueWithoutDeletion() + del self.xmlProcessor.functions[-1] + return self.returnValue + + def processChildNodes(self, elementNode): + 'Process childNodes if shouldReturn is false.' + for childNode in elementNode.childNodes: + if self.shouldReturn: + return + self.xmlProcessor.processElementNode(childNode) + + +class ClassFunction(BaseFunction): + 'Class to get class results.' + def getReturnValueByArguments(self, *arguments): + 'Get return value by arguments.' + setFunctionLocalDictionary(arguments, self) + return self.getReturnValue() + + def getReturnValueWithoutDeletion(self): + 'Get return value without deleting last function.' + self.returnValue = None + self.shouldReturn = False + self.xmlProcessor.functions.append(self) + self.processChildNodes(self.elementNode) + return self.returnValue + + +class ClassObject: + 'Class to hold class attributes and functions.' + def __init__(self, elementNode): + 'Initialize.' + self.functionDictionary = elementNode.xmlObject.functionDictionary + self.selfDictionary = {} + for variable in elementNode.xmlObject.variables: + self.selfDictionary[variable] = None + + def __repr__(self): + 'Get the string representation of this Class.' + return str(self.__dict__) + + def _getAccessibleAttribute(self, attributeName): + 'Get the accessible attribute.' + if attributeName in self.selfDictionary: + return self.selfDictionary[attributeName] + if attributeName in self.functionDictionary: + function = self.functionDictionary[attributeName] + function.classObject = self + return function.getReturnValueByArguments + return None + + def _setAccessibleAttribute(self, attributeName, value): + 'Set the accessible attribute.' + if attributeName in self.selfDictionary: + self.selfDictionary[attributeName] = value + + +class EmptyObject: + 'An empty object.' + def __init__(self): + 'Do nothing.' + pass + + +class Evaluator: + 'Base evaluator class.' + def __init__(self, elementNode, word): + 'Set value to none.' + self.value = None + self.word = word + + def __repr__(self): + 'Get the string representation of this Class.' + return str(self.__dict__) + + def executeBracket( self, bracketBeginIndex, bracketEndIndex, evaluators ): + 'Execute the bracket.' + pass + + def executeCenterOperation(self, evaluators, evaluatorIndex): + 'Execute operator which acts on the center.' + pass + + def executeDictionary(self, dictionary, evaluators, keys, evaluatorIndex, nextEvaluator): + 'Execute the dictionary.' + del evaluators[evaluatorIndex] + enumeratorKeys = euclidean.getEnumeratorKeys(dictionary, keys) + if enumeratorKeys.__class__ == list: + nextEvaluator.value = [] + for enumeratorKey in enumeratorKeys: + if enumeratorKey in dictionary: + nextEvaluator.value.append(dictionary[enumeratorKey]) + else: + print('Warning, key in executeKey in Evaluator in evaluate is not in for:') + print(enumeratorKey) + print(dictionary) + return + if enumeratorKeys in dictionary: + nextEvaluator.value = dictionary[enumeratorKeys] + else: + print('Warning, key in executeKey in Evaluator in evaluate is not in for:') + print(enumeratorKeys) + print(dictionary) + + def executeFunction(self, evaluators, evaluatorIndex, nextEvaluator): + 'Execute the function.' + pass + + def executeKey(self, evaluators, keys, evaluatorIndex, nextEvaluator): + 'Execute the key index.' + if self.value.__class__ == str: + self.executeString(evaluators, keys, evaluatorIndex, nextEvaluator) + return + if self.value.__class__ == list: + self.executeList(evaluators, keys, evaluatorIndex, nextEvaluator) + return + if self.value.__class__ == dict: + self.executeDictionary(self.value, evaluators, keys, evaluatorIndex, nextEvaluator) + return + getAccessibleDictionaryFunction = getattr(self.value, '_getAccessibleDictionary', None) + if getAccessibleDictionaryFunction != None: + self.executeDictionary(getAccessibleDictionaryFunction(), evaluators, keys, evaluatorIndex, nextEvaluator) + return + if self.value.__class__.__name__ != 'ElementNode': + return + del evaluators[evaluatorIndex] + enumeratorKeys = euclidean.getEnumeratorKeys(self.value.attributes, keys) + if enumeratorKeys.__class__ == list: + nextEvaluator.value = [] + for enumeratorKey in enumeratorKeys: + if enumeratorKey in self.value.attributes: + nextEvaluator.value.append(getEvaluatedExpressionValue(self.value, self.value.attributes[enumeratorKey])) + else: + print('Warning, key in executeKey in Evaluator in evaluate is not in for:') + print(enumeratorKey) + print(self.value.attributes) + return + if enumeratorKeys in self.value.attributes: + nextEvaluator.value = getEvaluatedExpressionValue(self.value, self.value.attributes[enumeratorKeys]) + else: + print('Warning, key in executeKey in Evaluator in evaluate is not in for:') + print(enumeratorKeys) + print(self.value.attributes) + + def executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel): + 'Execute operator which acts from the left.' + pass + + def executeList(self, evaluators, keys, evaluatorIndex, nextEvaluator): + 'Execute the key index.' + del evaluators[evaluatorIndex] + enumeratorKeys = euclidean.getEnumeratorKeys(self.value, keys) + if enumeratorKeys.__class__ == list: + nextEvaluator.value = [] + for enumeratorKey in enumeratorKeys: + intKey = euclidean.getIntFromValue(enumeratorKey) + if self.getIsInRange(intKey): + nextEvaluator.value.append(self.value[intKey]) + else: + print('Warning, key in executeList in Evaluator in evaluate is not in for:') + print(enumeratorKey) + print(self.value) + return + intKey = euclidean.getIntFromValue(enumeratorKeys) + if self.getIsInRange(intKey): + nextEvaluator.value = self.value[intKey] + else: + print('Warning, key in executeList in Evaluator in evaluate is not in for:') + print(enumeratorKeys) + print(self.value) + + def executePairOperation(self, evaluators, evaluatorIndex, operationLevel): + 'Operate on two evaluators.' + pass + + def executeRightOperation( self, evaluators, evaluatorIndex ): + 'Execute operator which acts from the right.' + pass + + def executeString(self, evaluators, keys, evaluatorIndex, nextEvaluator): + 'Execute the string.' + del evaluators[evaluatorIndex] + enumeratorKeys = euclidean.getEnumeratorKeys(self.value, keys) + if enumeratorKeys.__class__ == list: + nextEvaluator.value = '' + for enumeratorKey in enumeratorKeys: + intKey = euclidean.getIntFromValue(enumeratorKey) + if self.getIsInRange(intKey): + nextEvaluator.value += self.value[intKey] + else: + print('Warning, key in executeString in Evaluator in evaluate is not in for:') + print(enumeratorKey) + print(self.value) + return + intKey = euclidean.getIntFromValue(enumeratorKeys) + if self.getIsInRange(intKey): + nextEvaluator.value = self.value[intKey] + else: + print('Warning, key in executeString in Evaluator in evaluate is not in for:') + print(enumeratorKeys) + print(self.value) + + def getIsInRange(self, keyIndex): + 'Determine if the keyIndex is in range.' + if keyIndex == None: + return False + return keyIndex >= -len(self.value) and keyIndex < len(self.value) + + +class EvaluatorAddition(Evaluator): + 'Class to add two evaluators.' + def executePair( self, evaluators, evaluatorIndex ): + 'Add two evaluators.' + leftIndex = evaluatorIndex - 1 + rightIndex = evaluatorIndex + 1 + if leftIndex < 0: + print('Warning, no leftKey in executePair in EvaluatorAddition for:') + print(evaluators) + print(evaluatorIndex) + print(self) + del evaluators[evaluatorIndex] + return + if rightIndex >= len(evaluators): + print('Warning, no rightKey in executePair in EvaluatorAddition for:') + print(evaluators) + print(evaluatorIndex) + print(self) + del evaluators[evaluatorIndex] + return + rightValue = evaluators[rightIndex].value + evaluators[leftIndex].value = self.getOperationValue(evaluators[leftIndex].value, evaluators[rightIndex].value) + del evaluators[ evaluatorIndex : evaluatorIndex + 2 ] + + def executePairOperation(self, evaluators, evaluatorIndex, operationLevel): + 'Operate on two evaluators.' + if operationLevel == 20: + self.executePair(evaluators, evaluatorIndex) + + def getEvaluatedValues(self, enumerable, keys, value): + 'Get evaluatedValues.' + if enumerable.__class__ == dict: + evaluatedValues = {} + for key in keys: + evaluatedValues[key] = self.getOperationValue(value, enumerable[key]) + return evaluatedValues + evaluatedValues = [] + for key in keys: + evaluatedValues.append(self.getOperationValue(value, enumerable[key])) + return evaluatedValues + + def getOperationValue(self, leftValue, rightValue): + 'Get operation value.' + leftKeys = getKeys(leftValue) + rightKeys = getKeys(rightValue) + if leftKeys == None and rightKeys == None: + return self.getValueFromValuePair(leftValue, rightValue) + if leftKeys == None: + return self.getEvaluatedValues(rightValue, rightKeys, leftValue) + if rightKeys == None: + return self.getEvaluatedValues(leftValue, leftKeys, rightValue) + leftKeys.sort(reverse=True) + rightKeys.sort(reverse=True) + if leftKeys != rightKeys: + print('Warning, the leftKeys are different from the rightKeys in getOperationValue in EvaluatorAddition for:') + print('leftValue') + print(leftValue) + print(leftKeys) + print('rightValue') + print(rightValue) + print(rightKeys) + print(self) + return None + if leftValue.__class__ == dict or rightValue.__class__ == dict: + evaluatedValues = {} + for leftKey in leftKeys: + evaluatedValues[leftKey] = self.getOperationValue(leftValue[leftKey], rightValue[leftKey]) + return evaluatedValues + evaluatedValues = [] + for leftKey in leftKeys: + evaluatedValues.append(self.getOperationValue(leftValue[leftKey], rightValue[leftKey])) + return evaluatedValues + + def getValueFromValuePair(self, leftValue, rightValue): + 'Add two values.' + return leftValue + rightValue + + +class EvaluatorEqual(EvaluatorAddition): + 'Class to compare two evaluators.' + def executePairOperation(self, evaluators, evaluatorIndex, operationLevel): + 'Operate on two evaluators.' + if operationLevel == 15: + self.executePair(evaluators, evaluatorIndex) + + def getBooleanFromValuePair(self, leftValue, rightValue): + 'Compare two values.' + return leftValue == rightValue + + def getValueFromValuePair(self, leftValue, rightValue): + 'Get value from comparison.' + return self.getBooleanFromValuePair(leftValue, rightValue) + + +class EvaluatorSubtraction(EvaluatorAddition): + 'Class to subtract two evaluators.' + def executeLeft( self, evaluators, evaluatorIndex ): + 'Minus the value to the right.' + leftIndex = evaluatorIndex - 1 + rightIndex = evaluatorIndex + 1 + leftValue = None + if leftIndex >= 0: + leftValue = evaluators[leftIndex].value + if leftValue != None: + return + rightValue = evaluators[rightIndex].value + if rightValue == None: + print('Warning, can not minus.') + print(evaluators[rightIndex].word) + else: + evaluators[rightIndex].value = self.getNegativeValue(rightValue) + del evaluators[evaluatorIndex] + + def executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel): + 'Minus the value to the right.' + if operationLevel == 200: + self.executeLeft(evaluators, evaluatorIndex) + + def getNegativeValue( self, value ): + 'Get the negative value.' + keys = getKeys(value) + if keys == None: + return self.getValueFromSingleValue(value) + for key in keys: + value[key] = self.getNegativeValue(value[key]) + return value + + def getValueFromSingleValue( self, value ): + 'Minus value.' + return -value + + def getValueFromValuePair(self, leftValue, rightValue): + 'Subtract two values.' + return leftValue - rightValue + + +class EvaluatorAnd(EvaluatorAddition): + 'Class to compare two evaluators.' + def executePairOperation(self, evaluators, evaluatorIndex, operationLevel): + 'Operate on two evaluators.' + if operationLevel == 12: + self.executePair(evaluators, evaluatorIndex) + + def getBooleanFromValuePair(self, leftValue, rightValue): + 'And two values.' + return leftValue and rightValue + + def getValueFromValuePair(self, leftValue, rightValue): + 'Get value from comparison.' + return self.getBooleanFromValuePair(leftValue, rightValue) + + +class EvaluatorAttribute(Evaluator): + 'Class to handle an attribute.' + def executeFunction(self, evaluators, evaluatorIndex, nextEvaluator): + 'Execute the function.' + executeNextEvaluatorArguments(self, evaluators, evaluatorIndex, nextEvaluator) + + def executeRightOperation( self, evaluators, evaluatorIndex ): + 'Execute operator which acts from the right.' + attributeName = self.word[1 :] + previousIndex = evaluatorIndex - 1 + previousEvaluator = evaluators[previousIndex] + if previousEvaluator.value.__class__ == dict: + from fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables import dictionary_attribute + self.value = dictionary_attribute._getAccessibleAttribute(attributeName, previousEvaluator.value) + elif previousEvaluator.value.__class__ == list: + from fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables import list_attribute + self.value = list_attribute._getAccessibleAttribute(attributeName, previousEvaluator.value) + elif previousEvaluator.value.__class__ == str: + from fabmetheus_utilities.geometry.geometry_utilities.evaluate_enumerables import string_attribute + self.value = string_attribute._getAccessibleAttribute(attributeName, previousEvaluator.value) + else: + attributeKeywords = attributeName.split('.') + self.value = previousEvaluator.value + for attributeKeyword in attributeKeywords: + self.value = getattr(self.value, '_getAccessibleAttribute', None)(attributeKeyword) + if self.value == None: + print('Warning, EvaluatorAttribute in evaluate can not get a getAccessibleAttributeFunction for:') + print(attributeName) + print(previousEvaluator.value) + print(self) + return + del evaluators[previousIndex] + + +class EvaluatorBracketCurly(Evaluator): + 'Class to evaluate a string.' + def executeBracket(self, bracketBeginIndex, bracketEndIndex, evaluators): + 'Execute the bracket.' + for evaluatorIndex in xrange(bracketEndIndex - 3, bracketBeginIndex, - 1): + bracketEndIndex = getEndIndexConvertEquationValue(bracketEndIndex, evaluatorIndex, evaluators) + evaluatedExpressionValueEvaluators = getBracketEvaluators(bracketBeginIndex, bracketEndIndex, evaluators) + self.value = {} + for evaluatedExpressionValueEvaluator in evaluatedExpressionValueEvaluators: + keyValue = evaluatedExpressionValueEvaluator.value + self.value[keyValue.key] = keyValue.value + del evaluators[bracketBeginIndex + 1: bracketEndIndex + 1] + + +class EvaluatorBracketRound(Evaluator): + 'Class to evaluate a string.' + def __init__(self, elementNode, word): + 'Set value to none.' + self.arguments = [] + self.value = None + self.word = word + + def executeBracket( self, bracketBeginIndex, bracketEndIndex, evaluators ): + 'Execute the bracket.' + self.arguments = getBracketValuesDeleteEvaluator(bracketBeginIndex, bracketEndIndex, evaluators) + if len( self.arguments ) < 1: + return + if len( self.arguments ) > 1: + self.value = self.arguments + else: + self.value = self.arguments[0] + + def executeRightOperation( self, evaluators, evaluatorIndex ): + 'Evaluate the statement and delete the evaluators.' + previousIndex = evaluatorIndex - 1 + if previousIndex < 0: + return + evaluators[ previousIndex ].executeFunction( evaluators, previousIndex, self ) + + +class EvaluatorBracketSquare(Evaluator): + 'Class to evaluate a string.' + def executeBracket( self, bracketBeginIndex, bracketEndIndex, evaluators ): + 'Execute the bracket.' + self.value = getBracketValuesDeleteEvaluator(bracketBeginIndex, bracketEndIndex, evaluators) + + def executeRightOperation( self, evaluators, evaluatorIndex ): + 'Evaluate the statement and delete the evaluators.' + previousIndex = evaluatorIndex - 1 + if previousIndex < 0: + return + if self.value.__class__ != list: + return + evaluators[ previousIndex ].executeKey( evaluators, self.value, previousIndex, self ) + + +class EvaluatorClass(Evaluator): + 'Class evaluator class.' + def __init__(self, elementNode, word): + 'Set value to none.' + self.elementNode = elementNode + self.value = None + self.word = word + + def executeFunction(self, evaluators, evaluatorIndex, nextEvaluator): + 'Execute the function.' + if self.elementNode.xmlObject == None: + self.elementNode.xmlObject = FunctionVariable(self.elementNode) + nextEvaluator.value = ClassObject(self.elementNode) + initializeFunction = None + if '_init' in self.elementNode.xmlObject.functionDictionary: + function = self.elementNode.xmlObject.functionDictionary['_init'] + function.classObject = nextEvaluator.value + setFunctionLocalDictionary(nextEvaluator.arguments, function) + function.getReturnValue() + del evaluators[evaluatorIndex] + + +class EvaluatorComma(Evaluator): + 'Class to join two evaluators.' + def executePairOperation(self, evaluators, evaluatorIndex, operationLevel): + 'Operate on two evaluators.' + if operationLevel != 0: + return + previousIndex = evaluatorIndex - 1 + if previousIndex < 0: + evaluators[evaluatorIndex].value = None + return + if evaluators[previousIndex].word == ',': + evaluators[evaluatorIndex].value = None + return + del evaluators[evaluatorIndex] + + +class EvaluatorConcatenate(Evaluator): + 'Class to join two evaluators.' + def executePairOperation(self, evaluators, evaluatorIndex, operationLevel): + 'Operate on two evaluators.' + if operationLevel != 80: + return + leftIndex = evaluatorIndex - 1 + if leftIndex < 0: + del evaluators[evaluatorIndex] + return + rightIndex = evaluatorIndex + 1 + if rightIndex >= len(evaluators): + del evaluators[ leftIndex : rightIndex ] + return + leftValue = evaluators[leftIndex].value + rightValue = evaluators[rightIndex].value + if leftValue.__class__ == rightValue.__class__ and (leftValue.__class__ == list or rightValue.__class__ == str): + evaluators[leftIndex].value = leftValue + rightValue + del evaluators[ evaluatorIndex : evaluatorIndex + 2 ] + return + if leftValue.__class__ == list and rightValue.__class__ == int: + if rightValue > 0: + originalList = leftValue[:] + for copyIndex in xrange( rightValue - 1 ): + leftValue += originalList + evaluators[leftIndex].value = leftValue + del evaluators[ evaluatorIndex : evaluatorIndex + 2 ] + return + if leftValue.__class__ == dict and rightValue.__class__ == dict: + leftValue.update(rightValue) + evaluators[leftIndex].value = leftValue + del evaluators[ evaluatorIndex : evaluatorIndex + 2 ] + return + del evaluators[ leftIndex : evaluatorIndex + 2 ] + + +class EvaluatorDictionary(Evaluator): + 'Class to join two evaluators.' + def executePairOperation(self, evaluators, evaluatorIndex, operationLevel): + 'Operate on two evaluators.' + if operationLevel != 10: + return + leftEvaluatorIndex = evaluatorIndex - 1 + if leftEvaluatorIndex < 0: + print('Warning, leftEvaluatorIndex is less than zero in EvaluatorDictionary for:') + print(self) + print(evaluators) + return + rightEvaluatorIndex = evaluatorIndex + 1 + if rightEvaluatorIndex >= len(evaluators): + print('Warning, rightEvaluatorIndex too high in EvaluatorDictionary for:') + print(rightEvaluatorIndex) + print(self) + print(evaluators) + return + evaluators[rightEvaluatorIndex].value = KeyValue(evaluators[leftEvaluatorIndex].value, evaluators[rightEvaluatorIndex].value) + del evaluators[ leftEvaluatorIndex : rightEvaluatorIndex ] + + +class EvaluatorDivision(EvaluatorAddition): + 'Class to divide two evaluators.' + def executePairOperation(self, evaluators, evaluatorIndex, operationLevel): + 'Operate on two evaluators.' + if operationLevel == 40: + self.executePair(evaluators, evaluatorIndex) + + def getValueFromValuePair(self, leftValue, rightValue): + 'Divide two values.' + return leftValue / rightValue + + +class EvaluatorElement(Evaluator): + 'Element evaluator class.' + def __init__(self, elementNode, word): + 'Set value to none.' + self.elementNode = elementNode + self.value = None + self.word = word + + def executeCenterOperation(self, evaluators, evaluatorIndex): + 'Execute operator which acts on the center.' + dotIndex = self.word.find('.') + if dotIndex < 0: + print('Warning, EvaluatorElement in evaluate can not find the dot for:') + print(functionName) + print(self) + return + attributeName = self.word[dotIndex + 1 :] + moduleName = self.word[: dotIndex] + if moduleName in globalModuleFunctionsDictionary: + self.value = globalModuleFunctionsDictionary[moduleName](attributeName, self.elementNode) + return + pluginModule = None + if moduleName in globalElementNameSet: + pluginModule = archive.getModuleWithPath(archive.getElementsPath(moduleName)) + if pluginModule == None: + print('Warning, EvaluatorElement in evaluate can not get a pluginModule for:') + print(moduleName) + print(self) + return + getAccessibleAttributeFunction = pluginModule._getAccessibleAttribute + globalModuleFunctionsDictionary[moduleName] = getAccessibleAttributeFunction + self.value = getAccessibleAttributeFunction(attributeName, self.elementNode) + + def executeFunction(self, evaluators, evaluatorIndex, nextEvaluator): + 'Execute the function.' + executeNextEvaluatorArguments(self, evaluators, evaluatorIndex, nextEvaluator) + + +class EvaluatorFalse(Evaluator): + 'Class to evaluate a string.' + def __init__(self, elementNode, word): + 'Set value to zero.' + self.value = False + self.word = word + + +class EvaluatorFunction(Evaluator): + 'Function evaluator class.' + def __init__(self, elementNode, word): + 'Set value to none.' + self.elementNode = elementNode + self.value = None + self.word = word + + def executeFunction(self, evaluators, evaluatorIndex, nextEvaluator): + 'Execute the function.' + if self.elementNode.xmlObject == None: + if 'return' in self.elementNode.attributes: + value = self.elementNode.attributes['return'] + self.elementNode.xmlObject = getEvaluatorSplitWords(value) + else: + self.elementNode.xmlObject = [] + self.function = Function(self.elementNode ) + setFunctionLocalDictionary(nextEvaluator.arguments, self.function) + nextEvaluator.value = self.function.getReturnValue() + del evaluators[evaluatorIndex] + + +class EvaluatorFundamental(Evaluator): + 'Fundamental evaluator class.' + def executeCenterOperation(self, evaluators, evaluatorIndex): + 'Execute operator which acts on the center.' + dotIndex = self.word.find('.') + if dotIndex < 0: + print('Warning, EvaluatorFundamental in evaluate can not find the dot for:') + print(functionName) + print(self) + return + attributeName = self.word[dotIndex + 1 :] + moduleName = self.word[: dotIndex] + if moduleName in globalModuleFunctionsDictionary: + self.value = globalModuleFunctionsDictionary[moduleName](attributeName) + return + pluginModule = None + if moduleName in globalFundamentalNameSet: + pluginModule = archive.getModuleWithPath(archive.getFundamentalsPath(moduleName)) + else: + underscoredName = '_' + moduleName + if underscoredName in globalFundamentalNameSet: + pluginModule = archive.getModuleWithPath(archive.getFundamentalsPath(underscoredName)) + if pluginModule == None: + print('Warning, EvaluatorFundamental in evaluate can not get a pluginModule for:') + print(moduleName) + print(self) + return + getAccessibleAttributeFunction = pluginModule._getAccessibleAttribute + globalModuleFunctionsDictionary[moduleName] = getAccessibleAttributeFunction + self.value = getAccessibleAttributeFunction(attributeName) + + def executeFunction(self, evaluators, evaluatorIndex, nextEvaluator): + 'Execute the function.' + executeNextEvaluatorArguments(self, evaluators, evaluatorIndex, nextEvaluator) + + +class EvaluatorGreaterEqual( EvaluatorEqual ): + 'Class to compare two evaluators.' + def getBooleanFromValuePair(self, leftValue, rightValue): + 'Compare two values.' + return leftValue >= rightValue + + +class EvaluatorGreater( EvaluatorEqual ): + 'Class to compare two evaluators.' + def getBooleanFromValuePair(self, leftValue, rightValue): + 'Compare two values.' + return leftValue > rightValue + + +class EvaluatorLessEqual( EvaluatorEqual ): + 'Class to compare two evaluators.' + def getBooleanFromValuePair(self, leftValue, rightValue): + 'Compare two values.' + return leftValue <= rightValue + + +class EvaluatorLess( EvaluatorEqual ): + 'Class to compare two evaluators.' + def getBooleanFromValuePair(self, leftValue, rightValue): + 'Compare two values.' + return leftValue < rightValue + + +class EvaluatorLocal(EvaluatorElement): + 'Class to get a local variable.' + def executeCenterOperation(self, evaluators, evaluatorIndex): + 'Execute operator which acts on the center.' + functions = self.elementNode.getXMLProcessor().functions + if len(functions) < 1: + print('Warning, there are no functions in EvaluatorLocal in evaluate for:') + print(self.word) + return + attributeKeywords = self.word.split('.') + self.value = functions[-1].localDictionary[attributeKeywords[0]] + for attributeKeyword in attributeKeywords[1 :]: + self.value = self.value._getAccessibleAttribute(attributeKeyword) + + +class EvaluatorModulo( EvaluatorDivision ): + 'Class to modulo two evaluators.' + def getValueFromValuePair(self, leftValue, rightValue): + 'Modulo two values.' + return leftValue % rightValue + + +class EvaluatorMultiplication( EvaluatorDivision ): + 'Class to multiply two evaluators.' + def getValueFromValuePair(self, leftValue, rightValue): + 'Multiply two values.' + return leftValue * rightValue + + +class EvaluatorNone(Evaluator): + 'Class to evaluate None.' + def __init__(self, elementNode, word): + 'Set value to none.' + self.value = None + self.word = str(word) + + +class EvaluatorNot(EvaluatorSubtraction): + 'Class to compare two evaluators.' + def executeLeftOperation(self, evaluators, evaluatorIndex, operationLevel): + 'Minus the value to the right.' + if operationLevel == 13: + self.executeLeft(evaluators, evaluatorIndex) + + def getValueFromSingleValue( self, value ): + 'Minus value.' + return not value + + +class EvaluatorNotEqual( EvaluatorEqual ): + 'Class to compare two evaluators.' + def getBooleanFromValuePair(self, leftValue, rightValue): + 'Compare two values.' + return leftValue != rightValue + + +class EvaluatorNumeric(Evaluator): + 'Class to evaluate a string.' + def __init__(self, elementNode, word): + 'Set value.' + self.value = None + self.word = word + try: + if '.' in word: + self.value = float(word) + else: + self.value = int(word) + except: + print('Warning, EvaluatorNumeric in evaluate could not get a numeric value for:') + print(word) + print(elementNode) + + +class EvaluatorOr( EvaluatorAnd ): + 'Class to compare two evaluators.' + def getBooleanFromValuePair(self, leftValue, rightValue): + 'Or two values.' + return leftValue or rightValue + + +class EvaluatorPower(EvaluatorAddition): + 'Class to power two evaluators.' + def executePairOperation(self, evaluators, evaluatorIndex, operationLevel): + 'Operate on two evaluators.' + if operationLevel == 60: + self.executePair(evaluators, evaluatorIndex) + + def getValueFromValuePair(self, leftValue, rightValue): + 'Power of two values.' + return leftValue ** rightValue + + +class EvaluatorSelf(EvaluatorElement): + 'Class to handle self.' + def executeCenterOperation(self, evaluators, evaluatorIndex): + 'Execute operator which acts on the center.' + functions = self.elementNode.getXMLProcessor().functions + if len(functions) < 1: + print('Warning, there are no functions in executeCenterOperation in EvaluatorSelf in evaluate for:') + print(self.elementNode) + return + function = functions[-1] + attributeKeywords = self.word.split('.') + self.value = function.classObject + for attributeKeyword in attributeKeywords[1 :]: + self.value = self.value._getAccessibleAttribute(attributeKeyword) + + +class EvaluatorTrue(Evaluator): + 'Class to evaluate a string.' + def __init__(self, elementNode, word): + 'Set value to true.' + self.value = True + self.word = word + + +class EvaluatorValue(Evaluator): + 'Class to evaluate a string.' + def __init__(self, word): + 'Set value to none.' + self.value = word + self.word = str(word) + + +class Function(BaseFunction): + 'Class to get equation results.' + def __init__(self, elementNode): + 'Initialize.' + self.elementNode = elementNode + self.evaluatorSplitLine = elementNode.xmlObject + self.localDictionary = {} + self.xmlProcessor = elementNode.getXMLProcessor() + + def getReturnValueWithoutDeletion(self): + 'Get return value without deleting last function.' + self.returnValue = None + self.xmlProcessor.functions.append(self) + if len(self.evaluatorSplitLine) < 1: + self.shouldReturn = False + self.processChildNodes(self.elementNode) + else: + self.returnValue = getEvaluatedExpressionValueBySplitLine(self.elementNode, self.evaluatorSplitLine) + return self.returnValue + + +class FunctionVariable: + 'Class to hold class functions and variable set.' + def __init__(self, elementNode): + 'Initialize.' + self.functionDictionary = {} + self.variables = [] + self.processClass(elementNode) + + def addToVariableSet(self, elementNode): + 'Add to variables.' + setLocalAttribute(elementNode) + keySplitLine = elementNode.xmlObject.key.split('.') + if len(keySplitLine) == 2: + if keySplitLine[0] == 'self': + variable = keySplitLine[1] + if variable not in self.variables: + self.variables.append(variable) + + def processClass(self, elementNode): + 'Add class to FunctionVariable.' + for childNode in elementNode.childNodes: + self.processFunction(childNode) + if 'parentNode' in elementNode.attributes: + self.processClass(elementNode.getElementNodeByID(elementNode.attributes['parentNode'])) + + def processFunction(self, elementNode): + 'Add function to function dictionary.' + if elementNode.getNodeName() != 'function': + return + idKey = elementNode.attributes['id'] + if idKey in self.functionDictionary: + return + self.functionDictionary[idKey] = ClassFunction(elementNode) + for childNode in elementNode.childNodes: + self.processStatement(childNode) + + def processStatement(self, elementNode): + 'Add self statement to variables.' + if elementNode.getNodeName() == 'statement': + self.addToVariableSet(elementNode) + for childNode in elementNode.childNodes: + self.processStatement(childNode) + + +class KeyValue: + 'Class to hold a key value.' + def __init__(self, key=None, value=None): + 'Get key value.' + self.key = key + self.value = value + + def __repr__(self): + 'Get the string representation of this KeyValue.' + return str(self.__dict__) + + def getByCharacter( self, character, line ): + 'Get by character.' + dotIndex = line.find( character ) + if dotIndex < 0: + self.key = line + self.value = None + return self + self.key = line[: dotIndex] + self.value = line[dotIndex + 1 :] + return self + + def getByDot(self, line): + 'Get by dot.' + return self.getByCharacter('.', line ) + + def getByEqual(self, line): + 'Get by dot.' + return self.getByCharacter('=', line ) + + +class ModuleElementNode: + 'Class to get the in attribute, the index name and the value name.' + def __init__( self, elementNode): + 'Initialize.' + self.conditionSplitWords = None + self.elseElement = None + if 'condition' in elementNode.attributes: + self.conditionSplitWords = getEvaluatorSplitWords( elementNode.attributes['condition'] ) + else: + print('Warning, could not find the condition attribute in ModuleElementNode in evaluate for:') + print(elementNode) + return + if len( self.conditionSplitWords ) < 1: + self.conditionSplitWords = None + print('Warning, could not get split words for the condition attribute in ModuleElementNode in evaluate for:') + print(elementNode) + nextIndex = getNextChildIndex(elementNode) + if nextIndex >= len( elementNode.parentNode.childNodes ): + return + nextElementNode = elementNode.parentNode.childNodes[ nextIndex ] + lowerLocalName = nextElementNode.getNodeName().lower() + if lowerLocalName != 'else' and lowerLocalName != 'elif': + return + xmlProcessor = elementNode.getXMLProcessor() + if lowerLocalName not in xmlProcessor.namePathDictionary: + return + self.pluginModule = archive.getModuleWithPath( xmlProcessor.namePathDictionary[ lowerLocalName ] ) + if self.pluginModule == None: + return + self.elseElement = nextElementNode + + def processElse(self, elementNode): + 'Process the else statement.' + if self.elseElement != None: + self.pluginModule.processElse( self.elseElement) + + +globalCreationDictionary = archive.getGeometryDictionary('creation') +globalDictionaryOperatorBegin = { + '||' : EvaluatorConcatenate, + '==' : EvaluatorEqual, + '>=' : EvaluatorGreaterEqual, + '<=' : EvaluatorLessEqual, + '!=' : EvaluatorNotEqual, + '**' : EvaluatorPower } +globalModuleEvaluatorDictionary = {} +globalFundamentalNameSet = set(archive.getPluginFileNamesFromDirectoryPath(archive.getFundamentalsPath())) +addPrefixDictionary(globalModuleEvaluatorDictionary, globalFundamentalNameSet, EvaluatorFundamental) +globalElementNameSet = set(archive.getPluginFileNamesFromDirectoryPath(archive.getElementsPath())) +addPrefixDictionary(globalModuleEvaluatorDictionary, globalElementNameSet, EvaluatorElement) +globalModuleEvaluatorDictionary['self'] = EvaluatorSelf +globalSplitDictionaryOperator = { + '+' : EvaluatorAddition, + '{' : EvaluatorBracketCurly, + '}' : Evaluator, + '(' : EvaluatorBracketRound, + ')' : Evaluator, + '[' : EvaluatorBracketSquare, + ']' : Evaluator, + ',' : EvaluatorComma, + ':' : EvaluatorDictionary, + '/' : EvaluatorDivision, + '>' : EvaluatorGreater, + '<' : EvaluatorLess, + '%' : EvaluatorModulo, + '*' : EvaluatorMultiplication, + '-' : EvaluatorSubtraction } +globalSplitDictionary = getSplitDictionary() # must be after globalSplitDictionaryOperator diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_elements/__init__.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_elements/__init__.py new file mode 100644 index 0000000..58ec332 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_elements/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 4 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_elements/creation.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_elements/creation.py new file mode 100644 index 0000000..86f79af --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_elements/creation.py @@ -0,0 +1,60 @@ +""" +Boolean geometry utilities. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + + +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities import archive +from fabmetheus_utilities import gcodec + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def _getAccessibleAttribute(attributeName, elementNode): + 'Get the accessible attribute.' + functionName = attributeName[len('get') :].lower() + if functionName not in evaluate.globalCreationDictionary: + print('Warning, functionName not in globalCreationDictionary in _getAccessibleAttribute in creation for:') + print(functionName) + print(elementNode) + return None + pluginModule = archive.getModuleWithPath(evaluate.globalCreationDictionary[functionName]) + if pluginModule == None: + print('Warning, _getAccessibleAttribute in creation can not get a pluginModule for:') + print(functionName) + print(elementNode) + return None + return Creation(elementNode, pluginModule).getCreation + + +class Creation: + 'Class to handle a creation.' + def __init__(self, elementNode, pluginModule): + 'Initialize.' + self.elementNode = elementNode + self.pluginModule = pluginModule + + def __repr__(self): + "Get the string representation of this creation." + return self.elementNode + + def getCreation(self, *arguments): + "Get creation." + dictionary = {'_fromCreationEvaluator': 'true'} + firstArgument = None + if len(arguments) > 0: + firstArgument = arguments[0] + if firstArgument.__class__ == dict: + dictionary.update(firstArgument) + return self.pluginModule.getGeometryOutput(None, self.elementNode.getCopyShallow(dictionary)) + copyShallow = self.elementNode.getCopyShallow(dictionary) + return self.pluginModule.getGeometryOutputByArguments(arguments, copyShallow) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_elements/document.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_elements/document.py new file mode 100644 index 0000000..f956591 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_elements/document.py @@ -0,0 +1,97 @@ +""" +Boolean geometry utilities. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def _getAccessibleAttribute(attributeName, elementNode): + 'Get the accessible attribute.' + if attributeName in globalGetAccessibleAttributeSet: + return getattr(Document(elementNode), attributeName, None) + return None + + +class Document: + 'Class to handle elementNodes in a document.' + def __init__(self, elementNode): + 'Initialize.' + self.elementNode = elementNode + + def __repr__(self): + 'Get the string representation of this Document.' + return self.elementNode + + def getCascadeBoolean(self, defaultBoolean, key): + 'Get cascade boolean.' + return self.elementNode.getCascadeBoolean(defaultBoolean, key) + + def getCascadeFloat(self, defaultFloat, key): + 'Get cascade float.' + return self.elementNode.getCascadeFloat(defaultFloat, key) + + def getDocumentElement(self): + 'Get document element element.' + return self.elementNode.getDocumentElement() + + def getElementByID(self, idKey): + 'Get element by id.' + elementByID = self.elementNode.getElementNodeByID(idKey) + if elementByID == None: + print('Warning, could not get elementByID in getElementByID in document for:') + print(idKey) + print(self.elementNode) + return elementByID + + def getElementsByName(self, nameKey): + 'Get element by name.' + elementsByName = self.elementNode.getElementNodesByName(nameKey) + if elementsByName == None: + print('Warning, could not get elementsByName in getElementsByName in document for:') + print(nameKey) + print(self.elementNode) + return elementsByName + + def getElementsByTag(self, tagKey): + 'Get element by tag.' + elementsByTag = self.elementNode.getElementNodesByTag(tagKey) + if elementsByTag == None: + print('Warning, could not get elementsByTag in getElementsByTag in document for:') + print(tagKey) + print(self.elementNode) + return elementsByTag + + def getParentNode(self): + 'Get parentNode element.' + return self.elementNode.parentNode + + def getPrevious(self): + 'Get previous element.' + return self.getPreviousElement() + + def getPreviousElement(self): + 'Get previous element.' + return self.elementNode.getPreviousElementNode() + + def getPreviousVertex(self): + 'Get previous element.' + return self.elementNode.getPreviousVertex() + + def getSelfElement(self): + 'Get self element.' + return self.elementNode + + +globalAccessibleAttributeDictionary = 'getCascadeBoolean getCascadeFloat getDocumentElement getElementByID getElementsByName'.split() +globalAccessibleAttributeDictionary += 'getElementsByTag getParentNode getPrevious getPreviousElement getPreviousVertex'.split() +globalAccessibleAttributeDictionary += 'getSelfElement'.split() +globalGetAccessibleAttributeSet = set(globalAccessibleAttributeDictionary) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_elements/setting.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_elements/setting.py new file mode 100644 index 0000000..5c16679 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_elements/setting.py @@ -0,0 +1,180 @@ +""" +Boolean geometry utilities. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from skeinforge_application.skeinforge_utilities import skeinforge_craft +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def _getAccessibleAttribute(attributeName, elementNode): + 'Get the accessible attribute.' + if attributeName in globalGetAccessibleAttributeSet: + return getattr(Setting(elementNode), attributeName, None) + return None + +def getCascadeFloatWithoutSelf(defaultFloat, elementNode, key): + 'Get the cascade float.' + if key in elementNode.attributes: + value = elementNode.attributes[key] + functionName = 'get' + key[0].upper() + key[1 :] + if functionName in value: + if elementNode.parentNode == None: + return defaultFloat + else: + elementNode = elementNode.parentNode + return elementNode.getCascadeFloat(defaultFloat, key) + +def getEdgeWidth(elementNode): + 'Get the edge width.' + if elementNode == None: + return 0.72 + preferences = skeinforge_craft.getCraftPreferences('carve') + layerHeight = skeinforge_craft.getCraftValue('Layer Height', preferences) + layerHeight = getCascadeFloatWithoutSelf(layerHeight, elementNode, 'layerHeight') + edgeWidthOverHeight = skeinforge_craft.getCraftValue('Edge Width over Height', preferences) + edgeWidthOverHeight = getCascadeFloatWithoutSelf(edgeWidthOverHeight, elementNode, 'edgeWidthOverHeight') + return getCascadeFloatWithoutSelf(edgeWidthOverHeight * layerHeight, elementNode, 'edgeWidth') + +def getImportCoarseness(elementNode, preferences=None): + 'Get the importCoarseness.' + if elementNode == None: + return 1.0 + if preferences == None: + preferences = skeinforge_craft.getCraftPreferences('carve') + importCoarseness = skeinforge_craft.getCraftValue('Import Coarseness', preferences) + return getCascadeFloatWithoutSelf(importCoarseness, elementNode, 'importCoarseness') + +def getImportRadius(elementNode): + 'Get the importRadius.' + if elementNode == None: + return 0.36 + preferences = skeinforge_craft.getCraftPreferences('carve') + importCoarseness = getImportCoarseness(elementNode, preferences) + layerHeight = skeinforge_craft.getCraftValue('Layer Height', preferences) + layerHeight = getCascadeFloatWithoutSelf(layerHeight, elementNode, 'layerHeight') + edgeWidthOverHeight = skeinforge_craft.getCraftValue('Edge Width over Height', preferences) + edgeWidthOverHeight = getCascadeFloatWithoutSelf(edgeWidthOverHeight, elementNode, 'edgeWidthOverHeight') + return getCascadeFloatWithoutSelf(0.5 * importCoarseness * layerHeight * edgeWidthOverHeight, elementNode, 'importRadius') + +def getInteriorOverhangAngle(elementNode): + 'Get the interior overhang support angle in degrees.' + return getCascadeFloatWithoutSelf(30.0, elementNode, 'interiorOverhangAngle') + +def getInteriorOverhangRadians(elementNode): + 'Get the interior overhang support angle in radians.' + return math.radians(getInteriorOverhangAngle(elementNode)) + +def getLayerHeight(elementNode): + 'Get the layer height.' + if elementNode == None: + return 0.4 + preferences = skeinforge_craft.getCraftPreferences('carve') + return getCascadeFloatWithoutSelf(skeinforge_craft.getCraftValue('Layer Height', preferences), elementNode, 'layerHeight') + +def getOverhangAngle(elementNode): + 'Get the overhang support angle in degrees.' + return getCascadeFloatWithoutSelf(45.0, elementNode, 'overhangAngle') + +def getOverhangRadians(elementNode): + 'Get the overhang support angle in radians.' + return math.radians(getOverhangAngle(elementNode)) + +def getOverhangSpan(elementNode): + 'Get the overhang span.' + return getCascadeFloatWithoutSelf(2.0 * getLayerHeight(elementNode), elementNode, 'overhangSpan') + +def getPrecision(elementNode): + 'Get the cascade precision.' + return getCascadeFloatWithoutSelf(0.2 * getLayerHeight(elementNode), elementNode, 'precision') + +def getSheetThickness(elementNode): + 'Get the sheet thickness.' + return getCascadeFloatWithoutSelf(3.0, elementNode, 'sheetThickness') + +def getTwistPrecision(elementNode): + 'Get the twist precision in degrees.' + return getCascadeFloatWithoutSelf(5.0, elementNode, 'twistPrecision') + +def getTwistPrecisionRadians(elementNode): + 'Get the twist precision in radians.' + return math.radians(getTwistPrecision(elementNode)) + + +class Setting: + 'Class to get handle elementNodes in a setting.' + def __init__(self, elementNode): + 'Initialize.' + self.elementNode = elementNode + + def __repr__(self): + 'Get the string representation of this Setting.' + return self.elementNode + + def getEdgeWidth(self): + 'Get the edge width.' + return getEdgeWidth(self.elementNode) + + def getImportCoarseness(self): + 'Get the importCoarseness.' + return getImportCoarseness(self.elementNode) + + def getImportRadius(self): + 'Get the importRadius.' + return getImportRadius(self.elementNode) + + def getInteriorOverhangAngle(self): + 'Get the interior overhang support angle in degrees.' + return getInteriorOverhangAngle(self.elementNode) + + def getInteriorOverhangRadians(self): + 'Get the interior overhang support angle in radians.' + return getInteriorOverhangRadians(self.elementNode) + + def getLayerHeight(self): + 'Get the layer height.' + return getLayerHeight(self.elementNode) + + def getOverhangAngle(self): + 'Get the overhang support angle in degrees.' + return getOverhangAngle(self.elementNode) + + def getOverhangRadians(self): + 'Get the overhang support angle in radians.' + return getOverhangRadians(self.elementNode) + + def getOverhangSpan(self): + 'Get the overhang span.' + return getOverhangSpan(self.elementNode) + + def getPrecision(self): + 'Get the cascade precision.' + return getPrecision(self.elementNode) + + def getSheetThickness(self): + 'Get the sheet thickness.' + return getSheetThickness(self.elementNode) + + def getTwistPrecision(self): + 'Get the twist precision in degrees.' + return getTwistPrecision(self.elementNode) + + def getTwistPrecisionRadians(self): + 'Get the twist precision in radians.' + return getTwistPrecisionRadians(self.elementNode) + + +globalAccessibleAttributeDictionary = 'getEdgeWidth getImportCoarseness getImportRadius getInteriorOverhangAngle getInteriorOverhangRadians'.split() +globalAccessibleAttributeDictionary += 'getLayerHeight getOverhangSpan getOverhangAngle getOverhangRadians'.split() +globalAccessibleAttributeDictionary += 'getPrecision getSheetThickness getTwistPrecision getTwistPrecisionRadians'.split() +globalGetAccessibleAttributeSet = set(globalAccessibleAttributeDictionary) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_enumerables/__init__.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_enumerables/__init__.py new file mode 100644 index 0000000..58ec332 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_enumerables/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 4 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_enumerables/dictionary_attribute.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_enumerables/dictionary_attribute.py new file mode 100644 index 0000000..3c8abef --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_enumerables/dictionary_attribute.py @@ -0,0 +1,102 @@ +""" +Dictionary object attributes. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import euclidean + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def _getAccessibleAttribute(attributeName, dictionaryObject): + 'Get the accessible attribute.' + if attributeName in globalNativeFunctionSet: + return getattr(dictionaryObject, attributeName, None) + if attributeName in globalGetAccessibleAttributeSet: + stringAttribute = DictionaryAttribute(dictionaryObject) + return getattr(stringAttribute, attributeName, None) + return None + + +class DictionaryAttribute: + 'Class to handle a dictionary.' + def __init__(self, dictionaryObject): + 'Initialize.' + self.dictionaryObject = dictionaryObject + + def __repr__(self): + "Get the dictionary representation of this DictionaryAttribute." + return str(self.dictionaryObject) + + def count(self, value): + 'Get the count.' + countTotal = 0 + for key, iteratorValue in self.dictionaryObject.iteritems(): + if iteratorValue == value: + countTotal += 1 + return countTotal + + def delete(self, arguments): + 'Get the delete dictionary.' + if arguments.__class__ != list: + del self.dictionaryObject[arguments] + return self.dictionaryObject + if len(arguments) == 0: + self.dictionaryObject.clear() + return self.dictionaryObject + if len(arguments) == 1: + del self.dictionaryObject[arguments[0]] + return self.dictionaryObject + for enumeratorKey in euclidean.getEnumeratorKeysAlwaysList(self.dictionaryObject, arguments): + del self.dictionaryObject[enumeratorKey] + return self.dictionaryObject + + def getIsIn(self, value): + 'Determine if the value is in.' + return value in self.dictionaryObject + + def getIsNotIn(self, value): + 'Determine if the value is in.' + return not(value in self.dictionaryObject) + + def getLength(self): + 'Get the length.' + return len(self.dictionaryObject) + + def getMax(self): + 'Get the max.' + return max(self.dictionaryObject) + + def getMin(self): + 'Get the min.' + return min(self.dictionaryObject) + + def index(self, value): + 'Get the index element.' + for key, iteratorValue in self.dictionaryObject.iteritems(): + if iteratorValue == value: + return key + raise ValueError('Value (%s) not found in index in DictionaryAttribute for (%s).' % (value, self.dictionaryObject)) + + def length(self): + 'Get the length.' + return len(self.dictionaryObject) + + def set(self, itemIndex, value): + 'Set value.' + self.dictionaryObject[itemIndex] = value + return self.dictionaryObject + + +globalAccessibleAttributeDictionary = 'count delete getIsIn getIsNotIn getLength getMax getMin index length set'.split() +globalGetAccessibleAttributeSet = set(globalAccessibleAttributeDictionary) +globalNativeFunctions = 'clear copy fromkeys get items keys pop popitem remove setdefault update values'.split() +globalNativeFunctionSet = set(globalNativeFunctions) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_enumerables/list_attribute.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_enumerables/list_attribute.py new file mode 100644 index 0000000..81711e9 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_enumerables/list_attribute.py @@ -0,0 +1,123 @@ +""" +List object attributes. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import euclidean + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def _getAccessibleAttribute(attributeName, listObject): + 'Get the accessible attribute.' + if attributeName in globalNativeFunctionSet: + return getattr(listObject, attributeName, None) + if attributeName in globalGetAccessibleAttributeSet: + stringAttribute = ListAttribute(listObject) + return getattr(stringAttribute, attributeName, None) + return None + + +class ListAttribute: + 'Class to handle a list.' + def __init__(self, listObject): + 'Initialize.' + self.listObject = listObject + + def __repr__(self): + "Get the list representation of this ListAttribute." + return str(self.listObject) + + def add(self, value): + 'Get the concatenation, same as append.' + return self.listObject + [value] + + def copy(self): + 'Get the copy.' + return self.listObject[:] + + def delete(self, arguments): + 'Get the delete list.' + deleteList = [] + enumeratorSet = set(euclidean.getEnumeratorKeysAlwaysList(self.listObject, arguments)) + for elementIndex, element in enumerate(self.listObject): + if elementIndex not in enumeratorSet: + deleteList.append(element) + return deleteList + + def get(self, itemIndex): + 'Get value by index' + return self.listObject[itemIndex] + + def getExpansion(self, items): + 'Get the concatenated copies.' + expansion = [] + for itemIndex in xrange(items): + expansion += self.listObject[:] + return expansion + + def getIsIn(self, value): + 'Determine if the value is in.' + return value in self.listObject + + def getIsNotIn(self, value): + 'Determine if the value is in.' + return not(value in self.listObject) + + def getLength(self): + 'Get the length.' + return len(self.listObject) + + def getMax(self): + 'Get the max.' + return max(self.listObject) + + def getMin(self): + 'Get the min.' + return min(self.listObject) + + def insert(self, insertIndex, value): + 'Get the insert list.' + if insertIndex < 0: + insertIndex += len(self.listObject) + insertIndex = max(0, insertIndex) + return self.listObject[: insertIndex] + [value] + self.listObject[insertIndex :] + + def keys(self): + 'Get the keys.' + return range(len(self.listObject)) + + def length(self): + 'Get the length.' + return len(self.listObject) + + def rindex(self, value): + 'Get the rindex element.' + for elementIndex, element in enumerate(self.listObject): + if element == value: + return elementIndex + raise ValueError('Value (%s) not found in rindex in ListAttribute for (%s).' % (value, self.listObject)) + + def set(self, itemIndex, value): + 'Set value.' + self.listObject[itemIndex] = value + return self.listObject + + def values(self, arguments=None): + 'Get the values.' + return self.listObject + + +globalAccessibleAttributeDictionary = 'add copy count delete get getExpansion getIsIn getIsNotIn getLength getMax getMin'.split() +globalAccessibleAttributeDictionary += 'insert keys length rindex set values'.split() +globalGetAccessibleAttributeSet = set(globalAccessibleAttributeDictionary) +globalNativeFunctions = 'append extend index pop remove reverse sort'.split() +globalNativeFunctionSet = set(globalNativeFunctions) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_enumerables/string_attribute.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_enumerables/string_attribute.py new file mode 100644 index 0000000..f3ab451 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_enumerables/string_attribute.py @@ -0,0 +1,136 @@ +""" +String object attributes. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import euclidean + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def _getAccessibleAttribute(attributeName, stringObject): + 'Get the accessible attribute.' + if attributeName in globalNativeFunctionSet: + return getattr(stringObject, attributeName, None) + if attributeName in globalGetAccessibleAttributeSet: + stringAttribute = StringAttribute(stringObject) + return getattr(stringAttribute, attributeName, None) + return None + + +class StringAttribute: + 'Class to handle a string.' + def __init__(self, stringObject): + 'Initialize.' + self.stringObject = stringObject + + def __repr__(self): + "Get the string representation of this StringAttribute." + return self.stringObject + + def add(self, nextString): + 'Get the add string, same as append.' + return self.stringObject + nextString + + def append(self, nextString): + 'Get the append string.' + return self.stringObject + nextString + + def copy(self): + 'Get the copy.' + return self.stringObject[:] + + def delete(self, arguments): + 'Get the delete string.' + deleteString = '' + enumeratorSet = set(euclidean.getEnumeratorKeysAlwaysList(self.stringObject, arguments)) + for characterIndex, character in enumerate(self.stringObject): + if characterIndex not in enumeratorSet: + deleteString += character + return deleteString + + def get(self, itemIndex): + 'Get value by characterIndex' + return self.stringObject[itemIndex] + + def getExpansion(self, items): + 'Get the concatenated copies.' + expansion = '' + for itemIndex in xrange(items): + expansion += self.stringObject + return expansion + + def getIsIn(self, value): + 'Determine if the value is in.' + return value in self.stringObject + + def getIsNotIn(self, value): + 'Determine if the value is in.' + return not(value in self.stringObject) + + def getLength(self): + 'Get the length.' + return len(self.stringObject) + + def getMax(self): + 'Get the max.' + return max(self.stringObject) + + def getMin(self): + 'Get the min.' + return min(self.stringObject) + + def insert(self, insertIndex, value): + 'Get the insert string.' + if insertIndex < 0: + insertIndex += len(self.stringObject) + insertIndex = max(0, insertIndex) + return self.stringObject[: insertIndex] + value + self.stringObject[insertIndex :] + + def keys(self): + 'Get the keys.' + return range(len(self.stringObject)) + + def length(self): + 'Get the length.' + return len(self.stringObject) + + def remove(self, value): + 'Get the remove string.' + removeIndex = self.stringObject.find(value) + if removeIndex > -1: + return self.stringObject[: removeIndex] + self.stringObject[removeIndex + len(value) :] + return self.stringObject + + def reverse(self): + 'Get the reverse string.' + return self.stringObject[: : -1] + + def set(self, itemIndex, value): + 'Set value.' + self.stringObject[itemIndex] = value + return self.stringObject + + def values(self): + 'Get the values.' + values = [] + for character in self.stringObject: + values.append(character) + return values + + +globalAccessibleAttributeDictionary = 'add append copy delete get getExpansion getIsIn getIsNotIn getLength getMax getMin'.split() +globalAccessibleAttributeDictionary += 'insert keys length remove reverse set values'.split() +globalGetAccessibleAttributeSet = set(globalAccessibleAttributeDictionary) +globalNativeFunctions = 'capitalize center count decode encode endswith expandtabs find format index isalnum join'.split() +globalNativeFunctions += 'isalpha isdigit islower isspace istitle isupper ljust lower lstrip partition replace rfind rindex'.split() +globalNativeFunctions += 'rjust rpartition rsplit rstrip split splitlines startswith strip swapcase title translate upper zfill'.split() +globalNativeFunctionSet = set(globalNativeFunctions) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_fundamentals/__init__.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_fundamentals/__init__.py new file mode 100644 index 0000000..58ec332 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_fundamentals/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 4 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_fundamentals/_math.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_fundamentals/_math.py new file mode 100644 index 0000000..9a244f7 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_fundamentals/_math.py @@ -0,0 +1,101 @@ +""" +Boolean geometry utilities. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import euclidean +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__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} diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_fundamentals/euclid.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_fundamentals/euclid.py new file mode 100644 index 0000000..bf4d410 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_fundamentals/euclid.py @@ -0,0 +1,95 @@ +""" +Boolean geometry utilities. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities.vector3index import Vector3Index +from fabmetheus_utilities import euclidean +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def _getAccessibleAttribute(attributeName): + 'Get the accessible attribute.' + if attributeName in globalAccessibleAttributeDictionary: + return globalAccessibleAttributeDictionary[attributeName] + return None + +def getComplex(x=0.0, y=0.0): + 'Get the complex.' + return complex(x, y) + +def getCylindrical(azimuthDegrees, radius=1.0, z=0.0): + 'Get the cylindrical vector3 by degrees.' + return getCylindricalByRadians(math.radians(azimuthDegrees), radius, z) + +def getCylindricalByRadians(azimuthRadians, radius=1.0, z=0.0): + 'Get the cylindrical vector3 by radians.' + polar = radius * euclidean.getWiddershinsUnitPolar(azimuthRadians) + return Vector3(polar.real, polar.imag, z) + +def getNestedVectorTestExample(x=0.0, y=0.0, z=0.0): + 'Get the NestedVectorTestExample.' + return NestedVectorTestExample(Vector3(x, y, z)) + +def getPolar(angleDegrees, radius=1.0): + 'Get the complex polar by degrees.' + return radius * euclidean.getWiddershinsUnitPolar(math.radians(angleDegrees)) + +def getPolarByRadians(angleRadians, radius=1.0): + 'Get the complex polar by radians.' + return radius * euclidean.getWiddershinsUnitPolar(angleRadians) + +def getSpherical(azimuthDegrees, elevationDegrees, radius=1.0): + 'Get the spherical vector3 unit by degrees.' + return getSphericalByRadians(math.radians(azimuthDegrees), math.radians(elevationDegrees), radius) + +def getSphericalByRadians(azimuthRadians, elevationRadians, radius=1.0): + 'Get the spherical vector3 unit by radians.' + elevationComplex = euclidean.getWiddershinsUnitPolar(elevationRadians) + azimuthComplex = euclidean.getWiddershinsUnitPolar(azimuthRadians) * elevationComplex.real + return Vector3(azimuthComplex.real, azimuthComplex.imag, elevationComplex.imag) * radius + +def getVector3(x=0.0, y=0.0, z=0.0): + 'Get the vector3.' + return Vector3(x, y, z) + +def getVector3Index(index=0, x=0.0, y=0.0, z=0.0): + 'Get the vector3.' + return Vector3Index(index, x, y, z) + + +class NestedVectorTestExample: + 'Class to test local attribute.' + def __init__(self, vector3): + 'Get the accessible attribute.' + self.vector3 = vector3 + + def _getAccessibleAttribute(self, attributeName): + "Get the accessible attribute." + if attributeName == 'vector3': + return getattr(self, attributeName, None) + return None + + +globalAccessibleAttributeDictionary = { + 'complex' : getComplex, + 'getCylindrical' : getCylindrical, + 'getCylindricalByRadians' : getCylindricalByRadians, + 'getPolar' : getPolar, + 'getPolarByRadians' : getPolarByRadians, + 'getSpherical' : getSpherical, + 'getSphericalByRadians' : getSphericalByRadians, + 'NestedVectorTestExample' : getNestedVectorTestExample, + 'Vector3' : getVector3, + 'Vector3Index' : getVector3Index} diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_fundamentals/measure.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_fundamentals/measure.py new file mode 100644 index 0000000..f3ec99e --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_fundamentals/measure.py @@ -0,0 +1,63 @@ +""" +Boolean geometry utilities. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__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} diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_fundamentals/print.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_fundamentals/print.py new file mode 100644 index 0000000..199d08f --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/evaluate_fundamentals/print.py @@ -0,0 +1,36 @@ +""" +Boolean geometry utilities. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +import sys + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__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} diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/example.csv b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/example.csv new file mode 100644 index 0000000..eeecad1 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/example.csv @@ -0,0 +1,22 @@ +"A" +0 0 +2 8 +4 0 + +1 4 +3 4 + +"B" +0 4 +2 4 +4 5 +4 7 +3 8 +0 8 +0 0 +3 0 +4 1 +4 3 +2 4 + +"C" diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/matrix.py b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/matrix.py new file mode 100644 index 0000000..b409705 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/geometry_utilities/matrix.py @@ -0,0 +1,495 @@ +""" +Boolean geometry four by four matrix. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import xml_simple_writer +import cStringIO +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalExecutionOrder = 300 + + +def addVertexes(geometryOutput, vertexes): + 'Add the vertexes.' + if geometryOutput.__class__ == list: + for element in geometryOutput: + addVertexes(element, vertexes) + return + if geometryOutput.__class__ == dict: + for geometryOutputKey in geometryOutput.keys(): + if geometryOutputKey == 'vertex': + vertexes += geometryOutput[geometryOutputKey] + else: + addVertexes(geometryOutput[geometryOutputKey], vertexes) + +def getBranchMatrix(elementNode): + 'Get matrix starting from the object if it exists, otherwise get a matrix starting from stratch.' + branchMatrix = Matrix() + matrixChildElement = elementNode.getFirstChildByLocalName('matrix') + if matrixChildElement != None: + branchMatrix = branchMatrix.getFromElementNode(matrixChildElement, '') + branchMatrix = branchMatrix.getFromElementNode(elementNode, 'matrix.') + if elementNode.xmlObject == None: + return branchMatrix + elementNodeMatrix = elementNode.xmlObject.getMatrix4X4() + if elementNodeMatrix == None: + return branchMatrix + return elementNodeMatrix.getOtherTimesSelf(branchMatrix.tetragrid) + +def getBranchMatrixSetElementNode(elementNode): + 'Get matrix starting from the object if it exists, otherwise get a matrix starting from stratch.' + branchMatrix = getBranchMatrix(elementNode) + setElementNodeDictionaryMatrix(elementNode, branchMatrix) + return branchMatrix + +def getCumulativeVector3Remove(defaultVector3, elementNode, prefix): + 'Get cumulative vector3 and delete the prefixed attributes.' + if prefix == '': + defaultVector3.x = evaluate.getEvaluatedFloat(defaultVector3.x, elementNode, 'x') + defaultVector3.y = evaluate.getEvaluatedFloat(defaultVector3.y, elementNode, 'y') + defaultVector3.z = evaluate.getEvaluatedFloat(defaultVector3.z, elementNode, 'z') + euclidean.removeElementsFromDictionary(elementNode.attributes, ['x', 'y', 'z']) + prefix = 'cartesian' + defaultVector3 = evaluate.getVector3ByPrefix(defaultVector3, elementNode, prefix) + euclidean.removePrefixFromDictionary(elementNode.attributes, prefix) + return defaultVector3 + +def getDiagonalSwitchedTetragrid(angleDegrees, diagonals): + 'Get the diagonals and switched matrix by degrees.' + return getDiagonalSwitchedTetragridByRadians(math.radians(angleDegrees), diagonals) + +def getDiagonalSwitchedTetragridByPolar(diagonals, unitPolar): + 'Get the diagonals and switched matrix by unitPolar.' + diagonalSwitchedTetragrid = getIdentityTetragrid() + for diagonal in diagonals: + diagonalSwitchedTetragrid[diagonal][diagonal] = unitPolar.real + diagonalSwitchedTetragrid[diagonals[0]][diagonals[1]] = -unitPolar.imag + diagonalSwitchedTetragrid[diagonals[1]][diagonals[0]] = unitPolar.imag + return diagonalSwitchedTetragrid + +def getDiagonalSwitchedTetragridByRadians(angleRadians, diagonals): + 'Get the diagonals and switched matrix by radians.' + return getDiagonalSwitchedTetragridByPolar(diagonals, euclidean.getWiddershinsUnitPolar(angleRadians)) + +def getIdentityTetragrid(tetragrid=None): + 'Get four by four matrix with diagonal elements set to one.' + if tetragrid == None: + return [[1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [0.0, 0.0, 0.0, 1.0]] + return tetragrid + +def getIsIdentityTetragrid(tetragrid): + 'Determine if the tetragrid is the identity tetragrid.' + for column in xrange(4): + for row in xrange(4): + if column == row: + if tetragrid[column][row] != 1.0: + return False + elif tetragrid[column][row] != 0.0: + return False + return True + +def getIsIdentityTetragridOrNone(tetragrid): + 'Determine if the tetragrid is None or if it is the identity tetragrid.' + if tetragrid == None: + return True + return getIsIdentityTetragrid(tetragrid) + +def getKeyA(row, column, prefix=''): + 'Get the a format key string from row & column, counting from zero.' + return '%sa%s%s' % (prefix, row, column) + +def getKeyM(row, column, prefix=''): + 'Get the m format key string from row & column, counting from one.' + return '%sm%s%s' % (prefix, row + 1, column + 1) + +def getKeysA(prefix=''): + 'Get the matrix keys, counting from zero.' + keysA = [] + for row in xrange(4): + for column in xrange(4): + key = getKeyA(row, column, prefix) + keysA.append(key) + return keysA + +def getKeysM(prefix=''): + 'Get the matrix keys, counting from one.' + keysM = [] + for row in xrange(4): + for column in xrange(4): + key = getKeyM(row, column, prefix) + keysM.append(key) + return keysM + +def getRemovedFloat(defaultFloat, elementNode, key, prefix): + 'Get the float by the key and the prefix.' + prefixKey = prefix + key + if prefixKey in elementNode.attributes: + floatValue = evaluate.getEvaluatedFloat(None, elementNode, prefixKey) + if floatValue == None: + print('Warning, evaluated value in getRemovedFloatByKeys in matrix is None for key:') + print(prefixKey) + print('for elementNode dictionary value:') + print(elementNode.attributes[prefixKey]) + print('for elementNode dictionary:') + print(elementNode.attributes) + else: + defaultFloat = floatValue + del elementNode.attributes[prefixKey] + return defaultFloat + +def getRemovedFloatByKeys(defaultFloat, elementNode, keys, prefix): + 'Get the float by the keys and the prefix.' + for key in keys: + defaultFloat = getRemovedFloat(defaultFloat, elementNode, key, prefix) + return defaultFloat + +def getRotateAroundAxisTetragrid(elementNode, prefix): + 'Get rotate around axis tetragrid and delete the axis and angle attributes.' + angle = getRemovedFloatByKeys(0.0, elementNode, ['angle', 'counterclockwise'], prefix) + angle -= getRemovedFloat(0.0, elementNode, 'clockwise', prefix) + if angle == 0.0: + return None + angleRadians = math.radians(angle) + axis = getCumulativeVector3Remove(Vector3(), elementNode, prefix + 'axis') + axisLength = abs(axis) + if axisLength <= 0.0: + print('Warning, axisLength was zero in getRotateAroundAxisTetragrid in matrix so nothing will be done for:') + print(elementNode) + return None + axis /= axisLength + tetragrid = getIdentityTetragrid() + cosAngle = math.cos(angleRadians) + sinAngle = math.sin(angleRadians) + oneMinusCos = 1.0 - math.cos(angleRadians) + xx = axis.x * axis.x + xy = axis.x * axis.y + xz = axis.x * axis.z + yy = axis.y * axis.y + yz = axis.y * axis.z + zz = axis.z * axis.z + tetragrid[0] = [cosAngle + xx * oneMinusCos, xy * oneMinusCos - axis.z * sinAngle, xz * oneMinusCos + axis.y * sinAngle, 0.0] + tetragrid[1] = [xy * oneMinusCos + axis.z * sinAngle, cosAngle + yy * oneMinusCos, yz * oneMinusCos - axis.x * sinAngle, 0.0] + tetragrid[2] = [xz * oneMinusCos - axis.y * sinAngle, yz * oneMinusCos + axis.x * sinAngle, cosAngle + zz * oneMinusCos, 0.0] + return tetragrid + +def getRotateTetragrid(elementNode, prefix): + 'Get rotate tetragrid and delete the rotate attributes.' + # http://en.wikipedia.org/wiki/Rotation_matrix + rotateMatrix = Matrix() + rotateMatrix.tetragrid = getRotateAroundAxisTetragrid(elementNode, prefix) + zAngle = getRemovedFloatByKeys(0.0, elementNode, ['axisclockwisez', 'observerclockwisez', 'z'], prefix) + zAngle -= getRemovedFloatByKeys(0.0, elementNode, ['axiscounterclockwisez', 'observercounterclockwisez'], prefix) + if zAngle != 0.0: + rotateMatrix.tetragrid = getTetragridTimesOther(getDiagonalSwitchedTetragrid(-zAngle, [0, 1]), rotateMatrix.tetragrid) + xAngle = getRemovedFloatByKeys(0.0, elementNode, ['axisclockwisex', 'observerclockwisex', 'x'], prefix) + xAngle -= getRemovedFloatByKeys(0.0, elementNode, ['axiscounterclockwisex', 'observercounterclockwisex'], prefix) + if xAngle != 0.0: + rotateMatrix.tetragrid = getTetragridTimesOther(getDiagonalSwitchedTetragrid(-xAngle, [1, 2]), rotateMatrix.tetragrid) + yAngle = getRemovedFloatByKeys(0.0, elementNode, ['axiscounterclockwisey', 'observerclockwisey', 'y'], prefix) + yAngle -= getRemovedFloatByKeys(0.0, elementNode, ['axisclockwisey', 'observercounterclockwisey'], prefix) + if yAngle != 0.0: + rotateMatrix.tetragrid = getTetragridTimesOther(getDiagonalSwitchedTetragrid(yAngle, [0, 2]), rotateMatrix.tetragrid) + return rotateMatrix.tetragrid + +def getScaleTetragrid(elementNode, prefix): + 'Get scale matrix and delete the scale attributes.' + scaleDefaultVector3 = Vector3(1.0, 1.0, 1.0) + scale = getCumulativeVector3Remove(scaleDefaultVector3.copy(), elementNode, prefix) + if scale == scaleDefaultVector3: + return None + return [[scale.x, 0.0, 0.0, 0.0], [0.0, scale.y, 0.0, 0.0], [0.0, 0.0, scale.z, 0.0], [0.0, 0.0, 0.0, 1.0]] + +def getTetragridA(elementNode, prefix, tetragrid): + 'Get the tetragrid from the elementNode letter a values.' + keysA = getKeysA(prefix) + evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, keysA) + if len(evaluatedDictionary.keys()) < 1: + return tetragrid + for row in xrange(4): + for column in xrange(4): + key = getKeyA(row, column, prefix) + if key in evaluatedDictionary: + value = evaluatedDictionary[key] + if value == None or value == 'None': + print('Warning, value in getTetragridA in matrix is None for key for dictionary:') + print(key) + print(evaluatedDictionary) + else: + tetragrid = getIdentityTetragrid(tetragrid) + tetragrid[row][column] = float(value) + euclidean.removeElementsFromDictionary(elementNode.attributes, keysA) + return tetragrid + +def getTetragridC(elementNode, prefix, tetragrid): + 'Get the matrix Tetragrid from the elementNode letter c values.' + columnKeys = 'Pc1 Pc2 Pc3 Pc4'.replace('P', prefix).split() + evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, columnKeys) + if len(evaluatedDictionary.keys()) < 1: + return tetragrid + for columnKeyIndex, columnKey in enumerate(columnKeys): + if columnKey in evaluatedDictionary: + value = evaluatedDictionary[columnKey] + if value == None or value == 'None': + print('Warning, value in getTetragridC in matrix is None for columnKey for dictionary:') + print(columnKey) + print(evaluatedDictionary) + else: + tetragrid = getIdentityTetragrid(tetragrid) + for elementIndex, element in enumerate(value): + tetragrid[elementIndex][columnKeyIndex] = element + euclidean.removeElementsFromDictionary(elementNode.attributes, columnKeys) + return tetragrid + +def getTetragridCopy(tetragrid): + 'Get tetragrid copy.' + if tetragrid == None: + return None + tetragridCopy = [] + for tetragridRow in tetragrid: + tetragridCopy.append(tetragridRow[:]) + return tetragridCopy + +def getTetragridM(elementNode, prefix, tetragrid): + 'Get the tetragrid from the elementNode letter m values.' + keysM = getKeysM(prefix) + evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, keysM) + if len(evaluatedDictionary.keys()) < 1: + return tetragrid + for row in xrange(4): + for column in xrange(4): + key = getKeyM(row, column, prefix) + if key in evaluatedDictionary: + value = evaluatedDictionary[key] + if value == None or value == 'None': + print('Warning, value in getTetragridM in matrix is None for key for dictionary:') + print(key) + print(evaluatedDictionary) + else: + tetragrid = getIdentityTetragrid(tetragrid) + tetragrid[row][column] = float(value) + euclidean.removeElementsFromDictionary(elementNode.attributes, keysM) + return tetragrid + +def getTetragridMatrix(elementNode, prefix, tetragrid): + 'Get the tetragrid from the elementNode matrix value.' + matrixKey = prefix + 'matrix' + evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, [matrixKey]) + if len(evaluatedDictionary.keys()) < 1: + return tetragrid + value = evaluatedDictionary[matrixKey] + if value == None or value == 'None': + print('Warning, value in getTetragridMatrix in matrix is None for matrixKey for dictionary:') + print(matrixKey) + print(evaluatedDictionary) + else: + tetragrid = getIdentityTetragrid(tetragrid) + for rowIndex, row in enumerate(value): + for elementIndex, element in enumerate(row): + tetragrid[rowIndex][elementIndex] = element + euclidean.removeElementsFromDictionary(elementNode.attributes, [matrixKey]) + return tetragrid + +def getTetragridR(elementNode, prefix, tetragrid): + 'Get the tetragrid from the elementNode letter r values.' + rowKeys = 'Pr1 Pr2 Pr3 Pr4'.replace('P', prefix).split() + evaluatedDictionary = evaluate.getEvaluatedDictionaryByEvaluationKeys(elementNode, rowKeys) + if len(evaluatedDictionary.keys()) < 1: + return tetragrid + for rowKeyIndex, rowKey in enumerate(rowKeys): + if rowKey in evaluatedDictionary: + value = evaluatedDictionary[rowKey] + if value == None or value == 'None': + print('Warning, value in getTetragridR in matrix is None for rowKey for dictionary:') + print(rowKey) + print(evaluatedDictionary) + else: + tetragrid = getIdentityTetragrid(tetragrid) + for elementIndex, element in enumerate(value): + tetragrid[rowKeyIndex][elementIndex] = element + euclidean.removeElementsFromDictionary(elementNode.attributes, rowKeys) + return tetragrid + +def getTetragridTimesOther(firstTetragrid, otherTetragrid ): + 'Get this matrix multiplied by the other matrix.' + #A down, B right from http://en.wikipedia.org/wiki/Matrix_multiplication + if firstTetragrid == None: + return otherTetragrid + if otherTetragrid == None: + return firstTetragrid + tetragridTimesOther = [] + for row in xrange(4): + matrixRow = firstTetragrid[row] + tetragridTimesOtherRow = [] + tetragridTimesOther.append(tetragridTimesOtherRow) + for column in xrange(4): + dotProduct = 0 + for elementIndex in xrange(4): + dotProduct += matrixRow[elementIndex] * otherTetragrid[elementIndex][column] + tetragridTimesOtherRow.append(dotProduct) + return tetragridTimesOther + +def getTransformedByList(floatList, point): + 'Get the point transformed by the array.' + return floatList[0] * point.x + floatList[1] * point.y + floatList[2] * point.z + floatList[3] + +def getTransformedVector3(tetragrid, vector3): + 'Get the vector3 multiplied by a matrix.' + if getIsIdentityTetragridOrNone(tetragrid): + return vector3.copy() + return getTransformedVector3Blindly(tetragrid, vector3) + +def getTransformedVector3Blindly(tetragrid, vector3): + 'Get the vector3 multiplied by a tetragrid without checking if the tetragrid exists.' + return Vector3( + getTransformedByList(tetragrid[0], vector3), + getTransformedByList(tetragrid[1], vector3), + getTransformedByList(tetragrid[2], vector3)) + +def getTransformedVector3s(tetragrid, vector3s): + 'Get the vector3s multiplied by a matrix.' + if getIsIdentityTetragridOrNone(tetragrid): + return euclidean.getPathCopy(vector3s) + transformedVector3s = [] + for vector3 in vector3s: + transformedVector3s.append(getTransformedVector3Blindly(tetragrid, vector3)) + return transformedVector3s + +def getTransformTetragrid(elementNode, prefix): + 'Get the tetragrid from the elementNode.' + tetragrid = getTetragridA(elementNode, prefix, None) + tetragrid = getTetragridC(elementNode, prefix, tetragrid) + tetragrid = getTetragridM(elementNode, prefix, tetragrid) + tetragrid = getTetragridMatrix(elementNode, prefix, tetragrid) + tetragrid = getTetragridR(elementNode, prefix, tetragrid) + return tetragrid + +def getTranslateTetragrid(elementNode, prefix): + 'Get translate matrix and delete the translate attributes.' + translation = getCumulativeVector3Remove(Vector3(), elementNode, prefix) + if translation.getIsDefault(): + return None + return getTranslateTetragridByTranslation(translation) + +def getTranslateTetragridByTranslation(translation): + 'Get translate tetragrid by translation.' + return [[1.0, 0.0, 0.0, translation.x], [0.0, 1.0, 0.0, translation.y], [0.0, 0.0, 1.0, translation.z], [0.0, 0.0, 0.0, 1.0]] + +def getVertexes(geometryOutput): + 'Get the vertexes.' + vertexes = [] + addVertexes(geometryOutput, vertexes) + return vertexes + +def setAttributesToMultipliedTetragrid(elementNode, tetragrid): + 'Set the element attribute dictionary and element matrix to the matrix times the tetragrid.' + setElementNodeDictionaryMatrix(elementNode, getBranchMatrix(elementNode).getOtherTimesSelf(tetragrid)) + +def setElementNodeDictionaryMatrix(elementNode, matrix4X4): + 'Set the element attribute dictionary or element matrix to the matrix.' + if elementNode.xmlObject == None: + elementNode.attributes.update(matrix4X4.getAttributes('matrix.')) + else: + elementNode.xmlObject.matrix4X4 = matrix4X4 + +def transformVector3Blindly(tetragrid, vector3): + 'Transform the vector3 by a tetragrid without checking to see if it exists.' + x = getTransformedByList(tetragrid[0], vector3) + y = getTransformedByList(tetragrid[1], vector3) + z = getTransformedByList(tetragrid[2], vector3) + vector3.x = x + vector3.y = y + vector3.z = z + +def transformVector3ByMatrix(tetragrid, vector3): + 'Transform the vector3 by a matrix.' + if getIsIdentityTetragridOrNone(tetragrid): + return + transformVector3Blindly(tetragrid, vector3) + +def transformVector3sByMatrix(tetragrid, vector3s): + 'Transform the vector3s by a matrix.' + if getIsIdentityTetragridOrNone(tetragrid): + return + for vector3 in vector3s: + transformVector3Blindly(tetragrid, vector3) + + +class Matrix: + 'A four by four matrix.' + def __init__(self, tetragrid=None): + 'Add empty lists.' + self.tetragrid = getTetragridCopy(tetragrid) + + def __eq__(self, other): + 'Determine whether this matrix is identical to other one.' + if other == None: + return False + if other.__class__ != self.__class__: + return False + return other.tetragrid == self.tetragrid + + def __ne__(self, other): + 'Determine whether this vector is not identical to other one.' + return not self.__eq__(other) + + def __repr__(self): + 'Get the string representation of this four by four matrix.' + output = cStringIO.StringIO() + self.addXML(0, output) + return output.getvalue() + + def addXML(self, depth, output): + 'Add xml for this object.' + attributes = self.getAttributes() + if len(attributes) > 0: + xml_simple_writer.addClosedXMLTag(attributes, depth, self.__class__.__name__.lower(), output) + + def getAttributes(self, prefix=''): + 'Get the attributes from row column attribute strings, counting from one.' + attributes = {} + if self.tetragrid == None: + return attributes + for row in xrange(4): + for column in xrange(4): + default = float(column == row) + value = self.tetragrid[row][column] + if abs( value - default ) > 0.00000000000001: + if abs(value) < 0.00000000000001: + value = 0.0 + attributes[prefix + getKeyM(row, column)] = value + return attributes + + def getFromElementNode(self, elementNode, prefix): + 'Get the values from row column attribute strings, counting from one.' + attributes = elementNode.attributes + if attributes == None: + return self + self.tetragrid = getTetragridTimesOther(getTransformTetragrid(elementNode, prefix), self.tetragrid) + self.tetragrid = getTetragridTimesOther(getScaleTetragrid(elementNode, 'scale.'), self.tetragrid) + self.tetragrid = getTetragridTimesOther(getRotateTetragrid(elementNode, 'rotate.'), self.tetragrid) + self.tetragrid = getTetragridTimesOther(getTranslateTetragrid(elementNode, 'translate.'), self.tetragrid) + return self + + def getOtherTimesSelf(self, otherTetragrid): + 'Get this matrix reverse multiplied by the other matrix.' + return Matrix(getTetragridTimesOther(otherTetragrid, self.tetragrid)) + + def getSelfTimesOther(self, otherTetragrid): + 'Get this matrix multiplied by the other matrix.' + return Matrix(getTetragridTimesOther(self.tetragrid, otherTetragrid)) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_matrix/__init__.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_matrix/__init__.py new file mode 100644 index 0000000..1121e8a --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_matrix/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 3 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_matrix/_scale.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_matrix/_scale.py new file mode 100644 index 0000000..787ea1c --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_matrix/_scale.py @@ -0,0 +1,68 @@ +""" +Boolean geometry scale. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import solid +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalExecutionOrder = 340 + + +def getManipulatedGeometryOutput(elementNode, geometryOutput, prefix): + "Get equated geometryOutput." + scalePoints( elementNode, matrix.getVertexes(geometryOutput), prefix ) + return geometryOutput + +def getManipulatedPaths(close, elementNode, loop, prefix, sideLength): + "Get equated paths." + scalePoints( elementNode, loop, prefix ) + return [loop] + +def getNewDerivation(elementNode, prefix, sideLength): + 'Get new derivation.' + return ScaleDerivation(elementNode) + +def manipulateElementNode(elementNode, target): + "Manipulate the xml element." + derivation = ScaleDerivation(elementNode) + if derivation.scaleTetragrid == None: + print('Warning, scaleTetragrid was None in scale so nothing will be done for:') + print(elementNode) + return + matrix.setAttributesToMultipliedTetragrid(target, derivation.scaleTetragrid) + +def processElementNode(elementNode): + "Process the xml element." + solid.processElementNodeByFunction(elementNode, manipulateElementNode) + +def scalePoints(elementNode, points, prefix): + "Scale the points." + scaleVector3Default = Vector3(1.0, 1.0, 1.0) + scaleVector3 = matrix.getCumulativeVector3Remove(scaleVector3Default.copy(), elementNode, prefix) + if scaleVector3 == scaleVector3Default: + return + for point in points: + point.x *= scaleVector3.x + point.y *= scaleVector3.y + point.z *= scaleVector3.z + + +class ScaleDerivation: + "Class to hold scale variables." + def __init__(self, elementNode): + 'Set defaults.' + self.scaleTetragrid = matrix.getScaleTetragrid(elementNode, '') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_matrix/rotate.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_matrix/rotate.py new file mode 100644 index 0000000..a64b2d8 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_matrix/rotate.py @@ -0,0 +1,67 @@ +""" +Boolean geometry rotate. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import solid +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalExecutionOrder = 360 + + +def getManipulatedGeometryOutput(elementNode, geometryOutput, prefix): + 'Get equated geometryOutput.' + rotatePoints(elementNode, matrix.getVertexes(geometryOutput), prefix) + return geometryOutput + +def getManipulatedPaths(close, elementNode, loop, prefix, sideLength): + 'Get equated paths.' + rotatePoints(elementNode, loop, prefix) + return [loop] + +def getNewDerivation(elementNode, prefix, sideLength): + 'Get new derivation.' + return RotateDerivation(elementNode, prefix) + +def manipulateElementNode(elementNode, target): + 'Manipulate the xml element.' + derivation = RotateDerivation(elementNode, '') + if derivation.rotateTetragrid == None: + print('Warning, rotateTetragrid was None in rotate so nothing will be done for:') + print(elementNode) + return + matrix.setAttributesToMultipliedTetragrid(target, derivation.rotateTetragrid) + +def processElementNode(elementNode): + 'Process the xml element.' + solid.processElementNodeByFunction(elementNode, manipulateElementNode) + +def rotatePoints(elementNode, points, prefix): + 'Rotate the points.' + derivation = RotateDerivation(elementNode, prefix) + if derivation.rotateTetragrid == None: + print('Warning, rotateTetragrid was None in rotate so nothing will be done for:') + print(elementNode) + return + matrix.transformVector3sByMatrix(derivation.rotateTetragrid, points) + + +class RotateDerivation: + "Class to hold rotate variables." + def __init__(self, elementNode, prefix): + 'Set defaults.' + self.rotateTetragrid = matrix.getRotateTetragrid(elementNode, prefix) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_matrix/transform.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_matrix/transform.py new file mode 100644 index 0000000..3df0b61 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_matrix/transform.py @@ -0,0 +1,67 @@ +""" +Boolean geometry transform. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import solid +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalExecutionOrder = 320 + + +def getManipulatedGeometryOutput(elementNode, geometryOutput, prefix): + 'Get equated geometryOutput.' + transformPoints(elementNode, matrix.getVertexes(geometryOutput), prefix) + return geometryOutput + +def getManipulatedPaths(close, elementNode, loop, prefix, sideLength): + 'Get equated paths.' + transformPoints(elementNode, loop, prefix) + return [loop] + +def getNewDerivation(elementNode, prefix, sideLength): + 'Get new derivation.' + return TransformDerivation(elementNode, prefix) + +def manipulateElementNode(elementNode, target): + 'Manipulate the xml element.' + derivation = TransformDerivation(elementNode, '') + if derivation.transformTetragrid == None: + print('Warning, transformTetragrid was None in transform so nothing will be done for:') + print(elementNode) + return + matrix.setAttributesToMultipliedTetragrid(target, derivation.transformTetragrid) + +def processElementNode(elementNode): + 'Process the xml element.' + solid.processElementNodeByFunction(elementNode, manipulateElementNode) + +def transformPoints(elementNode, points, prefix): + 'Transform the points.' + derivation = TransformDerivation(elementNode, prefix) + if derivation.transformTetragrid == None: + print('Warning, transformTetragrid was None in transform so nothing will be done for:') + print(elementNode) + return + matrix.transformVector3sByMatrix(derivation.transformTetragrid, points) + + +class TransformDerivation: + "Class to hold transform variables." + def __init__(self, elementNode, prefix): + 'Set defaults.' + self.transformTetragrid = matrix.getTransformTetragrid(elementNode, prefix) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_matrix/translate.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_matrix/translate.py new file mode 100644 index 0000000..fb68398 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_matrix/translate.py @@ -0,0 +1,68 @@ +""" +Boolean geometry translation. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import solid +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalExecutionOrder = 380 + + +def getManipulatedGeometryOutput(elementNode, geometryOutput, prefix): + "Get equated geometryOutput." + translatePoints(elementNode, matrix.getVertexes(geometryOutput), prefix) + return geometryOutput + +def getManipulatedPaths(close, elementNode, loop, prefix, sideLength): + "Get equated paths." + translatePoints(elementNode, loop, prefix) + return [loop] + +def getNewDerivation(elementNode, prefix, sideLength): + 'Get new derivation.' + return TranslateDerivation(elementNode) + +def manipulateElementNode(elementNode, target): + "Manipulate the xml element." + derivation = TranslateDerivation(elementNode) + if derivation.translateTetragrid == None: + print('Warning, translateTetragrid was None in translate so nothing will be done for:') + print(elementNode) + return + matrix.setAttributesToMultipliedTetragrid(target, derivation.translateTetragrid) + +def processElementNode(elementNode): + "Process the xml element." + solid.processElementNodeByFunction(elementNode, manipulateElementNode) + +def translateNegativesPositives(negatives, positives, translation): + 'Translate the negatives and postives.' + euclidean.translateVector3Path(matrix.getVertexes(negatives), translation) + euclidean.translateVector3Path(matrix.getVertexes(positives), translation) + +def translatePoints(elementNode, points, prefix): + "Translate the points." + translateVector3 = matrix.getCumulativeVector3Remove(Vector3(), elementNode, prefix) + if abs(translateVector3) > 0.0: + euclidean.translateVector3Path(points, translateVector3) + + +class TranslateDerivation: + "Class to hold translate variables." + def __init__(self, elementNode): + 'Set defaults.' + self.translateTetragrid = matrix.getTranslateTetragrid(elementNode, '') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_meta/__init__.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_meta/__init__.py new file mode 100644 index 0000000..1121e8a --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_meta/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 3 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_meta/_array.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_meta/_array.py new file mode 100644 index 0000000..4f62546 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_meta/_array.py @@ -0,0 +1,129 @@ +""" +Boolean geometry array. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_tools import vertex +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities import euclidean +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def addPathToGroup(derivation, groupDictionaryCopy, path, targetMatrix, totalIndex): + 'Add path to the array group.' + for pointIndex, point in enumerate(path): + arrayElement = derivation.target.getCopy(derivation.elementNode.getIDSuffix(totalIndex), derivation.elementNode) + arrayDictionary = arrayElement.attributes + arrayDictionary['visible'] = str(derivation.visible).lower() + arrayDictionary.update(groupDictionaryCopy) + euclidean.removeTrueFromDictionary(arrayDictionary, 'visible') + vertexMatrix = matrix.Matrix(matrix.getTranslateTetragridByTranslation(point)) + zAngle = totalIndex * 50.0 + rotationMatrix = getRotationMatrix(arrayDictionary, derivation, path, point, pointIndex) + arrayElementMatrix = vertexMatrix.getSelfTimesOther(rotationMatrix.getSelfTimesOther(targetMatrix.tetragrid).tetragrid) + arrayDictionary.update(arrayElementMatrix.getAttributes('matrix.')) + arrayDictionary['_arrayIndex'] = totalIndex + arrayDictionary['_arrayPoint'] = point + totalIndex += 1 + +def getNewDerivation(elementNode): + 'Get new derivation.' + return ArrayDerivation(elementNode) + +def getRotationMatrix(arrayDictionary, derivation, path, point, pointIndex): + 'Get rotationMatrix.' + if len(path) < 2 or not derivation.track: + return matrix.Matrix() + point = point.dropAxis() + begin = path[(pointIndex + len(path) - 1) % len(path)].dropAxis() + end = path[(pointIndex + 1) % len(path)].dropAxis() + pointMinusBegin = point - begin + pointMinusBeginLength = abs(pointMinusBegin) + endMinusPoint = end - point + endMinusPointLength = abs(endMinusPoint) + if not derivation.closed: + if pointIndex == 0 and endMinusPointLength > 0.0: + return getRotationMatrixByPolar(arrayDictionary, endMinusPoint, endMinusPointLength) + elif pointIndex == len(path) - 1 and pointMinusBeginLength > 0.0: + return getRotationMatrixByPolar(arrayDictionary, pointMinusBegin, pointMinusBeginLength) + if pointMinusBeginLength <= 0.0: + print('Warning, point equals previous point in getRotationMatrix in array for:') + print(path) + print(pointIndex) + print(derivation.elementNode) + return matrix.Matrix() + pointMinusBegin /= pointMinusBeginLength + if endMinusPointLength <= 0.0: + print('Warning, point equals next point in getRotationMatrix in array for:') + print(path) + print(pointIndex) + print(derivation.elementNode) + return matrix.Matrix() + endMinusPoint /= endMinusPointLength + averagePolar = pointMinusBegin + endMinusPoint + averagePolarLength = abs(averagePolar) + if averagePolarLength <= 0.0: + print('Warning, averagePolarLength is zero in getRotationMatrix in array for:') + print(path) + print(pointIndex) + print(derivation.elementNode) + return matrix.Matrix() + return getRotationMatrixByPolar(arrayDictionary, averagePolar, averagePolarLength) + +def getRotationMatrixByPolar(arrayDictionary, polar, polarLength): + 'Get rotationMatrix by polar and polarLength.' + polar /= polarLength + arrayDictionary['_arrayRotation'] = math.degrees(math.atan2(polar.imag, polar.real)) + return matrix.Matrix(matrix.getDiagonalSwitchedTetragridByPolar([0, 1], polar)) + +def processElementNode(elementNode): + "Process the xml element." + processElementNodeByDerivation(None, elementNode) + +def processElementNodeByDerivation(derivation, elementNode): + 'Process the xml element by derivation.' + if derivation == None: + derivation = ArrayDerivation(elementNode) + if derivation.target == None: + print('Warning, array could not get target for:') + print(elementNode) + return + if len(derivation.paths) < 1: + print('Warning, array could not get paths for:') + print(elementNode) + return + groupDictionaryCopy = elementNode.attributes.copy() + euclidean.removeElementsFromDictionary(groupDictionaryCopy, ['closed', 'paths', 'target', 'track', 'vertexes']) + evaluate.removeIdentifiersFromDictionary(groupDictionaryCopy) + targetMatrix = matrix.getBranchMatrixSetElementNode(derivation.target) + elementNode.localName = 'group' + totalIndex = 0 + for path in derivation.paths: + addPathToGroup(derivation, groupDictionaryCopy, path, targetMatrix, totalIndex) + elementNode.getXMLProcessor().processElementNode(elementNode) + + +class ArrayDerivation: + "Class to hold array variables." + def __init__(self, elementNode): + 'Set defaults.' + self.closed = evaluate.getEvaluatedBoolean(True, elementNode, 'closed') + self.elementNode = elementNode + self.paths = evaluate.getTransformedPathsByKey([], elementNode, 'paths') + vertexTargets = evaluate.getElementNodesByKey(elementNode, 'vertexes') + for vertexTarget in vertexTargets: + self.paths.append(vertexTarget.getVertexes()) + self.target = evaluate.getElementNodeByKey(elementNode, 'target') + self.track = evaluate.getEvaluatedBoolean(True, elementNode, 'track') + self.visible = evaluate.getEvaluatedBoolean(True, elementNode, 'visible') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_meta/_carve.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_meta/_carve.py new file mode 100644 index 0000000..223fb23 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_meta/_carve.py @@ -0,0 +1,95 @@ +""" +Boolean geometry carve. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_tools import path +from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting +from fabmetheus_utilities.geometry.geometry_utilities import boolean_geometry +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import xml_simple_reader + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getLinkedElementNode(idSuffix, parentNode, target): + 'Get elementNode with identifiers and parentNode.' + linkedElementNode = xml_simple_reader.ElementNode() + euclidean.overwriteDictionary(target.attributes, ['id', 'name', 'quantity'], linkedElementNode.attributes) + linkedElementNode.addSuffixToID(idSuffix) + tagKeys = target.getTagKeys() + tagKeys.append('carve') + tagKeys.sort() + tags = ', '.join(tagKeys) + linkedElementNode.attributes['tags'] = tags + linkedElementNode.setParentAddToChildNodes(parentNode) + linkedElementNode.addToIdentifierDictionaries() + return linkedElementNode + +def getNewDerivation(elementNode): + 'Get new derivation.' + return CarveDerivation(elementNode) + +def processElementNode(elementNode): + 'Process the xml element.' + processElementNodeByDerivation(None, elementNode) + +def processElementNodeByDerivation(derivation, elementNode): + 'Process the xml element by derivation.' + if derivation == None: + derivation = CarveDerivation(elementNode) + targetElementNode = derivation.targetElementNode + if targetElementNode == None: + print('Warning, carve could not get target for:') + print(elementNode) + return + xmlObject = targetElementNode.xmlObject + if xmlObject == None: + print('Warning, processElementNodeByDerivation in carve could not get xmlObject for:') + print(targetElementNode) + print(derivation.elementNode) + return + matrix.getBranchMatrixSetElementNode(targetElementNode) + transformedVertexes = xmlObject.getTransformedVertexes() + if len(transformedVertexes) < 1: + print('Warning, transformedVertexes is zero in processElementNodeByDerivation in carve for:') + print(xmlObject) + print(targetElementNode) + print(derivation.elementNode) + return + elementNode.localName = 'group' + elementNode.getXMLProcessor().processElementNode(elementNode) + minimumZ = boolean_geometry.getMinimumZ(xmlObject) + maximumZ = euclidean.getTopPath(transformedVertexes) + zoneArrangement = triangle_mesh.ZoneArrangement(derivation.layerHeight, transformedVertexes) + oldVisibleString = targetElementNode.attributes['visible'] + targetElementNode.attributes['visible'] = True + z = minimumZ + 0.5 * derivation.layerHeight + loopLayers = boolean_geometry.getLoopLayers([xmlObject], derivation.importRadius, derivation.layerHeight, maximumZ, False, z, zoneArrangement) + targetElementNode.attributes['visible'] = oldVisibleString + for loopLayerIndex, loopLayer in enumerate(loopLayers): + if len(loopLayer.loops) > 0: + pathElement = getLinkedElementNode('_carve_%s' % loopLayerIndex, elementNode, targetElementNode) + vector3Loops = euclidean.getVector3Paths(loopLayer.loops, loopLayer.z) + path.convertElementNode(pathElement, vector3Loops) + + +class CarveDerivation: + "Class to hold carve variables." + def __init__(self, elementNode): + 'Set defaults.' + self.elementNode = elementNode + self.importRadius = setting.getImportRadius(elementNode) + self.layerHeight = setting.getLayerHeight(elementNode) + self.targetElementNode = evaluate.getElementNodeByKey(elementNode, 'target') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_meta/_copy.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_meta/_copy.py new file mode 100644 index 0000000..ade3b95 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_meta/_copy.py @@ -0,0 +1,72 @@ +""" +Boolean geometry copy. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.creation import solid +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities import euclidean + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getNewDerivation(elementNode): + 'Get new derivation.' + return CopyDerivation(elementNode) + +def processElementNode(elementNode): + 'Process the xml element.' + processElementNodeByDerivation(None, elementNode) + +def processElementNodeByDerivation(derivation, elementNode): + 'Process the xml element by derivation.' + if derivation == None: + derivation = CopyDerivation(elementNode) + if derivation.target == None: + print('Warning, copy could not get target for:') + print(elementNode) + return + del elementNode.attributes['target'] + copyMatrix = matrix.getBranchMatrixSetElementNode(elementNode) + targetMatrix = matrix.getBranchMatrixSetElementNode(derivation.target) + targetDictionaryCopy = evaluate.removeIdentifiersFromDictionary(derivation.target.attributes.copy()) + targetDictionaryCopy.update(elementNode.attributes) + elementNode.attributes = targetDictionaryCopy + euclidean.removeTrueFromDictionary(elementNode.attributes, 'visible') + elementNode.localName = derivation.target.localName + derivation.target.copyXMLChildNodes(elementNode.getIDSuffix(), elementNode) + elementNode.getXMLProcessor().processElementNode(elementNode) + if copyMatrix != None and targetMatrix != None: + elementNode.xmlObject.matrix4X4 = copyMatrix.getSelfTimesOther(targetMatrix.tetragrid) + if elementNode.xmlObject == None: + return + if len(elementNode.xmlObject.getPaths()) > 0: + lineation.processElementNode(elementNode) + return + geometryOutput = elementNode.xmlObject.getGeometryOutput() + if geometryOutput == None: + return + solidMatchingPlugins = solid.getSolidMatchingPlugins(elementNode) + if len(solidMatchingPlugins) == 0: + return + geometryOutput = solid.getGeometryOutputByManipulation(elementNode, geometryOutput) + elementNode.xmlObject.transformGeometryOutput(geometryOutput) + lineation.removeChildNodesFromElementObject(elementNode) + elementNode.getXMLProcessor().convertElementNode(elementNode, geometryOutput) + + +class CopyDerivation: + "Class to hold copy variables." + def __init__(self, elementNode): + 'Set defaults.' + self.target = evaluate.getElementNodeByKey(elementNode, 'target') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_meta/disjoin.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_meta/disjoin.py new file mode 100644 index 0000000..32ebd49 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_meta/disjoin.py @@ -0,0 +1,114 @@ +""" +Boolean geometry disjoin. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_tools import path +from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting +from fabmetheus_utilities.geometry.geometry_utilities import boolean_geometry +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities.geometry.solids import difference +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import xml_simple_reader +from fabmetheus_utilities.vector3 import Vector3 + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getLinkedElementNode(idSuffix, parentNode, target): + 'Get elementNode with identifiers and parentNode.' + linkedElementNode = xml_simple_reader.ElementNode() + euclidean.overwriteDictionary(target.attributes, ['id', 'name', 'quantity'], linkedElementNode.attributes) + linkedElementNode.addSuffixToID(idSuffix) + tagKeys = target.getTagKeys() + tagKeys.append('disjoin') + tagKeys.sort() + tags = ', '.join(tagKeys) + linkedElementNode.attributes['tags'] = tags + linkedElementNode.setParentAddToChildNodes(parentNode) + linkedElementNode.addToIdentifierDictionaries() + return linkedElementNode + +def getNewDerivation(elementNode): + 'Get new derivation.' + return DisjoinDerivation(elementNode) + +def processElementNode(elementNode): + 'Process the xml element.' + processElementNodeByDerivation(None, elementNode) + +def processElementNodeByDerivation(derivation, elementNode): + 'Process the xml element by derivation.' + if derivation == None: + derivation = DisjoinDerivation(elementNode) + targetElementNode = derivation.targetElementNode + if targetElementNode == None: + print('Warning, disjoin could not get target for:') + print(elementNode) + return + xmlObject = targetElementNode.xmlObject + if xmlObject == None: + print('Warning, processElementNodeByDerivation in disjoin could not get xmlObject for:') + print(targetElementNode) + print(derivation.elementNode) + return + matrix.getBranchMatrixSetElementNode(targetElementNode) + transformedVertexes = xmlObject.getTransformedVertexes() + if len(transformedVertexes) < 1: + print('Warning, transformedVertexes is zero in processElementNodeByDerivation in disjoin for:') + print(xmlObject) + print(targetElementNode) + print(derivation.elementNode) + return + elementNode.localName = 'group' + elementNode.getXMLProcessor().processElementNode(elementNode) + targetChainMatrix = matrix.Matrix(xmlObject.getMatrixChainTetragrid()) + minimumZ = boolean_geometry.getMinimumZ(xmlObject) + z = minimumZ + 0.5 * derivation.sheetThickness + zoneArrangement = triangle_mesh.ZoneArrangement(derivation.layerHeight, transformedVertexes) + oldVisibleString = targetElementNode.attributes['visible'] + targetElementNode.attributes['visible'] = True + loops = boolean_geometry.getEmptyZLoops([xmlObject], derivation.importRadius, False, z, zoneArrangement) + targetElementNode.attributes['visible'] = oldVisibleString + vector3Loops = euclidean.getVector3Paths(loops, z) + pathElement = getLinkedElementNode('_sheet', elementNode, targetElementNode) + path.convertElementNode(pathElement, vector3Loops) + targetOutput = xmlObject.getGeometryOutput() + differenceElement = getLinkedElementNode('_solid', elementNode, targetElementNode) + targetElementCopy = targetElementNode.getCopy('_positive', differenceElement) + targetElementCopy.attributes['visible'] = True + targetElementCopy.attributes.update(targetChainMatrix.getAttributes('matrix.')) + complexMaximum = euclidean.getMaximumByVector3Path(transformedVertexes).dropAxis() + complexMinimum = euclidean.getMinimumByVector3Path(transformedVertexes).dropAxis() + centerComplex = 0.5 * (complexMaximum + complexMinimum) + centerVector3 = Vector3(centerComplex.real, centerComplex.imag, minimumZ) + slightlyMoreThanHalfExtent = 0.501 * (complexMaximum - complexMinimum) + inradius = Vector3(slightlyMoreThanHalfExtent.real, slightlyMoreThanHalfExtent.imag, derivation.sheetThickness) + cubeElement = xml_simple_reader.ElementNode() + cubeElement.attributes['inradius'] = str(inradius) + if not centerVector3.getIsDefault(): + cubeElement.attributes['translate.'] = str(centerVector3) + cubeElement.localName = 'cube' + cubeElement.setParentAddToChildNodes(differenceElement) + difference.processElementNode(differenceElement) + + +class DisjoinDerivation: + "Class to hold disjoin variables." + def __init__(self, elementNode): + 'Set defaults.' + self.elementNode = elementNode + self.importRadius = setting.getImportRadius(elementNode) + self.layerHeight = setting.getLayerHeight(elementNode) + self.sheetThickness = setting.getSheetThickness(elementNode) + self.targetElementNode = evaluate.getElementNodeByKey(elementNode, 'target') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_meta/import.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_meta/import.py new file mode 100644 index 0000000..f457a84 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_meta/import.py @@ -0,0 +1,99 @@ +""" +Boolean geometry group of solids. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.solids import group +from fabmetheus_utilities import xml_simple_reader +from fabmetheus_utilities import xml_simple_writer +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +import cStringIO +import os + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def appendAttributes(fromElementNode, toElementNode): + 'Append the attributes from the child nodes of fromElementNode to the attributes of toElementNode.' + for childNode in fromElementNode.childNodes: + toElementNode.attributes.update(evaluate.removeIdentifiersFromDictionary(childNode.attributes.copy())) + +def getNewDerivation(elementNode): + 'Get new derivation.' + return ImportDerivation(elementNode) + +def getXMLFromCarvingFileName(fileName): + 'Get xml text from xml text.' + carving = fabmetheus_interpret.getCarving(fileName) + if carving == None: + return '' + output = xml_simple_writer.getBeginGeometryXMLOutput() + carving.addXML(0, output) + return xml_simple_writer.getEndGeometryXMLString(output) + +def processElementNode(elementNode): + "Process the xml element." + processElementNodeByDerivation(None, elementNode) + +def processElementNodeByDerivation(derivation, elementNode): + 'Process the xml element by derivation.' + if derivation == None: + derivation = ImportDerivation(elementNode) + if derivation.fileName == None: + return + parserFileName = elementNode.getOwnerDocument().fileName + absoluteFileName = archive.getAbsoluteFolderPath(parserFileName, derivation.fileName) + if 'models/' not in absoluteFileName: + print('Warning, models/ was not in the absolute file path, so for security nothing will be done for:') + print(elementNode) + print('For which the absolute file path is:') + print(absoluteFileName) + print('The import tool can only read a file which has models/ in the file path.') + print('To import the file, move the file into a folder called model/ or a subfolder which is inside the model folder tree.') + return + xmlText = '' + if derivation.fileName.endswith('.xml'): + xmlText = archive.getFileText(absoluteFileName) + else: + xmlText = getXMLFromCarvingFileName(absoluteFileName) + print('The import tool is opening the file:') + print(absoluteFileName) + if xmlText == '': + print('The file %s could not be found by processElementNode in import.' % derivation.fileName) + return + if derivation.importName == None: + elementNode.attributes['_importName'] = archive.getUntilDot(derivation.fileName) + if derivation.basename: + elementNode.attributes['_importName'] = os.path.basename(elementNode.attributes['_importName']) + xml_simple_reader.createAppendByText(elementNode, xmlText) + if derivation.appendDocumentElement: + appendAttributes(elementNode, elementNode.getDocumentElement()) + if derivation.appendElement: + appendAttributes(elementNode, elementNode) + elementNode.localName = 'group' + evaluate.processArchivable(group.Group, elementNode) + + +class ImportDerivation: + "Class to hold import variables." + def __init__(self, elementNode): + 'Set defaults.' + self.appendDocumentElement = evaluate.getEvaluatedBoolean(False, elementNode, 'appendDocumentElement') + self.appendElement = evaluate.getEvaluatedBoolean(False, elementNode, 'appendElement') + self.basename = evaluate.getEvaluatedBoolean(True, elementNode, 'basename') + self.elementNode = elementNode + self.fileName = evaluate.getEvaluatedString('', elementNode, 'file') + self.importName = evaluate.getEvaluatedString(None, elementNode, '_importName') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_meta/write.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_meta/write.py new file mode 100644 index 0000000..f581168 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_meta/write.py @@ -0,0 +1,98 @@ +""" +Boolean geometry write. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +import os + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getNewDerivation(elementNode): + 'Get new derivation.' + return WriteDerivation(elementNode) + +def processElementNode(elementNode): + "Process the xml element." + processElementNodeByDerivation(None, elementNode) + +def processElementNodeByDerivation(derivation, elementNode): + 'Process the xml element by derivation.' + if derivation == None: + derivation = WriteDerivation(elementNode) + if len(derivation.targets) < 1: + print('Warning, processElementNode in write could not get targets for:') + print(elementNode) + return + fileNames = [] + for target in derivation.targets: + writeElementNode(derivation, fileNames, target) + +def writeElementNode(derivation, fileNames, target): + "Write a quantity of the target." + xmlObject = target.xmlObject + if xmlObject == None: + print('Warning, writeTarget in write could not get xmlObject for:') + print(target) + print(derivation.elementNode) + return + parserDirectory = os.path.dirname(derivation.elementNode.getOwnerDocument().fileName) + absoluteFolderDirectory = os.path.abspath(os.path.join(parserDirectory, derivation.folderName)) + if '/models' not in absoluteFolderDirectory: + print('Warning, models/ was not in the absolute file path, so for security nothing will be done for:') + print(derivation.elementNode) + print('For which the absolute folder path is:') + print(absoluteFolderDirectory) + print('The write tool can only write a file which has models/ in the file path.') + print('To write the file, move the file into a folder called model/ or a subfolder which is inside the model folder tree.') + return + quantity = evaluate.getEvaluatedInt(1, target, 'quantity') + for itemIndex in xrange(quantity): + writeXMLObject(absoluteFolderDirectory, derivation, fileNames, target, xmlObject) + +def writeXMLObject(absoluteFolderDirectory, derivation, fileNames, target, xmlObject): + "Write one instance of the xmlObject." + extension = evaluate.getEvaluatedString(xmlObject.getFabricationExtension(), derivation.elementNode, 'extension') + fileNameRoot = derivation.fileName + if fileNameRoot == '': + fileNameRoot = evaluate.getEvaluatedString('', target, 'name') + fileNameRoot = evaluate.getEvaluatedString(fileNameRoot, target, 'id') + fileNameRoot += derivation.suffix + fileName = '%s.%s' % (fileNameRoot, extension) + suffixIndex = 2 + while fileName in fileNames: + fileName = '%s_%s.%s' % (fileNameRoot, suffixIndex, extension) + suffixIndex += 1 + absoluteFileName = os.path.join(absoluteFolderDirectory, fileName) + fileNames.append(fileName) + archive.makeDirectory(absoluteFolderDirectory) + if not derivation.writeMatrix: + xmlObject.matrix4X4 = matrix.Matrix() + print('The write tool generated the file:') + print(absoluteFileName) + archive.writeFileText(absoluteFileName, xmlObject.getFabricationText(derivation.addLayerTemplate)) + + +class WriteDerivation: + "Class to hold write variables." + def __init__(self, elementNode): + 'Set defaults.' + self.addLayerTemplate = evaluate.getEvaluatedBoolean(False, elementNode, 'addLayerTemplate') + self.elementNode = elementNode + self.fileName = evaluate.getEvaluatedString('', elementNode, 'file') + self.folderName = evaluate.getEvaluatedString('', elementNode, 'folder') + self.suffix = evaluate.getEvaluatedString('', elementNode, 'suffix') + self.targets = evaluate.getElementNodesByKey(elementNode, 'target') + self.writeMatrix = evaluate.getEvaluatedBoolean(True, elementNode, 'writeMatrix') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/__init__.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/__init__.py new file mode 100644 index 0000000..1121e8a --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 3 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/bevel.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/bevel.py new file mode 100644 index 0000000..78afcfd --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/bevel.py @@ -0,0 +1,71 @@ +""" +Add material to support overhang or remove material at the overhang angle. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalExecutionOrder = 20 + + +def getBevelPath( begin, center, close, end, radius ): + "Get bevel path." + beginComplex = begin.dropAxis() + centerComplex = center.dropAxis() + endComplex = end.dropAxis() + beginComplexSegmentLength = abs( centerComplex - beginComplex ) + endComplexSegmentLength = abs( centerComplex - endComplex ) + minimumRadius = lineation.getMinimumRadius( beginComplexSegmentLength, endComplexSegmentLength, radius ) + if minimumRadius <= close: + return [ center ] + beginBevel = center + minimumRadius / beginComplexSegmentLength * ( begin - center ) + endBevel = center + minimumRadius / endComplexSegmentLength * ( end - center ) + if radius > 0.0: + return [ beginBevel, endBevel ] + midpointComplex = 0.5 * ( beginBevel.dropAxis() + endBevel.dropAxis() ) + spikeComplex = centerComplex + centerComplex - midpointComplex + return [ beginBevel, Vector3( spikeComplex.real, spikeComplex.imag, center.z ), endBevel ] + +def getManipulatedPaths(close, elementNode, loop, prefix, sideLength): + "Get bevel loop." + if len(loop) < 3: + return [loop] + derivation = BevelDerivation(elementNode, prefix, sideLength) + if derivation.radius == 0.0: + return loop + bevelLoop = [] + for pointIndex in xrange(len(loop)): + begin = loop[(pointIndex + len(loop) - 1) % len(loop)] + center = loop[pointIndex] + end = loop[(pointIndex + 1) % len(loop)] + bevelLoop += getBevelPath(begin, center, close, end, derivation.radius) + return [euclidean.getLoopWithoutCloseSequentialPoints(close, bevelLoop)] + +def getNewDerivation(elementNode, prefix, sideLength): + 'Get new derivation.' + return BevelDerivation(elementNode, prefix, sideLength) + +def processElementNode(elementNode): + "Process the xml element." + lineation.processElementNodeByFunction(elementNode, getManipulatedPaths) + + +class BevelDerivation: + "Class to hold bevel variables." + def __init__(self, elementNode, prefix, sideLength): + 'Set defaults.' + self.radius = lineation.getFloatByPrefixSide(0.0, elementNode, prefix + 'radius', sideLength) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/convex.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/convex.py new file mode 100644 index 0000000..f3adbf2 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/convex.py @@ -0,0 +1,37 @@ +""" +Create outline. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities import euclidean + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__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) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/outline.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/outline.py new file mode 100644 index 0000000..9c27bc5 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/outline.py @@ -0,0 +1,53 @@ +""" +Create outline. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import intercircle + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalExecutionOrder = 80 + + +def getManipulatedPaths(close, elementNode, loop, prefix, sideLength): + "Get path with outline." + if len(loop) < 2: + return [loop] + derivation = OutlineDerivation(elementNode, prefix, sideLength) + loopComplex = euclidean.getComplexPath(loop) + if derivation.isClosed: + loopComplexes = intercircle.getAroundsFromLoop(loopComplex, derivation.radius) + else: + loopComplexes = intercircle.getAroundsFromPath(loopComplex, derivation.radius) + return euclidean.getVector3Paths(loopComplexes, loop[0].z) + +def getNewDerivation(elementNode, prefix, sideLength): + 'Get new derivation.' + return OutlineDerivation(elementNode, prefix, sideLength) + +def processElementNode(elementNode): + "Process the xml element." + lineation.processElementNodeByFunction(elementNode, getManipulatedPaths) + + +class OutlineDerivation: + "Class to hold outline variables." + def __init__(self, elementNode, prefix, sideLength): + 'Set defaults.' + self.isClosed = evaluate.getEvaluatedBoolean(False, elementNode, prefix + 'closed') + self.radius = evaluate.getEvaluatedFloat(setting.getEdgeWidth(elementNode), elementNode, prefix + 'radius') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/overhang.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/overhang.py new file mode 100644 index 0000000..c913e7d --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/overhang.py @@ -0,0 +1,397 @@ +""" +Add material to support overhang or remove material at the overhang angle. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalExecutionOrder = 100 + + +def addUnsupportedPointIndexes( alongAway ): + "Add the indexes of the unsupported points." + addedUnsupportedPointIndexes = [] + for pointIndex in xrange( len( alongAway.loop ) ): + point = alongAway.loop[pointIndex] + if pointIndex not in alongAway.unsupportedPointIndexes: + if not alongAway.getIsClockwisePointSupported(point): + alongAway.unsupportedPointIndexes.append( pointIndex ) + addedUnsupportedPointIndexes.append( pointIndex ) + for pointIndex in addedUnsupportedPointIndexes: + point = alongAway.loop[pointIndex] + point.y += alongAway.maximumYPlus + +def alterClockwiseSupportedPath( alongAway, elementNode ): + "Get clockwise path with overhangs carved out." + alongAway.bottomPoints = [] + alongAway.overhangSpan = setting.getOverhangSpan(elementNode) + maximumY = - 987654321.0 + minimumYPointIndex = 0 + for pointIndex in xrange( len( alongAway.loop ) ): + point = alongAway.loop[pointIndex] + if point.y < alongAway.loop[ minimumYPointIndex ].y: + minimumYPointIndex = pointIndex + maximumY = max( maximumY, point.y ) + alongAway.maximumYPlus = 2.0 * ( maximumY - alongAway.loop[ minimumYPointIndex ].y ) + alongAway.loop = euclidean.getAroundLoop( minimumYPointIndex, minimumYPointIndex, alongAway.loop ) + overhangClockwise = OverhangClockwise( alongAway ) + alongAway.unsupportedPointIndexes = [] + oldUnsupportedPointIndexesLength = - 987654321.0 + while len( alongAway.unsupportedPointIndexes ) > oldUnsupportedPointIndexesLength: + oldUnsupportedPointIndexesLength = len( alongAway.unsupportedPointIndexes ) + addUnsupportedPointIndexes( alongAway ) + for pointIndex in alongAway.unsupportedPointIndexes: + point = alongAway.loop[pointIndex] + point.y -= alongAway.maximumYPlus + alongAway.unsupportedPointIndexes.sort() + alongAway.unsupportedPointIndexLists = [] + oldUnsupportedPointIndex = - 987654321.0 + unsupportedPointIndexList = None + for unsupportedPointIndex in alongAway.unsupportedPointIndexes: + if unsupportedPointIndex > oldUnsupportedPointIndex + 1: + unsupportedPointIndexList = [] + alongAway.unsupportedPointIndexLists.append( unsupportedPointIndexList ) + oldUnsupportedPointIndex = unsupportedPointIndex + unsupportedPointIndexList.append( unsupportedPointIndex ) + alongAway.unsupportedPointIndexLists.reverse() + for unsupportedPointIndexList in alongAway.unsupportedPointIndexLists: + overhangClockwise.alterLoop( unsupportedPointIndexList ) + +def alterWiddershinsSupportedPath( alongAway, close ): + "Get widdershins path with overhangs filled in." + alongAway.bottomPoints = [] + alongAway.minimumY = getMinimumYByPath( alongAway.loop ) + for point in alongAway.loop: + if point.y - alongAway.minimumY < close: + alongAway.addToBottomPoints(point) + ascendingYPoints = alongAway.loop[:] + ascendingYPoints.sort( compareYAscending ) + overhangWiddershinsLeft = OverhangWiddershinsLeft( alongAway ) + overhangWiddershinsRight = OverhangWiddershinsRight( alongAway ) + for point in ascendingYPoints: + alterWiddershinsSupportedPathByPoint( alongAway, overhangWiddershinsLeft, overhangWiddershinsRight, point ) + +def alterWiddershinsSupportedPathByPoint( alongAway, overhangWiddershinsLeft, overhangWiddershinsRight, point ): + "Get widdershins path with overhangs filled in for point." + if alongAway.getIsWiddershinsPointSupported(point): + return + overhangWiddershins = overhangWiddershinsLeft + if overhangWiddershinsRight.getDistance() < overhangWiddershinsLeft.getDistance(): + overhangWiddershins = overhangWiddershinsRight + overhangWiddershins.alterLoop() + +def compareYAscending( point, pointOther ): + "Get comparison in order to sort points in ascending y." + if point.y < pointOther.y: + return - 1 + return int( point.y > pointOther.y ) + +def getManipulatedPaths(close, elementNode, loop, prefix, sideLength): + "Get path with overhangs removed or filled in." + if len(loop) < 3: + print('Warning, loop has less than three sides in getManipulatedPaths in overhang for:') + print(elementNode) + return [loop] + derivation = OverhangDerivation(elementNode, prefix) + overhangPlaneAngle = euclidean.getWiddershinsUnitPolar(0.5 * math.pi - derivation.overhangRadians) + if derivation.overhangInclinationRadians != 0.0: + overhangInclinationCosine = abs(math.cos(derivation.overhangInclinationRadians)) + if overhangInclinationCosine == 0.0: + return [loop] + imaginaryTimesCosine = overhangPlaneAngle.imag * overhangInclinationCosine + overhangPlaneAngle = euclidean.getNormalized(complex(overhangPlaneAngle.real, imaginaryTimesCosine)) + alongAway = AlongAway(loop, overhangPlaneAngle) + if euclidean.getIsWiddershinsByVector3(loop): + alterWiddershinsSupportedPath(alongAway, close) + else: + alterClockwiseSupportedPath(alongAway, elementNode) + return [euclidean.getLoopWithoutCloseSequentialPoints(close, alongAway.loop)] + +def getMinimumYByPath(path): + "Get path with overhangs removed or filled in." + minimumYByPath = path[0].y + for point in path: + minimumYByPath = min( minimumYByPath, point.y ) + return minimumYByPath + +def getNewDerivation(elementNode, prefix, sideLength): + 'Get new derivation.' + return OverhangDerivation(elementNode, prefix) + +def processElementNode(elementNode): + "Process the xml element." + lineation.processElementNodeByFunction(elementNode, getManipulatedPaths) + + +class AlongAway: + "Class to derive the path along the point and away from the point." + def __init__( self, loop, overhangPlaneAngle ): + "Initialize." + self.loop = loop + self.overhangPlaneAngle = overhangPlaneAngle + self.ySupport = - self.overhangPlaneAngle.imag + + def __repr__(self): + "Get the string representation of AlongAway." + return '%s' % ( self.overhangPlaneAngle ) + + def addToBottomPoints(self, point): + "Add point to bottom points and set y to minimumY." + self.bottomPoints.append(point) + point.y = self.minimumY + + def getIsClockwisePointSupported(self, point): + "Determine if the point on the clockwise loop is supported." + self.point = point + self.pointIndex = None + self.awayIndexes = [] + numberOfIntersectionsBelow = 0 + for pointIndex in xrange( len( self.loop ) ): + begin = self.loop[pointIndex] + end = self.loop[ (pointIndex + 1) % len( self.loop ) ] + if begin != point and end != point: + self.awayIndexes.append( pointIndex ) + yIntersection = euclidean.getYIntersectionIfExists( begin.dropAxis(), end.dropAxis(), point.x ) + if yIntersection != None: + numberOfIntersectionsBelow += ( yIntersection < point.y ) + if begin == point: + self.pointIndex = pointIndex + if numberOfIntersectionsBelow % 2 == 0: + return True + if self.pointIndex == None: + return True + if self.getIsPointSupportedBySegment( self.pointIndex - 1 + len( self.loop ) ): + return True + return self.getIsPointSupportedBySegment( self.pointIndex + 1 ) + + def getIsPointSupportedBySegment( self, endIndex ): + "Determine if the point on the widdershins loop is supported." + endComplex = self.loop[ ( endIndex % len( self.loop ) ) ].dropAxis() + endMinusPointComplex = euclidean.getNormalized( endComplex - self.point.dropAxis() ) + return endMinusPointComplex.imag < self.ySupport + + def getIsWiddershinsPointSupported(self, point): + "Determine if the point on the widdershins loop is supported." + if point.y <= self.minimumY: + return True + self.point = point + self.pointIndex = None + self.awayIndexes = [] + numberOfIntersectionsBelow = 0 + for pointIndex in xrange( len( self.loop ) ): + begin = self.loop[pointIndex] + end = self.loop[ (pointIndex + 1) % len( self.loop ) ] + if begin != point and end != point: + self.awayIndexes.append( pointIndex ) + yIntersection = euclidean.getYIntersectionIfExists( begin.dropAxis(), end.dropAxis(), point.x ) + if yIntersection != None: + numberOfIntersectionsBelow += ( yIntersection < point.y ) + if begin == point: + self.pointIndex = pointIndex + if numberOfIntersectionsBelow % 2 == 1: + return True + if self.pointIndex == None: + return True + if self.getIsPointSupportedBySegment( self.pointIndex - 1 + len( self.loop ) ): + return True + return self.getIsPointSupportedBySegment( self.pointIndex + 1 ) + + +class OverhangClockwise: + "Class to get the intersection up from the point." + def __init__( self, alongAway ): + "Initialize." + self.alongAway = alongAway + self.halfRiseOverWidth = 0.5 * alongAway.overhangPlaneAngle.imag / alongAway.overhangPlaneAngle.real + self.widthOverRise = alongAway.overhangPlaneAngle.real / alongAway.overhangPlaneAngle.imag + + def __repr__(self): + "Get the string representation of OverhangClockwise." + return '%s' % ( self.intersectionPlaneAngle ) + + def alterLoop( self, unsupportedPointIndexes ): + "Alter alongAway loop." + unsupportedBeginIndex = unsupportedPointIndexes[0] + unsupportedEndIndex = unsupportedPointIndexes[-1] + beginIndex = unsupportedBeginIndex - 1 + endIndex = unsupportedEndIndex + 1 + begin = self.alongAway.loop[ beginIndex ] + end = self.alongAway.loop[ endIndex ] + truncatedOverhangSpan = self.alongAway.overhangSpan + width = end.x - begin.x + heightDifference = abs( end.y - begin.y ) + remainingWidth = width - self.widthOverRise * heightDifference + if remainingWidth <= 0.0: + del self.alongAway.loop[ unsupportedBeginIndex : endIndex ] + return + highest = begin + supportX = begin.x + remainingWidth + if end.y > begin.y: + highest = end + supportX = end.x - remainingWidth + tipY = highest.y + self.halfRiseOverWidth * remainingWidth + highestBetween = - 987654321.0 + for unsupportedPointIndex in unsupportedPointIndexes: + highestBetween = max( highestBetween, self.alongAway.loop[ unsupportedPointIndex ].y ) + if highestBetween > highest.y: + truncatedOverhangSpan = 0.0 + if highestBetween < tipY: + below = tipY - highestBetween + truncatedOverhangSpan = min( self.alongAway.overhangSpan, below / self.halfRiseOverWidth ) + truncatedOverhangSpanRadius = 0.5 * truncatedOverhangSpan + if remainingWidth <= truncatedOverhangSpan: + supportPoint = Vector3( supportX, highest.y, highest.z ) + self.alongAway.loop[ unsupportedBeginIndex : endIndex ] = [ supportPoint ] + return + midSupportX = 0.5 * ( supportX + highest.x ) + if truncatedOverhangSpan <= 0.0: + supportPoint = Vector3( midSupportX, tipY, highest.z ) + self.alongAway.loop[ unsupportedBeginIndex : endIndex ] = [ supportPoint ] + return + supportXLeft = midSupportX - truncatedOverhangSpanRadius + supportXRight = midSupportX + truncatedOverhangSpanRadius + supportY = tipY - self.halfRiseOverWidth * truncatedOverhangSpan + supportPoints = [ Vector3( supportXLeft, supportY, highest.z ), Vector3( supportXRight, supportY, highest.z ) ] + self.alongAway.loop[ unsupportedBeginIndex : endIndex ] = supportPoints + + +class OverhangDerivation: + "Class to hold overhang variables." + def __init__(self, elementNode, prefix): + 'Set defaults.' + self.overhangRadians = setting.getOverhangRadians(elementNode) + self.overhangInclinationRadians = math.radians(evaluate.getEvaluatedFloat(0.0, elementNode, prefix + 'inclination')) + + +class OverhangWiddershinsLeft: + "Class to get the intersection from the point down to the left." + def __init__( self, alongAway ): + "Initialize." + self.alongAway = alongAway + self.intersectionPlaneAngle = - alongAway.overhangPlaneAngle + self.setRatios() + + def __repr__(self): + "Get the string representation of OverhangWiddershins." + return '%s' % ( self.intersectionPlaneAngle ) + + def alterLoop(self): + "Alter alongAway loop." + insertedPoint = self.alongAway.point.copy() + if self.closestXIntersectionIndex != None: + self.alongAway.loop = self.getIntersectLoop() + intersectionRelativeComplex = self.closestXDistance * self.intersectionPlaneAngle + intersectionPoint = insertedPoint + Vector3( intersectionRelativeComplex.real, intersectionRelativeComplex.imag ) + self.alongAway.loop.append( intersectionPoint ) + return + if self.closestBottomPoint == None: + return + if self.closestBottomPoint not in self.alongAway.loop: + return + insertedPoint.x = self.bottomX + closestBottomIndex = self.alongAway.loop.index( self.closestBottomPoint ) + self.alongAway.addToBottomPoints( insertedPoint ) + self.alongAway.loop = self.getBottomLoop( closestBottomIndex, insertedPoint ) + self.alongAway.loop.append( insertedPoint ) + + def getBottomLoop( self, closestBottomIndex, insertedPoint ): + "Get loop around bottom." + endIndex = closestBottomIndex + len( self.alongAway.loop ) + 1 + return euclidean.getAroundLoop( self.alongAway.pointIndex, endIndex, self.alongAway.loop ) + + def getDistance(self): + "Get distance between point and closest intersection or bottom point along line." + self.pointMinusBottomY = self.alongAway.point.y - self.alongAway.minimumY + self.diagonalDistance = self.pointMinusBottomY * self.diagonalRatio + if self.alongAway.pointIndex == None: + return self.getDistanceToBottom() + rotatedLoop = euclidean.getRotatedComplexes( self.intersectionYMirror, euclidean.getComplexPath( self.alongAway.loop ) ) + rotatedPointComplex = rotatedLoop[ self.alongAway.pointIndex ] + beginX = rotatedPointComplex.real + endX = beginX + self.diagonalDistance + self.diagonalDistance + xIntersectionIndexList = [] + for pointIndex in self.alongAway.awayIndexes: + beginComplex = rotatedLoop[pointIndex] + endComplex = rotatedLoop[ (pointIndex + 1) % len( rotatedLoop ) ] + xIntersection = euclidean.getXIntersectionIfExists( beginComplex, endComplex, rotatedPointComplex.imag ) + if xIntersection != None: + if xIntersection >= beginX and xIntersection < endX: + xIntersectionIndexList.append( euclidean.XIntersectionIndex( pointIndex, xIntersection ) ) + self.closestXDistance = 987654321.0 + self.closestXIntersectionIndex = None + for xIntersectionIndex in xIntersectionIndexList: + xDistance = abs( xIntersectionIndex.x - beginX ) + if xDistance < self.closestXDistance: + self.closestXIntersectionIndex = xIntersectionIndex + self.closestXDistance = xDistance + if self.closestXIntersectionIndex != None: + return self.closestXDistance + return self.getDistanceToBottom() + + def getDistanceToBottom(self): + "Get distance between point and closest bottom point along line." + self.bottomX = self.alongAway.point.x + self.pointMinusBottomY * self.xRatio + self.closestBottomPoint = None + closestDistanceX = 987654321.0 + for point in self.alongAway.bottomPoints: + distanceX = abs( point.x - self.bottomX ) + if self.getIsOnside(point.x): + if distanceX < closestDistanceX: + closestDistanceX = distanceX + self.closestBottomPoint = point + return closestDistanceX + self.diagonalDistance + + def getIntersectLoop(self): + "Get intersection loop." + endIndex = self.closestXIntersectionIndex.index + len( self.alongAway.loop ) + 1 + return euclidean.getAroundLoop( self.alongAway.pointIndex, endIndex, self.alongAway.loop ) + + def getIsOnside( self, x ): + "Determine if x is on the side along the direction of the intersection line." + return x <= self.alongAway.point.x + + def setRatios(self): + "Set ratios." + self.diagonalRatio = 1.0 / abs( self.intersectionPlaneAngle.imag ) + self.intersectionYMirror = complex( self.intersectionPlaneAngle.real, - self.intersectionPlaneAngle.imag ) + self.xRatio = self.intersectionPlaneAngle.real / abs( self.intersectionPlaneAngle.imag ) + + +class OverhangWiddershinsRight( OverhangWiddershinsLeft ): + "Class to get the intersection from the point down to the right." + def __init__( self, alongAway ): + "Initialize." + self.alongAway = alongAway + self.intersectionPlaneAngle = complex( alongAway.overhangPlaneAngle.real, - alongAway.overhangPlaneAngle.imag ) + self.setRatios() + + def getBottomLoop( self, closestBottomIndex, insertedPoint ): + "Get loop around bottom." + endIndex = self.alongAway.pointIndex + len( self.alongAway.loop ) + 1 + return euclidean.getAroundLoop( closestBottomIndex, endIndex, self.alongAway.loop ) + + def getIntersectLoop(self): + "Get intersection loop." + beginIndex = self.closestXIntersectionIndex.index + len( self.alongAway.loop ) + 1 + endIndex = self.alongAway.pointIndex + len( self.alongAway.loop ) + 1 + return euclidean.getAroundLoop( beginIndex, endIndex, self.alongAway.loop ) + + def getIsOnside( self, x ): + "Determine if x is on the side along the direction of the intersection line." + return x >= self.alongAway.point.x diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/round.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/round.py new file mode 100644 index 0000000..ef96224 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/round.py @@ -0,0 +1,94 @@ +""" +Add material to support overhang or remove material at the overhang angle. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalExecutionOrder = 40 + + +def getManipulatedPaths(close, elementNode, loop, prefix, sideLength): + "Get round loop." + if len(loop) < 3: + return [loop] + derivation = RoundDerivation(elementNode, prefix, sideLength) + if derivation.radius == 0.0: + return loop + roundLoop = [] + sidesPerRadian = 0.5 / math.pi * evaluate.getSidesMinimumThreeBasedOnPrecision(elementNode, sideLength) + for pointIndex in xrange(len(loop)): + begin = loop[(pointIndex + len(loop) - 1) % len(loop)] + center = loop[pointIndex] + end = loop[(pointIndex + 1) % len(loop)] + roundLoop += getRoundPath(begin, center, close, end, derivation.radius, sidesPerRadian) + return [euclidean.getLoopWithoutCloseSequentialPoints(close, roundLoop)] + +def getNewDerivation(elementNode, prefix, sideLength): + 'Get new derivation.' + return RoundDerivation(elementNode, prefix, sideLength) + +def getRoundPath( begin, center, close, end, radius, sidesPerRadian ): + "Get round path." + beginComplex = begin.dropAxis() + centerComplex = center.dropAxis() + endComplex = end.dropAxis() + beginComplexSegmentLength = abs( centerComplex - beginComplex ) + endComplexSegmentLength = abs( centerComplex - endComplex ) + minimumRadius = lineation.getMinimumRadius( beginComplexSegmentLength, endComplexSegmentLength, radius ) + if minimumRadius <= close: + return [ center ] + beginBevel = center + minimumRadius / beginComplexSegmentLength * ( begin - center ) + endBevel = center + minimumRadius / endComplexSegmentLength * ( end - center ) + beginBevelComplex = beginBevel.dropAxis() + endBevelComplex = endBevel.dropAxis() + midpointComplex = 0.5 * ( beginBevelComplex + endBevelComplex ) + if radius < 0.0: + centerComplex = midpointComplex + midpointComplex - centerComplex + midpointMinusCenterComplex = midpointComplex - centerComplex + midpointCenterLength = abs( midpointMinusCenterComplex ) + midpointEndLength = abs( midpointComplex - endBevelComplex ) + midpointCircleCenterLength = midpointEndLength * midpointEndLength / midpointCenterLength + circleRadius = math.sqrt( midpointCircleCenterLength * midpointCircleCenterLength + midpointEndLength * midpointEndLength ) + circleCenterComplex = midpointComplex + midpointMinusCenterComplex * midpointCircleCenterLength / midpointCenterLength + circleCenter = Vector3( circleCenterComplex.real, circleCenterComplex.imag, center.z ) + endMinusCircleCenterComplex = endBevelComplex - circleCenterComplex + beginMinusCircleCenter = beginBevel - circleCenter + beginMinusCircleCenterComplex = beginMinusCircleCenter.dropAxis() + angleDifference = euclidean.getAngleDifferenceByComplex( endMinusCircleCenterComplex, beginMinusCircleCenterComplex ) + steps = int( math.ceil( abs( angleDifference ) * sidesPerRadian ) ) + stepPlaneAngle = euclidean.getWiddershinsUnitPolar( angleDifference / float( steps ) ) + deltaZStep = ( end.z - begin.z ) / float( steps ) + roundPath = [ beginBevel ] + for step in xrange( 1, steps ): + beginMinusCircleCenterComplex = beginMinusCircleCenterComplex * stepPlaneAngle + arcPointComplex = circleCenterComplex + beginMinusCircleCenterComplex + arcPoint = Vector3( arcPointComplex.real, arcPointComplex.imag, begin.z + deltaZStep * step ) + roundPath.append( arcPoint ) + return roundPath + [ endBevel ] + +def processElementNode(elementNode): + "Process the xml element." + lineation.processElementNodeByFunction(elementNode, getManipulatedPaths) + + +class RoundDerivation: + "Class to hold round variables." + def __init__(self, elementNode, prefix, sideLength): + 'Set defaults.' + self.radius = lineation.getFloatByPrefixSide(0.0, elementNode, prefix + 'radius', sideLength) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/segment.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/segment.py new file mode 100644 index 0000000..57ce3a7 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/segment.py @@ -0,0 +1,166 @@ +""" +Add material to support overhang or remove material at the overhang angle. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalExecutionOrder = 60 + + +def getManipulatedPaths(close, elementNode, loop, prefix, sideLength): + "Get segment loop." + if len(loop) < 3: + return [loop] + derivation = SegmentDerivation(elementNode, prefix) + if derivation.path == getSegmentPathDefault(): + return [loop] + path = getXNormalizedVector3Path(derivation.path) + if euclidean.getIsWiddershinsByVector3(loop): + path = path[: : -1] + for point in path: + point.x = 1.0 - point.x + if derivation.center == None: + point.y = - point.y + segmentLoop = [] + startEnd = StartEnd(elementNode, len(loop), prefix) + for pointIndex in xrange(len(loop)): + if pointIndex >= startEnd.start and pointIndex < startEnd.end: + segmentLoop += getSegmentPath(derivation.center, loop, path, pointIndex) + else: + segmentLoop.append(loop[pointIndex]) + return [euclidean.getLoopWithoutCloseSequentialPoints( close, segmentLoop)] + +def getNewDerivation(elementNode, prefix, sideLength): + 'Get new derivation.' + return SegmentDerivation(elementNode, prefix) + +def getRadialPath(begin, center, end, path): + "Get radial path." + beginComplex = begin.dropAxis() + endComplex = end.dropAxis() + centerComplex = center.dropAxis() + beginMinusCenterComplex = beginComplex - centerComplex + endMinusCenterComplex = endComplex - centerComplex + beginMinusCenterComplexRadius = abs( beginMinusCenterComplex ) + endMinusCenterComplexRadius = abs( endMinusCenterComplex ) + if beginMinusCenterComplexRadius == 0.0 or endMinusCenterComplexRadius == 0.0: + return [ begin ] + beginMinusCenterComplex /= beginMinusCenterComplexRadius + endMinusCenterComplex /= endMinusCenterComplexRadius + angleDifference = euclidean.getAngleDifferenceByComplex( endMinusCenterComplex, beginMinusCenterComplex ) + radialPath = [] + for point in path: + weightEnd = point.x + weightBegin = 1.0 - weightEnd + weightedRadius = beginMinusCenterComplexRadius * weightBegin + endMinusCenterComplexRadius * weightEnd * ( 1.0 + point.y ) + radialComplex = weightedRadius * euclidean.getWiddershinsUnitPolar( angleDifference * point.x ) * beginMinusCenterComplex + polygonPoint = center + Vector3( radialComplex.real, radialComplex.imag, point.z ) + radialPath.append( polygonPoint ) + return radialPath + +def getSegmentPath(center, loop, path, pointIndex): + "Get segment path." + centerBegin = loop[pointIndex] + centerEnd = loop[(pointIndex + 1) % len(loop)] + centerEndMinusBegin = centerEnd - centerBegin + if abs( centerEndMinusBegin ) <= 0.0: + return [ centerBegin ] + if center != None: + return getRadialPath(centerBegin, center, centerEnd, path) + begin = loop[(pointIndex + len(loop) - 1) % len(loop)] + end = loop[(pointIndex + 2) % len(loop)] + return getWedgePath(begin, centerBegin, centerEnd, centerEndMinusBegin, end, path) + +def getSegmentPathDefault(): + "Get segment path default." + return [Vector3(), Vector3(0.0, 1.0)] + +def getWedgePath( begin, centerBegin, centerEnd, centerEndMinusBegin, end, path ): + "Get segment path." + beginComplex = begin.dropAxis() + centerBeginComplex = centerBegin.dropAxis() + centerEndComplex = centerEnd.dropAxis() + endComplex = end.dropAxis() + wedgePath = [] + centerBeginMinusBeginComplex = euclidean.getNormalized( centerBeginComplex - beginComplex ) + centerEndMinusCenterBeginComplexOriginal = centerEndComplex - centerBeginComplex + centerEndMinusCenterBeginComplexLength = abs( centerEndMinusCenterBeginComplexOriginal ) + if centerEndMinusCenterBeginComplexLength <= 0.0: + return [ centerBegin ] + centerEndMinusCenterBeginComplex = centerEndMinusCenterBeginComplexOriginal / centerEndMinusCenterBeginComplexLength + endMinusCenterEndComplex = euclidean.getNormalized( endComplex - centerEndComplex ) + widdershinsBegin = getWiddershinsAverageByVector3( centerBeginMinusBeginComplex, centerEndMinusCenterBeginComplex ) + widdershinsEnd = getWiddershinsAverageByVector3( centerEndMinusCenterBeginComplex, endMinusCenterEndComplex ) + for point in path: + weightEnd = point.x + weightBegin = 1.0 - weightEnd + polygonPoint = centerBegin + centerEndMinusBegin * point.x + weightedWiddershins = widdershinsBegin * weightBegin + widdershinsEnd * weightEnd + polygonPoint += weightedWiddershins * point.y * centerEndMinusCenterBeginComplexLength + polygonPoint.z += point.z + wedgePath.append( polygonPoint ) + return wedgePath + +def getWiddershinsAverageByVector3( centerMinusBeginComplex, endMinusCenterComplex ): + "Get the normalized average of the widdershins vectors." + centerMinusBeginWiddershins = Vector3( - centerMinusBeginComplex.imag, centerMinusBeginComplex.real ) + endMinusCenterWiddershins = Vector3( - endMinusCenterComplex.imag, endMinusCenterComplex.real ) + return ( centerMinusBeginWiddershins + endMinusCenterWiddershins ).getNormalized() + +def getXNormalizedVector3Path(path): + "Get path where the x ranges from 0 to 1." + if len(path) < 1: + return path + minimumX = path[0].x + for point in path[1 :]: + minimumX = min( minimumX, point.x ) + for point in path: + point.x -= minimumX + maximumX = path[0].x + for point in path[1 :]: + maximumX = max( maximumX, point.x ) + for point in path: + point.x /= maximumX + return path + +def processElementNode(elementNode): + "Process the xml element." + lineation.processElementNodeByFunction(elementNode, getManipulatedPaths) + + +class SegmentDerivation: + "Class to hold segment variables." + def __init__(self, elementNode, prefix): + 'Set defaults.' + self.center = evaluate.getVector3ByPrefix(None, elementNode, prefix + 'center') + self.path = evaluate.getPathByPrefix(elementNode, getSegmentPathDefault(), prefix) + + +class StartEnd: + 'Class to get a start through end range.' + def __init__(self, elementNode, modulo, prefix): + "Initialize." + self.start = evaluate.getEvaluatedInt(0, elementNode, prefix + 'start') + self.extent = evaluate.getEvaluatedInt(modulo - self.start, elementNode, prefix + 'extent') + self.end = evaluate.getEvaluatedInt(self.start + self.extent, elementNode, prefix + 'end') + self.revolutions = evaluate.getEvaluatedInt(1, elementNode, prefix + 'revolutions') + if self.revolutions > 1: + self.end += modulo * (self.revolutions - 1) + + def __repr__(self): + "Get the string representation of this StartEnd." + return '%s, %s, %s' % (self.start, self.end, self.revolutions) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/wedge.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/wedge.py new file mode 100644 index 0000000..70800f1 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_paths/wedge.py @@ -0,0 +1,43 @@ +""" +Add material to support overhang or remove material at the overhang angle. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.vector3 import Vector3 + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalExecutionOrder = -200 + + +def getManipulatedPaths(close, elementNode, loop, prefix, sideLength): + "Get wedge loop." + derivation = WedgeDerivation(elementNode, prefix) + loop.append(derivation.center) + return [loop] + +def getNewDerivation(elementNode, prefix, sideLength): + 'Get new derivation.' + return WedgeDerivation(elementNode, prefix) + +def processElementNode(elementNode): + "Process the xml element." + lineation.processElementNodeByFunction(elementNode, getManipulatedPaths) + + +class WedgeDerivation: + "Class to hold wedge variables." + def __init__(self, elementNode, prefix): + 'Set defaults.' + self.center = evaluate.getVector3ByPrefix(Vector3(), elementNode, prefix + 'center') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_shapes/__init__.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_shapes/__init__.py new file mode 100644 index 0000000..1121e8a --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_shapes/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 3 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_shapes/_bottom.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_shapes/_bottom.py new file mode 100644 index 0000000..7e10912 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_shapes/_bottom.py @@ -0,0 +1,104 @@ +""" +Boolean geometry bottom. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import solid +from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting +from fabmetheus_utilities.geometry.geometry_utilities import boolean_geometry +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities import euclidean + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalExecutionOrder = 400 + + +def bottomElementNode(derivation, target): + "Bottom target." + xmlObject = target.xmlObject + if xmlObject == None: + print('Warning, bottomTarget in bottom could not get xmlObject for:') + print(target) + print(derivation.elementNode) + return + targetMatrix = matrix.getBranchMatrixSetElementNode(target) + lift = derivation.altitude + transformedPaths = xmlObject.getTransformedPaths() + if len(transformedPaths) > 0: + lift += derivation.getAdditionalPathLift() - euclidean.getBottomByPaths(transformedPaths) + else: + lift -= boolean_geometry.getMinimumZ(xmlObject) + targetMatrix.tetragrid = matrix.getIdentityTetragrid(targetMatrix.tetragrid) + targetMatrix.tetragrid[2][3] += lift + matrix.setElementNodeDictionaryMatrix(target, targetMatrix) + +def getManipulatedGeometryOutput(elementNode, geometryOutput, prefix): + 'Get bottomed geometryOutput.' + derivation = BottomDerivation(elementNode, prefix) + copyShallow = elementNode.getCopyShallow() + solid.processElementNodeByGeometry(copyShallow, geometryOutput) + targetMatrix = matrix.getBranchMatrixSetElementNode(elementNode) + matrix.setElementNodeDictionaryMatrix(copyShallow, targetMatrix) + minimumZ = boolean_geometry.getMinimumZ(copyShallow.xmlObject) + copyShallow.parentNode.xmlObject.archivableObjects.remove(copyShallow.xmlObject) + lift = derivation.altitude - minimumZ + vertexes = matrix.getVertexes(geometryOutput) + for vertex in vertexes: + vertex.z += lift + return geometryOutput + +def getManipulatedPaths(close, elementNode, loop, prefix, sideLength): + 'Get flipped paths.' + if len(loop) < 1: + return [[]] + derivation = BottomDerivation(elementNode, prefix) + targetMatrix = matrix.getBranchMatrixSetElementNode(elementNode) + transformedLoop = matrix.getTransformedVector3s(matrix.getIdentityTetragrid(targetMatrix.tetragrid), loop) + lift = derivation.altitude + derivation.getAdditionalPathLift() - euclidean.getBottomByPath(transformedLoop) + for point in loop: + point.z += lift + return [loop] + +def getNewDerivation(elementNode, prefix, sideLength): + 'Get new derivation.' + return BottomDerivation(elementNode, '') + +def processElementNode(elementNode): + "Process the xml element." + processElementNodeByDerivation(None, elementNode) + +def processElementNodeByDerivation(derivation, elementNode): + 'Process the xml element by derivation.' + if derivation == None: + derivation = BottomDerivation(elementNode, '') + targets = evaluate.getElementNodesByKey(elementNode, 'target') + if len(targets) < 1: + print('Warning, processElementNode in bottom could not get targets for:') + print(elementNode) + return + for target in targets: + bottomElementNode(derivation, target) + + +class BottomDerivation: + "Class to hold bottom variables." + def __init__(self, elementNode, prefix): + 'Set defaults.' + self.altitude = evaluate.getEvaluatedFloat(0.0, elementNode, prefix + 'altitude') + self.elementNode = elementNode + self.liftPath = evaluate.getEvaluatedBoolean(True, elementNode, prefix + 'liftPath') + + def getAdditionalPathLift(self): + "Get path lift." + return 0.5 * setting.getLayerHeight(self.elementNode) * float(self.liftPath) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_shapes/_inset.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_shapes/_inset.py new file mode 100644 index 0000000..6e9e2de --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_shapes/_inset.py @@ -0,0 +1,85 @@ +""" +Create inset. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.creation import solid +from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting +from fabmetheus_utilities.geometry.geometry_utilities import boolean_solid +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3index import Vector3Index +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import intercircle +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalExecutionOrder = 80 + + +def getManipulatedGeometryOutput(elementNode, geometryOutput, prefix): + 'Get inset geometryOutput.' + derivation = InsetDerivation(elementNode, prefix) + if derivation.radius == 0.0: + return geometryOutput + halfLayerHeight = 0.5 * derivation.radius + importRadius = 0.5 * derivation.radius * setting.getImportCoarseness(elementNode) + loopLayers = solid.getLoopLayersSetCopy(elementNode, geometryOutput, importRadius, derivation.radius) + triangleAltitude = math.sqrt(0.75) * derivation.radius + loops = [] + vertexes = [] + for loopLayerIndex in xrange(1, len(loopLayers), 2): + loopLayer = loopLayers[loopLayerIndex] + loopLayer.loops[0] = intercircle.getLargestInsetLoopFromLoop(loopLayer.loops[0], triangleAltitude) + for loopLayerIndex in xrange(0, len(loopLayers), 2): + loopLayer = loopLayers[loopLayerIndex] + loopLists = [[solid.getLoopOrEmpty(loopLayerIndex - 2, loopLayers)]] + loopLists.append([solid.getLoopOrEmpty(loopLayerIndex - 1, loopLayers)]) + loopLists.append([intercircle.getLargestInsetLoopFromLoop(loopLayer.loops[0], derivation.radius)]) + if evaluate.getEvaluatedBoolean(True, elementNode, prefix + 'insetTop'): + loopLists.append([solid.getLoopOrEmpty(loopLayerIndex + 1, loopLayers)]) + loopLists.append([solid.getLoopOrEmpty(loopLayerIndex + 2, loopLayers)]) + largestLoop = euclidean.getLargestLoop(boolean_solid.getLoopsIntersection(importRadius, loopLists)) + triangle_mesh.addVector3Loop(largestLoop, loops, vertexes, loopLayer.z) + if evaluate.getEvaluatedBoolean(False, elementNode, prefix + 'addExtraTopLayer') and len(loops) > 0: + topLoop = loops[-1] + vector3Loop = [] + loops.append(vector3Loop) + z = topLoop[0].z + derivation.radius + for point in topLoop: + vector3Index = Vector3Index(len(vertexes), point.x, point.y, z) + vector3Loop.append(vector3Index) + vertexes.append(vector3Index) + return triangle_mesh.getMeldedPillarOutput(loops) + +def getManipulatedPaths(close, elementNode, loop, prefix, sideLength): + "Get inset path." + derivation = InsetDerivation(elementNode, prefix) + return intercircle.getInsetLoopsFromVector3Loop(loop, derivation.radius) + +def getNewDerivation(elementNode, prefix, sideLength): + 'Get new derivation.' + return OutsetDerivation(elementNode, prefix) + +def processElementNode(elementNode): + 'Process the xml element.' + solid.processElementNodeByFunctionPair(elementNode, getManipulatedGeometryOutput, getManipulatedPaths) + + +class InsetDerivation: + "Class to hold inset variables." + def __init__(self, elementNode, prefix): + 'Set defaults.' + self.radius = evaluate.getEvaluatedFloat(2.0 * setting.getEdgeWidth(elementNode), elementNode, prefix + 'radius') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_shapes/_outset.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_shapes/_outset.py new file mode 100644 index 0000000..6217214 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_shapes/_outset.py @@ -0,0 +1,78 @@ +""" +Create inset. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.creation import solid +from fabmetheus_utilities.geometry.geometry_utilities.evaluate_elements import setting +from fabmetheus_utilities.geometry.geometry_utilities import boolean_solid +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3index import Vector3Index +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import intercircle +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalExecutionOrder = 80 + + +def getManipulatedGeometryOutput(elementNode, geometryOutput, prefix): + 'Get outset geometryOutput.' + derivation = OutsetDerivation(elementNode, prefix) + if derivation.radius == 0.0: + return geometryOutput + halfLayerHeight = 0.5 * derivation.radius + importRadius = 0.5 * derivation.radius * setting.getImportCoarseness(elementNode) + loopLayers = solid.getLoopLayersSetCopy(elementNode, geometryOutput, importRadius, derivation.radius) + if len(loopLayers) == 0: + return triangle_mesh.getMeldedPillarOutput([]) + triangleAltitude = math.sqrt(0.75) * derivation.radius + loops = [] + vertexes = [] + for loopLayerIndex in xrange(1, len(loopLayers), 2): + loopLayer = loopLayers[loopLayerIndex] + loopLayer.loops[0] = intercircle.getLargestInsetLoopFromLoop(loopLayer.loops[0], triangleAltitude) + z = loopLayers[0].z - derivation.radius + for loopIndex in xrange(-2, len(loopLayers) + 2, 2): + loopLists = [[solid.getLoopOrEmpty(loopIndex - 2, loopLayers)]] + loopLists.append([solid.getLoopOrEmpty(loopIndex - 1, loopLayers)]) + loopLists.append([intercircle.getLargestInsetLoopFromLoop(solid.getLoopOrEmpty(loopIndex, loopLayers), -derivation.radius)]) + loopLists.append([solid.getLoopOrEmpty(loopIndex + 1, loopLayers)]) + loopLists.append([solid.getLoopOrEmpty(loopIndex + 2, loopLayers)]) + largestLoop = euclidean.getLargestLoop(boolean_solid.getLoopsUnion(importRadius, loopLists)) + triangle_mesh.addVector3Loop(largestLoop, loops, vertexes, z) + z += derivation.radius + return triangle_mesh.getMeldedPillarOutput(loops) + +def getManipulatedPaths(close, elementNode, loop, prefix, sideLength): + "Get outset path." + derivation = OutsetDerivation(elementNode, prefix) + return intercircle.getInsetLoopsFromVector3Loop(loop, -derivation.radius) + +def getNewDerivation(elementNode, prefix, sideLength): + 'Get new derivation.' + return OutsetDerivation(elementNode, prefix) + +def processElementNode(elementNode): + 'Process the xml element.' + solid.processElementNodeByFunctionPair(elementNode, getManipulatedGeometryOutput, getManipulatedPaths) + + +class OutsetDerivation: + "Class to hold outset variables." + def __init__(self, elementNode, prefix): + 'Set defaults.' + self.radius = evaluate.getEvaluatedFloat(2.0 * setting.getEdgeWidth(elementNode), elementNode, prefix + 'radius') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_shapes/equation.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_shapes/equation.py new file mode 100644 index 0000000..76b610b --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_shapes/equation.py @@ -0,0 +1,116 @@ +""" +Equation for vertexes. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities import euclidean +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalExecutionOrder = -100 + + +def equate(point, returnValue): + "Get equation for rectangular." + point.setToVector3(evaluate.getVector3ByDictionaryListValue(returnValue, point)) + +def equatePoints(elementNode, points, prefix, revolutions): + "Equate the points." + derivation = EquationDerivation(elementNode, prefix) + for equationResult in derivation.equationResults: + for point in points: + returnValue = equationResult.getReturnValue(point, revolutions) + if returnValue == None: + print('Warning, returnValue in alterVertexesByEquation in equation is None for:') + print(point) + print(elementNode) + else: + equationResult.equationFunction(point, returnValue) + +def equateX(point, returnValue): + "Get equation for rectangular x." + point.x = returnValue + +def equateY(point, returnValue): + "Get equation for rectangular y." + point.y = returnValue + +def equateZ(point, returnValue): + "Get equation for rectangular z." + point.z = returnValue + +def getManipulatedGeometryOutput(elementNode, geometryOutput, prefix): + "Get equated geometryOutput." + equatePoints(elementNode, matrix.getVertexes(geometryOutput), prefix, None) + return geometryOutput + +def getManipulatedPaths(close, elementNode, loop, prefix, sideLength): + "Get equated paths." + equatePoints(elementNode, loop, prefix, 0.0) + return [loop] + +def getNewDerivation(elementNode, prefix, sideLength): + 'Get new derivation.' + return EquationDerivation(elementNode, prefix) + + +class EquationDerivation: + "Class to hold equation variables." + def __init__(self, elementNode, prefix): + 'Set defaults.' + self.equationResults = [] + self.addEquationResult(elementNode, equate, prefix) + self.addEquationResult(elementNode, equateX, prefix) + self.addEquationResult(elementNode, equateY, prefix) + self.addEquationResult(elementNode, equateZ, prefix) + + def addEquationResult(self, elementNode, equationFunction, prefix): + 'Add equation result to equationResults.' + prefixedEquationName = prefix + equationFunction.__name__[ len('equate') : ].replace('Dot', '.').lower() + if prefixedEquationName in elementNode.attributes: + self.equationResults.append(EquationResult(elementNode, equationFunction, prefixedEquationName)) + + +class EquationResult: + "Class to get equation results." + def __init__(self, elementNode, equationFunction, key): + "Initialize." + self.distance = 0.0 + elementNode.xmlObject = evaluate.getEvaluatorSplitWords(elementNode.attributes[key]) + self.equationFunction = equationFunction + self.function = evaluate.Function(elementNode) + self.points = [] + + def getReturnValue(self, point, revolutions): + "Get return value." + if self.function == None: + return point + self.function.localDictionary['azimuth'] = math.degrees(math.atan2(point.y, point.x)) + if len(self.points) > 0: + self.distance += abs(point - self.points[-1]) + self.function.localDictionary['distance'] = self.distance + self.function.localDictionary['radius'] = abs(point.dropAxis()) + if revolutions != None: + if len( self.points ) > 0: + revolutions += 0.5 / math.pi * euclidean.getAngleAroundZAxisDifference(point, self.points[-1]) + self.function.localDictionary['revolutions'] = revolutions + self.function.localDictionary['vertex'] = point + self.function.localDictionary['vertexes'] = self.points + self.function.localDictionary['vertexindex'] = len(self.points) + self.function.localDictionary['x'] = point.x + self.function.localDictionary['y'] = point.y + self.function.localDictionary['z'] = point.z + self.points.append(point) + return self.function.getReturnValueWithoutDeletion() diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_shapes/flip.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_shapes/flip.py new file mode 100644 index 0000000..3212552 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_shapes/flip.py @@ -0,0 +1,92 @@ +""" +Add material to support overhang or remove material at the overhang angle. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import solid +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities.vector3 import Vector3 + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalExecutionOrder = 200 + + +# http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=269576 +# http://www.opengl.org/resources/code/samples/sig99/advanced99/notes/node159.html +# m.a00 = -2 * norm.x * norm.x + 1; +# m.a10 = -2 * norm.y * norm.x; +# m.a20 = -2 * norm.z * norm.x; +# m.a30 = 0; + +# m.a01 = -2 * norm.x * norm.y; +# m.a11 = -2 * norm.y * norm.y + 1; +# m.a21 = -2 * norm.z * norm.y; +# m.a31 = 0; + +# m.a02 = -2 * norm.x * norm.z; +# m.a12 = -2 * norm.y * norm.z; +# m.a22 = -2 * norm.z * norm.z + 1; +# m.a32 = 0; + +# m.a03 = -2 * norm.x * d; +# m.a13 = -2 * norm.y * d; +# m.a23 = -2 * norm.z * d; +# m.a33 = 1; + +# normal = unit_vector(normal[:3]) +# M = numpy.identity(4) +# M[:3, :3] -= 2.0 * numpy.outer(normal, normal) +# M[:3, 3] = (2.0 * numpy.dot(point[:3], normal)) * normal +# return M +def flipPoints(elementNode, points, prefix): + 'Flip the points.' + derivation = FlipDerivation(elementNode, prefix) + for point in points: + point.setToVector3(point - 2.0 * derivation.axis.dot(point - derivation.origin) * derivation.axis) + +def getFlippedLoop(elementNode, loop, prefix): + 'Get flipped loop.' + flipPoints(elementNode, loop, prefix) + if getShouldReverse(elementNode, prefix): + loop.reverse() + return loop + +def getManipulatedGeometryOutput(elementNode, geometryOutput, prefix): + 'Get equated geometryOutput.' + flipPoints(elementNode, matrix.getVertexes(geometryOutput), prefix) + return geometryOutput + +def getManipulatedPaths(close, elementNode, loop, prefix, sideLength): + 'Get flipped paths.' + return [getFlippedLoop(elementNode, loop, prefix)] + +def getNewDerivation(elementNode, prefix, sideLength): + 'Get new derivation.' + return FlipDerivation(elementNode, prefix) + +def getShouldReverse(elementNode, prefix): + 'Determine if the loop should be reversed.' + return evaluate.getEvaluatedBoolean(True, elementNode, prefix + 'reverse') + +def processElementNode(elementNode): + 'Process the xml element.' + solid.processElementNodeByFunctionPair(elementNode, getManipulatedGeometryOutput, getManipulatedPaths) + + +class FlipDerivation: + "Class to hold flip variables." + def __init__(self, elementNode, prefix): + 'Set defaults.' + self.origin = evaluate.getVector3ByPrefix(Vector3(), elementNode, prefix + 'origin') + self.axis = evaluate.getVector3ByPrefix(Vector3(1.0, 0.0, 0.0), elementNode, prefix + 'axis').getNormalized() diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_shapes/mirror.py b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_shapes/mirror.py new file mode 100644 index 0000000..4b997d7 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/manipulation_shapes/mirror.py @@ -0,0 +1,49 @@ +""" +Add material to support overhang or remove material at the overhang angle. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import solid +from fabmetheus_utilities.geometry.geometry_tools import face +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities.geometry.manipulation_shapes import flip +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__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) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/solids/__init__.py b/SkeinPyPy/fabmetheus_utilities/geometry/solids/__init__.py new file mode 100644 index 0000000..cefa3e7 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/solids/__init__.py @@ -0,0 +1,12 @@ +""" +This page is in the table of contents. +This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +""" +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 3 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/solids/cube.py b/SkeinPyPy/fabmetheus_utilities/geometry/solids/cube.py new file mode 100644 index 0000000..088904f --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/solids/cube.py @@ -0,0 +1,78 @@ +""" +Boolean geometry cube. + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import solid +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3 import Vector3 + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Nophead \nArt of Illusion ' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def addCube(elementNode, faces, inradius, vertexes): + 'Add cube by inradius.' + square = [ + complex(-inradius.x, -inradius.y), + complex(inradius.x, -inradius.y), + complex(inradius.x, inradius.y), + complex(-inradius.x, inradius.y)] + bottomTopSquare = triangle_mesh.getAddIndexedLoops(square, vertexes, [-inradius.z, inradius.z]) + triangle_mesh.addPillarByLoops(faces, bottomTopSquare) + +def getGeometryOutput(elementNode, inradius): + 'Get cube triangle mesh by inradius.' + faces = [] + vertexes = [] + addCube(elementNode, faces, inradius, vertexes) + return {'trianglemesh' : {'vertex' : vertexes, 'face' : faces}} + +def getNewDerivation(elementNode): + 'Get new derivation.' + return CubeDerivation(elementNode) + +def processElementNode(elementNode): + 'Process the xml element.' + evaluate.processArchivable(Cube, elementNode) + + +class Cube(triangle_mesh.TriangleMesh): + 'A cube object.' + def addXMLSection(self, depth, output): + 'Add the xml section for this object.' + pass + + def createShape(self): + 'Create the shape.' + addCube(self.elementNode, self.faces, self.inradius, self.vertexes) + + def setToElementNode(self, elementNode): + 'Set to elementNode.' + attributes = elementNode.attributes + self.elementNode = elementNode + self.inradius = CubeDerivation(elementNode).inradius + attributes['inradius.x'] = self.inradius.x + attributes['inradius.y'] = self.inradius.y + attributes['inradius.z'] = self.inradius.z + if 'inradius' in attributes: + del attributes['inradius'] + self.createShape() + solid.processArchiveRemoveSolid(elementNode, self.getGeometryOutput()) + + +class CubeDerivation: + "Class to hold cube variables." + def __init__(self, elementNode): + 'Set defaults.' + self.inradius = evaluate.getVector3ByPrefixes(elementNode, ['demisize', 'inradius'], Vector3(1.0, 1.0, 1.0)) + self.inradius = evaluate.getVector3ByMultiplierPrefix(elementNode, 2.0, 'size', self.inradius) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/solids/cylinder.py b/SkeinPyPy/fabmetheus_utilities/geometry/solids/cylinder.py new file mode 100644 index 0000000..aa2c46e --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/solids/cylinder.py @@ -0,0 +1,111 @@ +""" +Boolean geometry cylinder. + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import lineation +from fabmetheus_utilities.geometry.creation import solid +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities.geometry.solids import cube +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +import math + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Nophead \nArt of Illusion ' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def addCylinder(faces, inradius, sides, topOverBottom, vertexes): + 'Add cylinder by inradius.' + polygonBottom = euclidean.getComplexPolygonByComplexRadius(complex(inradius.x, inradius.y), sides) + polygonTop = polygonBottom + if topOverBottom <= 0.0: + polygonTop = [complex()] + elif topOverBottom != 1.0: + polygonTop = euclidean.getComplexPathByMultiplier(topOverBottom, polygonTop) + bottomTopPolygon = [ + triangle_mesh.getAddIndexedLoop(polygonBottom, vertexes, -inradius.z), + triangle_mesh.getAddIndexedLoop(polygonTop, vertexes, inradius.z)] + triangle_mesh.addPillarByLoops(faces, bottomTopPolygon) + +def addCylinderOutputByEndStart(endZ, inradiusComplex, outputs, sides, start, topOverBottom=1.0): + 'Add cylinder triangle mesh by endZ, inradius and start.' + inradius = Vector3(inradiusComplex.real, inradiusComplex.imag, 0.5 * abs(endZ - start.z)) + cylinderOutput = getGeometryOutput(inradius, sides, topOverBottom) + vertexes = matrix.getVertexes(cylinderOutput) + if endZ < start.z: + for vertex in vertexes: + vertex.z = -vertex.z + translation = Vector3(start.x, start.y, inradius.z + min(start.z, endZ)) + euclidean.translateVector3Path(vertexes, translation) + outputs.append(cylinderOutput) + +def getGeometryOutput(inradius, sides, topOverBottom): + 'Get cylinder triangle mesh by inradius.' + faces = [] + vertexes = [] + addCylinder(faces, inradius, sides, topOverBottom, vertexes) + return {'trianglemesh' : {'vertex' : vertexes, 'face' : faces}} + +def getNewDerivation(elementNode): + 'Get new derivation.' + return CylinderDerivation(elementNode) + +def getTopOverBottom(angle, endZ, inradiusComplex, startZ): + 'Get topOverBottom by angle in radians, endZ, inradius and start.' + return max(1.0 - abs(endZ - startZ) * math.tan(angle) / lineation.getRadiusAverage(inradiusComplex), 0.0) + +def processElementNode(elementNode): + 'Process the xml element.' + evaluate.processArchivable(Cylinder, elementNode) + + +class Cylinder( cube.Cube ): + 'A cylinder object.' + def __init__(self): + 'Add empty lists.' + cube.Cube.__init__(self) + + def createShape(self): + 'Create the shape.' + sides = evaluate.getSidesMinimumThreeBasedOnPrecision(self.elementNode, max(self.inradius.x, self.inradius.y)) + if self.elementNode.getCascadeBoolean(False, 'radiusAreal'): + radiusArealizedMultiplier = euclidean.getRadiusArealizedMultiplier(sides) + self.inradius.x *= radiusArealizedMultiplier + self.inradius.y *= radiusArealizedMultiplier + addCylinder(self.faces, self.inradius, sides, self.topOverBottom, self.vertexes) + + def setToElementNode(self, elementNode): + 'Set to elementNode.' + attributes = elementNode.attributes + self.elementNode = elementNode + derivation = CylinderDerivation(elementNode) + self.inradius = derivation.inradius + self.topOverBottom = derivation.topOverBottom + if 'inradius' in attributes: + del attributes['inradius'] + attributes['height'] = self.inradius.z + self.inradius.z + attributes['radius.x'] = self.inradius.x + attributes['radius.y'] = self.inradius.y + attributes['topOverBottom'] = self.topOverBottom + self.createShape() + solid.processArchiveRemoveSolid(elementNode, self.getGeometryOutput()) + + +class CylinderDerivation: + "Class to hold cylinder variables." + def __init__(self, elementNode): + 'Set defaults.' + self.inradius = evaluate.getVector3ByPrefixes(elementNode, ['demisize', 'inradius', 'radius'], Vector3(1.0, 1.0, 1.0)) + self.inradius = evaluate.getVector3ByMultiplierPrefixes(elementNode, 2.0, ['diameter', 'size'], self.inradius) + self.inradius.z = 0.5 * evaluate.getEvaluatedFloat(self.inradius.z + self.inradius.z, elementNode, 'height') + self.topOverBottom = evaluate.getEvaluatedFloat(1.0, elementNode, 'topOverBottom') diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/solids/difference.py b/SkeinPyPy/fabmetheus_utilities/geometry/solids/difference.py new file mode 100644 index 0000000..447b697 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/solids/difference.py @@ -0,0 +1,43 @@ +""" +Boolean geometry difference of solids. + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.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 \nArt of Illusion ' +__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() diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/solids/group.py b/SkeinPyPy/fabmetheus_utilities/geometry/solids/group.py new file mode 100644 index 0000000..37de4ed --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/solids/group.py @@ -0,0 +1,82 @@ +""" +Boolean geometry group of solids. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.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 ' +__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) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/solids/intersection.py b/SkeinPyPy/fabmetheus_utilities/geometry/solids/intersection.py new file mode 100644 index 0000000..04aa15a --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/solids/intersection.py @@ -0,0 +1,39 @@ +""" +Boolean geometry intersection of solids. + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.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 \nArt of Illusion ' +__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) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/solids/sphere.py b/SkeinPyPy/fabmetheus_utilities/geometry/solids/sphere.py new file mode 100644 index 0000000..9104056 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/solids/sphere.py @@ -0,0 +1,83 @@ +""" +Boolean geometry sphere. + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.creation import solid +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.solids import 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 \nArt of Illusion ' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def addSphere(elementNode, faces, radius, vertexes): + 'Add sphere by radius.' + bottom = -radius.z + sides = evaluate.getSidesMinimumThreeBasedOnPrecision(elementNode, max(radius.x, radius.y, radius.z)) + sphereSlices = max(sides / 2, 2) + equator = euclidean.getComplexPolygonByComplexRadius(complex(radius.x, radius.y), sides) + polygons = [triangle_mesh.getAddIndexedLoop([complex()], vertexes, bottom)] + zIncrement = (radius.z + radius.z) / float(sphereSlices) + z = bottom + for sphereSlice in xrange(1, sphereSlices): + z += zIncrement + zPortion = abs(z) / radius.z + multipliedPath = euclidean.getComplexPathByMultiplier(math.sqrt(1.0 - zPortion * zPortion), equator) + polygons.append(triangle_mesh.getAddIndexedLoop(multipliedPath, vertexes, z)) + polygons.append(triangle_mesh.getAddIndexedLoop([complex()], vertexes, radius.z)) + triangle_mesh.addPillarByLoops(faces, polygons) + +def getGeometryOutput(elementNode, radius): + 'Get triangle mesh from attribute dictionary.' + faces = [] + vertexes = [] + addSphere(elementNode, faces, radius, vertexes) + return {'trianglemesh' : {'vertex' : vertexes, 'face' : faces}} + +def getNewDerivation(elementNode): + 'Get new derivation.' + return SphereDerivation(elementNode) + +def processElementNode(elementNode): + 'Process the xml element.' + evaluate.processArchivable(Sphere, elementNode) + + +class Sphere(cube.Cube): + 'A sphere object.' + def createShape(self): + 'Create the shape.' + addSphere(self.elementNode, self.faces, self.radius, self.vertexes) + + def setToElementNode(self, elementNode): + 'Set to elementNode.' + attributes = elementNode.attributes + self.elementNode = elementNode + self.radius = SphereDerivation(elementNode).radius + if 'radius' in attributes: + del attributes['radius'] + attributes['radius.x'] = self.radius.x + attributes['radius.y'] = self.radius.y + attributes['radius.z'] = self.radius.z + self.createShape() + solid.processArchiveRemoveSolid(elementNode, self.getGeometryOutput()) + + +class SphereDerivation: + "Class to hold sphere variables." + def __init__(self, elementNode): + 'Set defaults.' + self.radius = evaluate.getVector3ByPrefixes(elementNode, ['demisize', 'radius'], Vector3(1.0, 1.0, 1.0)) + self.radius = evaluate.getVector3ByMultiplierPrefixes(elementNode, 2.0, ['diameter', 'size'], self.radius) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/solids/triangle_mesh.py b/SkeinPyPy/fabmetheus_utilities/geometry/solids/triangle_mesh.py new file mode 100644 index 0000000..e624ee8 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/solids/triangle_mesh.py @@ -0,0 +1,939 @@ +""" +Triangle Mesh holds the faces and edges of a triangular mesh. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_tools import face +from fabmetheus_utilities.geometry.geometry_tools import dictionary +from fabmetheus_utilities.geometry.geometry_tools import vertex +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities.geometry.solids import group +from fabmetheus_utilities import xml_simple_writer +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities.vector3index import Vector3Index +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def addEdgePair( edgePairTable, edges, faceEdgeIndex, remainingEdgeIndex, remainingEdgeTable ): + 'Add edge pair to the edge pair table.' + if faceEdgeIndex == remainingEdgeIndex: + return + if not faceEdgeIndex in remainingEdgeTable: + return + edgePair = EdgePair().getFromIndexesEdges( [ remainingEdgeIndex, faceEdgeIndex ], edges ) + edgePairTable[ str( edgePair ) ] = edgePair + +def addFacesByConcaveLoop(faces, indexedLoop): + 'Add faces from a polygon which is concave.' + if len(indexedLoop) < 3: + return + remainingLoop = indexedLoop[:] + while len(remainingLoop) > 2: + remainingLoop = getRemainingLoopAddFace(faces, remainingLoop) + +def addFacesByConvex(faces, indexedLoop): + 'Add faces from a convex polygon.' + if len(indexedLoop) < 3: + return + indexBegin = indexedLoop[0].index + for indexedPointIndex in xrange(1, len(indexedLoop) - 1): + indexCenter = indexedLoop[indexedPointIndex].index + indexEnd = indexedLoop[(indexedPointIndex + 1) % len(indexedLoop) ].index + if indexBegin != indexCenter and indexCenter != indexEnd and indexEnd != indexBegin: + faceFromConvex = face.Face() + faceFromConvex.index = len(faces) + faceFromConvex.vertexIndexes.append(indexBegin) + faceFromConvex.vertexIndexes.append(indexCenter) + faceFromConvex.vertexIndexes.append(indexEnd) + faces.append(faceFromConvex) + +def addFacesByConvexBottomTopLoop(faces, indexedLoopBottom, indexedLoopTop): + 'Add faces from loops.' + if len(indexedLoopBottom) == 0 or len(indexedLoopTop) == 0: + return + for indexedPointIndex in xrange(max(len(indexedLoopBottom), len(indexedLoopTop))): + indexedConvex = [] + if len(indexedLoopBottom) > 1: + indexedConvex.append(indexedLoopBottom[indexedPointIndex]) + indexedConvex.append(indexedLoopBottom[(indexedPointIndex + 1) % len(indexedLoopBottom)]) + else: + indexedConvex.append(indexedLoopBottom[0]) + if len(indexedLoopTop) > 1: + indexedConvex.append(indexedLoopTop[(indexedPointIndex + 1) % len(indexedLoopTop)]) + indexedConvex.append(indexedLoopTop[indexedPointIndex]) + else: + indexedConvex.append(indexedLoopTop[0]) + addFacesByConvex(faces, indexedConvex) + +def addFacesByConvexLoops(faces, indexedLoops): + 'Add faces from loops.' + if len(indexedLoops) < 2: + return + for indexedLoopsIndex in xrange(len(indexedLoops) - 2): + addFacesByConvexBottomTopLoop(faces, indexedLoops[indexedLoopsIndex], indexedLoops[indexedLoopsIndex + 1]) + indexedLoopBottom = indexedLoops[-2] + indexedLoopTop = indexedLoops[-1] + if len(indexedLoopTop) < 1: + indexedLoopTop = indexedLoops[0] + addFacesByConvexBottomTopLoop(faces, indexedLoopBottom, indexedLoopTop) + +def addFacesByConvexReversed(faces, indexedLoop): + 'Add faces from a reversed convex polygon.' + addFacesByConvex(faces, indexedLoop[: : -1]) + +def addFacesByGrid(faces, grid): + 'Add faces from grid.' + cellTopLoops = getIndexedCellLoopsFromIndexedGrid(grid) + for cellTopLoop in cellTopLoops: + addFacesByConvex(faces, cellTopLoop) + +def addFacesByLoop(faces, indexedLoop): + 'Add faces from a polygon which may be concave.' + if len(indexedLoop) < 3: + return + lastNormal = None + for pointIndex, point in enumerate(indexedLoop): + center = indexedLoop[(pointIndex + 1) % len(indexedLoop)] + end = indexedLoop[(pointIndex + 2) % len(indexedLoop)] + normal = euclidean.getNormalWeighted(point, center, end) + if abs(normal) > 0.0: + if lastNormal != None: + if lastNormal.dot(normal) < 0.0: + addFacesByConcaveLoop(faces, indexedLoop) + return + lastNormal = normal +# totalNormal = Vector3() +# for pointIndex, point in enumerate(indexedLoop): +# center = indexedLoop[(pointIndex + 1) % len(indexedLoop)] +# end = indexedLoop[(pointIndex + 2) % len(indexedLoop)] +# totalNormal += euclidean.getNormalWeighted(point, center, end) +# totalNormal.normalize() + addFacesByConvex(faces, indexedLoop) + +def addFacesByLoopReversed(faces, indexedLoop): + 'Add faces from a reversed convex polygon.' + addFacesByLoop(faces, indexedLoop[: : -1]) + +def addFacesByMeldedConvexLoops(faces, indexedLoops): + 'Add faces from melded loops.' + if len(indexedLoops) < 2: + return + for indexedLoopsIndex in xrange(len(indexedLoops) - 2): + FaceGenerator(faces, indexedLoops[indexedLoopsIndex], indexedLoops[indexedLoopsIndex + 1]) + indexedLoopBottom = indexedLoops[-2] + indexedLoopTop = indexedLoops[-1] + if len(indexedLoopTop) < 1: + indexedLoopTop = indexedLoops[0] + FaceGenerator(faces, indexedLoopBottom, indexedLoopTop) + +def addLoopToPointTable(loop, pointTable): + 'Add the points in the loop to the point table.' + for point in loop: + pointTable[point] = None + +def addMeldedPillarByLoops(faces, indexedLoops): + 'Add melded pillar by loops which may be concave.' + if len(indexedLoops) < 1: + return + if len(indexedLoops[-1]) < 1: + addFacesByMeldedConvexLoops(faces, indexedLoops) + return + addFacesByLoopReversed(faces, indexedLoops[0]) + addFacesByMeldedConvexLoops(faces, indexedLoops) + addFacesByLoop(faces, indexedLoops[-1]) + +def addPillarByLoops(faces, indexedLoops): + 'Add pillar by loops which may be concave.' + if len(indexedLoops) < 1: + return + if len(indexedLoops[-1]) < 1: + addFacesByConvexLoops(faces, indexedLoops) + return + addFacesByLoopReversed(faces, indexedLoops[0]) + addFacesByConvexLoops(faces, indexedLoops) + addFacesByLoop(faces, indexedLoops[-1]) + +def addPillarFromConvexLoopsGrids(faces, indexedGrids, indexedLoops): + 'Add pillar from convex loops and grids.' + cellBottomLoops = getIndexedCellLoopsFromIndexedGrid(indexedGrids[0]) + for cellBottomLoop in cellBottomLoops: + addFacesByConvexReversed(faces, cellBottomLoop) + addFacesByConvexLoops(faces, indexedLoops) + addFacesByGrid(faces, indexedGrids[-1]) + +def addPillarFromConvexLoopsGridTop(faces, indexedGridTop, indexedLoops): + 'Add pillar from convex loops and grid top.' + addFacesByLoopReversed(faces, indexedLoops[0]) + addFacesByConvexLoops(faces, indexedLoops) + addFacesByGrid(faces, indexedGridTop) + +def addPointsAtZ(edgePair, points, radius, vertexes, z): + 'Add point complexes on the segment between the edge intersections with z.' + carveIntersectionFirst = getCarveIntersectionFromEdge(edgePair.edges[0], vertexes, z) + carveIntersectionSecond = getCarveIntersectionFromEdge(edgePair.edges[1], vertexes, z) + # threshold radius above 0.8 can create extra holes on Screw Holder, 0.7 should be safe for everything + intercircle.addPointsFromSegment(carveIntersectionFirst, carveIntersectionSecond, points, radius, 0.7) + +def addSymmetricXPath(outputs, path, x): + 'Add x path output to outputs.' + vertexes = [] + loops = [getSymmetricXLoop(path, vertexes, -x), getSymmetricXLoop(path, vertexes, x)] + outputs.append(getPillarOutput(loops)) + +def addSymmetricXPaths(outputs, paths, x): + 'Add x paths outputs to outputs.' + for path in paths: + addSymmetricXPath(outputs, path, x) + +def addSymmetricYPath(outputs, path, y): + 'Add y path output to outputs.' + vertexes = [] + loops = [getSymmetricYLoop(path, vertexes, -y), getSymmetricYLoop(path, vertexes, y)] + outputs.append(getPillarOutput(loops)) + +def addSymmetricYPaths(outputs, paths, y): + 'Add y paths outputs to outputs.' + for path in paths: + addSymmetricYPath(outputs, path, y) + +def addVector3Loop(loop, loops, vertexes, z): + 'Add vector3Loop to loops if there is something in it, for inset and outset.' + vector3Loop = [] + for point in loop: + vector3Index = Vector3Index(len(vertexes), point.real, point.imag, z) + vector3Loop.append(vector3Index) + vertexes.append(vector3Index) + if len(vector3Loop) > 0: + loops.append(vector3Loop) + +def addWithLeastLength(importRadius, loops, point): + 'Insert a point into a loop, at the index at which the loop would be shortest.' + close = 1.65 * importRadius # a bit over the experimental minimum additional loop length to restore a right angle + shortestAdditionalLength = close + shortestLoop = None + shortestPointIndex = None + for loop in loops: + if len(loop) > 3: + for pointIndex in xrange(len(loop)): + additionalLoopLength = getAdditionalLoopLength(loop, point, pointIndex) + if additionalLoopLength < shortestAdditionalLength: + if getIsPointCloseInline(close, loop, point, pointIndex): + shortestAdditionalLength = additionalLoopLength + shortestLoop = loop + shortestPointIndex = pointIndex + if shortestPointIndex != None: + shortestLoop.insert( shortestPointIndex, point ) + +def convertElementNode(elementNode, geometryOutput): + 'Convert the xml element to a TriangleMesh xml element.' + elementNode.linkObject(TriangleMesh()) + matrix.getBranchMatrixSetElementNode(elementNode) + vertex.addGeometryList(elementNode, geometryOutput['vertex']) + face.addGeometryList(elementNode, geometryOutput['face']) + elementNode.getXMLProcessor().processChildNodes(elementNode) + +def getAddIndexedGrid( grid, vertexes, z ): + 'Get and add an indexed grid.' + indexedGrid = [] + for row in grid: + indexedRow = [] + indexedGrid.append( indexedRow ) + for pointComplex in row: + vector3index = Vector3Index( len(vertexes), pointComplex.real, pointComplex.imag, z ) + indexedRow.append(vector3index) + vertexes.append(vector3index) + return indexedGrid + +def getAddIndexedLoop(loop, vertexes, z): + 'Get and add an indexed loop.' + indexedLoop = [] + for index in xrange(len(loop)): + pointComplex = loop[index] + vector3index = Vector3Index(len(vertexes), pointComplex.real, pointComplex.imag, z) + indexedLoop.append(vector3index) + vertexes.append(vector3index) + return indexedLoop + +def getAddIndexedLoops( loop, vertexes, zList ): + 'Get and add indexed loops.' + indexedLoops = [] + for z in zList: + indexedLoop = getAddIndexedLoop( loop, vertexes, z ) + indexedLoops.append(indexedLoop) + return indexedLoops + +def getAdditionalLoopLength(loop, point, pointIndex): + 'Get the additional length added by inserting a point into a loop.' + afterPoint = loop[pointIndex] + beforePoint = loop[(pointIndex + len(loop) - 1) % len(loop)] + return abs(point - beforePoint) + abs(point - afterPoint) - abs(afterPoint - beforePoint) + +def getCarveIntersectionFromEdge(edge, vertexes, z): + 'Get the complex where the carve intersects the edge.' + firstVertex = vertexes[ edge.vertexIndexes[0] ] + firstVertexComplex = firstVertex.dropAxis() + secondVertex = vertexes[ edge.vertexIndexes[1] ] + secondVertexComplex = secondVertex.dropAxis() + zMinusFirst = z - firstVertex.z + up = secondVertex.z - firstVertex.z + return zMinusFirst * ( secondVertexComplex - firstVertexComplex ) / up + firstVertexComplex + +def getClosestDistanceIndexToPoint(point, loop): + 'Get the distance squared to the closest point of the loop and index of that point.' + smallestDistance = 987654321987654321.0 + closestDistanceIndex = None + pointComplex = point.dropAxis() + for otherPointIndex, otherPoint in enumerate(loop): + distance = abs(pointComplex - otherPoint.dropAxis()) + if distance < smallestDistance: + smallestDistance = distance + closestDistanceIndex = euclidean.DistanceIndex(distance, otherPointIndex) + return closestDistanceIndex + +def getDescendingAreaLoops(allPoints, corners, importRadius): + 'Get descending area loops which include most of the points.' + loops = intercircle.getCentersFromPoints(allPoints, importRadius) + descendingAreaLoops = [] + sortLoopsInOrderOfArea(True, loops) + pointDictionary = {} + for loop in loops: + if len(loop) > 2 and getOverlapRatio(loop, pointDictionary) < 0.3: + intercircle.directLoop(not euclidean.getIsInFilledRegion(descendingAreaLoops, loop[0]), loop) + descendingAreaLoops.append(loop) + addLoopToPointTable(loop, pointDictionary) + descendingAreaLoops = euclidean.getSimplifiedLoops(descendingAreaLoops, importRadius) + return getLoopsWithCorners(corners, importRadius, descendingAreaLoops, pointDictionary) + +def getDescendingAreaOrientedLoops(allPoints, corners, importRadius): + 'Get descending area oriented loops which include most of the points.' + return getOrientedLoops(getDescendingAreaLoops(allPoints, corners, importRadius)) + +def getGeometryOutputByFacesVertexes(faces, vertexes): + 'Get geometry output dictionary by faces and vertexes.' + return {'trianglemesh' : {'vertex' : vertexes, 'face' : faces}} + +def getGeometryOutputCopy(object): + 'Get the geometry output copy.' + objectClass = object.__class__ + if objectClass == dict: + objectCopy = {} + for key in object: + objectCopy[key] = getGeometryOutputCopy(object[key]) + return objectCopy + if objectClass == list: + objectCopy = [] + for value in object: + objectCopy.append(getGeometryOutputCopy(value)) + return objectCopy + if objectClass == face.Face or objectClass == Vector3 or objectClass == Vector3Index: + return object.copy() + return object + +def getIndexedCellLoopsFromIndexedGrid( grid ): + 'Get indexed cell loops from an indexed grid.' + indexedCellLoops = [] + for rowIndex in xrange( len( grid ) - 1 ): + rowBottom = grid[ rowIndex ] + rowTop = grid[ rowIndex + 1 ] + for columnIndex in xrange( len( rowBottom ) - 1 ): + columnIndexEnd = columnIndex + 1 + indexedConvex = [] + indexedConvex.append( rowBottom[ columnIndex ] ) + indexedConvex.append( rowBottom[ columnIndex + 1 ] ) + indexedConvex.append( rowTop[ columnIndex + 1 ] ) + indexedConvex.append( rowTop[ columnIndex ] ) + indexedCellLoops.append( indexedConvex ) + return indexedCellLoops + +def getIndexedLoopFromIndexedGrid( indexedGrid ): + 'Get indexed loop from around the indexed grid.' + indexedLoop = indexedGrid[0][:] + for row in indexedGrid[1 : -1]: + indexedLoop.append( row[-1] ) + indexedLoop += indexedGrid[-1][: : -1] + for row in indexedGrid[ len( indexedGrid ) - 2 : 0 : - 1 ]: + indexedLoop.append( row[0] ) + return indexedLoop + +def getInfillDictionary(arounds, aroundWidth, infillInset, infillWidth, pixelTable, rotatedLoops, testLoops=None): + 'Get combined fill loops which include most of the points.' + slightlyGreaterThanInfillInset = intercircle.globalIntercircleMultiplier * infillInset + allPoints = intercircle.getPointsFromLoops(rotatedLoops, infillInset, 0.7) + centers = intercircle.getCentersFromPoints(allPoints, slightlyGreaterThanInfillInset) + infillDictionary = {} + for center in centers: + insetCenter = intercircle.getSimplifiedInsetFromClockwiseLoop(center, infillInset) + insetPoint = insetCenter[0] + if len(insetCenter) > 2 and intercircle.getIsLarge(insetCenter, infillInset) and euclidean.getIsInFilledRegion(rotatedLoops, insetPoint): + around = euclidean.getSimplifiedLoop(center, infillInset) + euclidean.addLoopToPixelTable(around, pixelTable, aroundWidth) + arounds.append(around) + insetLoop = intercircle.getSimplifiedInsetFromClockwiseLoop(center, infillInset) + euclidean.addXIntersectionsFromLoopForTable(insetLoop, infillDictionary, infillWidth) + if testLoops != None: + testLoops.append(insetLoop) + return infillDictionary + +def getInsetPoint( loop, tinyRadius ): + 'Get the inset vertex.' + pointIndex = getWideAnglePointIndex(loop) + point = loop[ pointIndex % len(loop) ] + afterPoint = loop[(pointIndex + 1) % len(loop)] + beforePoint = loop[ ( pointIndex - 1 ) % len(loop) ] + afterSegmentNormalized = euclidean.getNormalized( afterPoint - point ) + beforeSegmentNormalized = euclidean.getNormalized( beforePoint - point ) + afterClockwise = complex( afterSegmentNormalized.imag, - afterSegmentNormalized.real ) + beforeWiddershins = complex( - beforeSegmentNormalized.imag, beforeSegmentNormalized.real ) + midpoint = afterClockwise + beforeWiddershins + midpointNormalized = midpoint / abs( midpoint ) + return point + midpointNormalized * tinyRadius + +def getIsPathEntirelyOutsideTriangle(begin, center, end, vector3Path): + 'Determine if a path is entirely outside another loop.' + loop = [begin.dropAxis(), center.dropAxis(), end.dropAxis()] + for vector3 in vector3Path: + point = vector3.dropAxis() + if euclidean.isPointInsideLoop(loop, point): + return False + return True + +def getIsPointCloseInline(close, loop, point, pointIndex): + 'Insert a point into a loop, at the index at which the loop would be shortest.' + afterCenterComplex = loop[pointIndex] + if abs(afterCenterComplex - point) > close: + return False + afterEndComplex = loop[(pointIndex + 1) % len(loop)] + if not isInline( point, afterCenterComplex, afterEndComplex ): + return False + beforeCenterComplex = loop[(pointIndex + len(loop) - 1) % len(loop)] + if abs(beforeCenterComplex - point) > close: + return False + beforeEndComplex = loop[(pointIndex + len(loop) - 2) % len(loop)] + return isInline(point, beforeCenterComplex, beforeEndComplex) + +def getLoopsFromCorrectMesh( edges, faces, vertexes, z ): + 'Get loops from a carve of a correct mesh.' + remainingEdgeTable = getRemainingEdgeTable(edges, vertexes, z) + remainingValues = remainingEdgeTable.values() + for edge in remainingValues: + if len( edge.faceIndexes ) < 2: + print('This should never happen, there is a hole in the triangle mesh, each edge should have two faces.') + print(edge) + 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 = [] + while isPathAdded( edges, faces, loops, remainingEdgeTable, vertexes, z ): + pass + if euclidean.isLoopListIntersecting(loops): + print('Warning, the triangle mesh slice intersects itself in getLoopsFromCorrectMesh in triangle_mesh.') + 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 [] + return loops +# untouchables = [] +# for boundingLoop in boundingLoops: +# if not boundingLoop.isIntersectingList( untouchables ): +# untouchables.append( boundingLoop ) +# if len( untouchables ) < len( boundingLoops ): +# print('This should never happen, the carve layer intersects itself. 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) +# remainingLoops = [] +# for untouchable in untouchables: +# remainingLoops.append( untouchable.loop ) +# return remainingLoops + +def getLoopsFromUnprovenMesh(edges, faces, importRadius, vertexes, z): + 'Get loops from a carve of an unproven mesh.' + edgePairTable = {} + corners = [] + remainingEdgeTable = getRemainingEdgeTable(edges, vertexes, z) + remainingEdgeTableKeys = remainingEdgeTable.keys() + for remainingEdgeIndexKey in remainingEdgeTable: + edge = remainingEdgeTable[remainingEdgeIndexKey] + carveIntersection = getCarveIntersectionFromEdge(edge, vertexes, z) + corners.append(carveIntersection) + for edgeFaceIndex in edge.faceIndexes: + face = faces[edgeFaceIndex] + for edgeIndex in face.edgeIndexes: + addEdgePair(edgePairTable, edges, edgeIndex, remainingEdgeIndexKey, remainingEdgeTable) + allPoints = corners[:] + for edgePairValue in edgePairTable.values(): + addPointsAtZ(edgePairValue, allPoints, importRadius, vertexes, z) + pointTable = {} + return getDescendingAreaLoops(allPoints, corners, importRadius) + +def getLoopLayerAppend(loopLayers, z): + 'Get next z and add extruder loops.' + settings.printProgress(len(loopLayers), 'slice') + loopLayer = euclidean.LoopLayer(z) + loopLayers.append(loopLayer) + return loopLayer + +def getLoopsWithCorners(corners, importRadius, loops, pointTable): + 'Add corners to the loops.' + for corner in corners: + if corner not in pointTable: + addWithLeastLength(importRadius, loops, corner) + pointTable[corner] = None + return euclidean.getSimplifiedLoops(loops, importRadius) + +def getMeldedPillarOutput(loops): + 'Get melded pillar output.' + faces = [] + vertexes = getUniqueVertexes(loops) + addMeldedPillarByLoops(faces, loops) + return getGeometryOutputByFacesVertexes(faces, vertexes) + +def getNewDerivation(elementNode): + 'Get new derivation.' + return evaluate.EmptyObject(elementNode) + +def getNextEdgeIndexAroundZ(edge, faces, remainingEdgeTable): + 'Get the next edge index in the mesh carve.' + for faceIndex in edge.faceIndexes: + face = faces[faceIndex] + for edgeIndex in face.edgeIndexes: + if edgeIndex in remainingEdgeTable: + return edgeIndex + return -1 + +def getOrientedLoops(loops): + 'Orient the loops which must be in descending order.' + for loopIndex, loop in enumerate(loops): + leftPoint = euclidean.getLeftPoint(loop) + isInFilledRegion = euclidean.getIsInFilledRegion(loops[: loopIndex] + loops[loopIndex + 1 :], leftPoint) + if isInFilledRegion == euclidean.isWiddershins(loop): + loop.reverse() + return loops + +def getOverlapRatio( loop, pointTable ): + 'Get the overlap ratio between the loop and the point table.' + numberOfOverlaps = 0 + for point in loop: + if point in pointTable: + numberOfOverlaps += 1 + return float( numberOfOverlaps ) / float(len(loop)) + +def getPath( edges, pathIndexes, loop, z ): + 'Get the path from the edge intersections.' + path = [] + for pathIndexIndex in xrange( len( pathIndexes ) ): + pathIndex = pathIndexes[ pathIndexIndex ] + edge = edges[ pathIndex ] + carveIntersection = getCarveIntersectionFromEdge( edge, loop, z ) + path.append( carveIntersection ) + return path + +def getPillarOutput(loops): + 'Get pillar output.' + faces = [] + vertexes = getUniqueVertexes(loops) + addPillarByLoops(faces, loops) + return getGeometryOutputByFacesVertexes(faces, vertexes) + +def getPillarsOutput(loopLists): + 'Get pillars output.' + pillarsOutput = [] + for loopList in loopLists: + pillarsOutput.append(getPillarOutput(loopList)) + return getUnifiedOutput(pillarsOutput) + +def getRemainingEdgeTable(edges, vertexes, z): + 'Get the remaining edge hashtable.' + remainingEdgeTable = {} + if len(edges) > 0: + if edges[0].zMinimum == None: + for edge in edges: + setEdgeMaximumMinimum(edge, vertexes) + for edgeIndex in xrange(len(edges)): + edge = edges[edgeIndex] + if (edge.zMinimum < z) and (edge.zMaximum > z): + remainingEdgeTable[edgeIndex] = edge + return remainingEdgeTable + +def getRemainingLoopAddFace(faces, remainingLoop): + 'Get the remaining loop and add face.' + for indexedVertexIndex, indexedVertex in enumerate(remainingLoop): + nextIndex = (indexedVertexIndex + 1) % len(remainingLoop) + previousIndex = (indexedVertexIndex + len(remainingLoop) - 1) % len(remainingLoop) + nextVertex = remainingLoop[nextIndex] + previousVertex = remainingLoop[previousIndex] + remainingPath = euclidean.getAroundLoop((indexedVertexIndex + 2) % len(remainingLoop), previousIndex, remainingLoop) + if len(remainingLoop) < 4 or getIsPathEntirelyOutsideTriangle(previousVertex, indexedVertex, nextVertex, remainingPath): + faceConvex = face.Face() + faceConvex.index = len(faces) + faceConvex.vertexIndexes.append(indexedVertex.index) + faceConvex.vertexIndexes.append(nextVertex.index) + faceConvex.vertexIndexes.append(previousVertex.index) + faces.append(faceConvex) + return euclidean.getAroundLoop(nextIndex, indexedVertexIndex, remainingLoop) + print('Warning, could not decompose polygon in getRemainingLoopAddFace in trianglemesh for:') + print(remainingLoop) + return [] + +def getSharedFace( firstEdge, faces, secondEdge ): + 'Get the face which is shared by two edges.' + for firstEdgeFaceIndex in firstEdge.faceIndexes: + for secondEdgeFaceIndex in secondEdge.faceIndexes: + if firstEdgeFaceIndex == secondEdgeFaceIndex: + return faces[ firstEdgeFaceIndex ] + return None + +def getSymmetricXLoop(path, vertexes, x): + 'Get symmetrix x loop.' + loop = [] + for point in path: + vector3Index = Vector3Index(len(vertexes), x, point.real, point.imag) + loop.append(vector3Index) + vertexes.append(vector3Index) + return loop + +def getSymmetricYLoop(path, vertexes, y): + 'Get symmetrix y loop.' + loop = [] + for point in path: + vector3Index = Vector3Index(len(vertexes), point.real, y, point.imag) + loop.append(vector3Index) + vertexes.append(vector3Index) + return loop + +def getUnifiedOutput(outputs): + 'Get unified output.' + if len(outputs) < 1: + return {} + if len(outputs) == 1: + return outputs[0] + return {'union' : {'shapes' : outputs}} + +def getUniqueVertexes(loops): + 'Get unique vertexes.' + vertexDictionary = {} + uniqueVertexes = [] + for loop in loops: + for vertexIndex, vertex in enumerate(loop): + vertexTuple = (vertex.x, vertex.y, vertex.z) + if vertexTuple in vertexDictionary: + loop[vertexIndex] = vertexDictionary[vertexTuple] + else: + if vertex.__class__ == Vector3Index: + loop[vertexIndex].index = len(vertexDictionary) + else: + loop[vertexIndex] = Vector3Index(len(vertexDictionary), vertex.x, vertex.y, vertex.z) + vertexDictionary[vertexTuple] = loop[vertexIndex] + uniqueVertexes.append(loop[vertexIndex]) + return uniqueVertexes + +def getWideAnglePointIndex(loop): + 'Get a point index which has a wide enough angle, most point indexes have a wide enough angle, this is just to make sure.' + dotProductMinimum = 9999999.9 + widestPointIndex = 0 + for pointIndex in xrange(len(loop)): + point = loop[ pointIndex % len(loop) ] + afterPoint = loop[(pointIndex + 1) % len(loop)] + beforePoint = loop[ ( pointIndex - 1 ) % len(loop) ] + afterSegmentNormalized = euclidean.getNormalized( afterPoint - point ) + beforeSegmentNormalized = euclidean.getNormalized( beforePoint - point ) + dotProduct = euclidean.getDotProduct( afterSegmentNormalized, beforeSegmentNormalized ) + if dotProduct < .99: + return pointIndex + if dotProduct < dotProductMinimum: + dotProductMinimum = dotProduct + widestPointIndex = pointIndex + return widestPointIndex + +def isInline( beginComplex, centerComplex, endComplex ): + 'Determine if the three complex points form a line.' + centerBeginComplex = beginComplex - centerComplex + centerEndComplex = endComplex - centerComplex + centerBeginLength = abs( centerBeginComplex ) + centerEndLength = abs( centerEndComplex ) + if centerBeginLength <= 0.0 or centerEndLength <= 0.0: + return False + centerBeginComplex /= centerBeginLength + centerEndComplex /= centerEndLength + return euclidean.getDotProduct( centerBeginComplex, centerEndComplex ) < -0.999 + +def isPathAdded( edges, faces, loops, remainingEdgeTable, vertexes, z ): + 'Get the path indexes around a triangle mesh carve and add the path to the flat loops.' + if len( remainingEdgeTable ) < 1: + return False + pathIndexes = [] + remainingEdgeIndexKey = remainingEdgeTable.keys()[0] + pathIndexes.append( remainingEdgeIndexKey ) + del remainingEdgeTable[remainingEdgeIndexKey] + nextEdgeIndexAroundZ = getNextEdgeIndexAroundZ( edges[remainingEdgeIndexKey], faces, remainingEdgeTable ) + while nextEdgeIndexAroundZ != - 1: + pathIndexes.append( nextEdgeIndexAroundZ ) + del remainingEdgeTable[ nextEdgeIndexAroundZ ] + nextEdgeIndexAroundZ = getNextEdgeIndexAroundZ( edges[ nextEdgeIndexAroundZ ], faces, remainingEdgeTable ) + if len( pathIndexes ) < 3: + print('Dangling edges, will use intersecting circles to get import layer at height %s' % z) + del loops[:] + return False + loops.append( getPath( edges, pathIndexes, vertexes, z ) ) + return True + +def processElementNode(elementNode): + 'Process the xml element.' + evaluate.processArchivable(TriangleMesh, elementNode) + +def setEdgeMaximumMinimum(edge, vertexes): + 'Set the edge maximum and minimum.' + beginIndex = edge.vertexIndexes[0] + endIndex = edge.vertexIndexes[1] + if beginIndex >= len(vertexes) or endIndex >= len(vertexes): + print('Warning, there are duplicate vertexes in setEdgeMaximumMinimum in triangle_mesh.') + print('Something might still be printed, but there is no guarantee that it will be the correct shape.' ) + edge.zMaximum = -987654321.0 + edge.zMinimum = -987654321.0 + return + beginZ = vertexes[beginIndex].z + endZ = vertexes[endIndex].z + edge.zMinimum = min(beginZ, endZ) + edge.zMaximum = max(beginZ, endZ) + +def sortLoopsInOrderOfArea(isDescending, loops): + 'Sort the loops in the order of area according isDescending.' + loops.sort(key=euclidean.getAreaLoopAbsolute, reverse=isDescending) + + +class EdgePair: + def __init__(self): + 'Pair of edges on a face.' + self.edgeIndexes = [] + self.edges = [] + + def __repr__(self): + 'Get the string representation of this EdgePair.' + return str( self.edgeIndexes ) + + def getFromIndexesEdges( self, edgeIndexes, edges ): + 'Initialize from edge indices.' + self.edgeIndexes = edgeIndexes[:] + self.edgeIndexes.sort() + for edgeIndex in self.edgeIndexes: + self.edges.append( edges[ edgeIndex ] ) + return self + + +class FaceGenerator: + 'A face generator.' + def __init__(self, faces, indexedLoopBottom, indexedLoopTop): + 'Initialize.' + self.startTop = 0 + if len(indexedLoopBottom) == 0 or len(indexedLoopTop) == 0: + return + smallestDistance = 987654321987654321.0 + for pointIndex, point in enumerate(indexedLoopBottom): + distanceIndex = getClosestDistanceIndexToPoint(point, indexedLoopTop) + if distanceIndex.distance < smallestDistance: + smallestDistance = distanceIndex.distance + offsetBottom = pointIndex + offsetTop = distanceIndex.index + self.indexedLoopBottom = indexedLoopBottom[offsetBottom :] + indexedLoopBottom[: offsetBottom] + self.indexedLoopTop = indexedLoopTop[offsetTop :] + indexedLoopTop[: offsetTop] + for bottomIndex in xrange(len(self.indexedLoopBottom)): + self.addFacesByBottomIndex(bottomIndex, faces) + subsetTop = self.indexedLoopTop[self.startTop :] + subsetTop.append(self.indexedLoopTop[0]) + addFacesByConvexBottomTopLoop(faces, [self.indexedLoopBottom[0]], subsetTop[: : -1]) + + def addFacesByBottomIndex(self, bottomIndex, faces): + 'Add faces from the bottom index to the next index.' + bottomPoint = self.indexedLoopBottom[bottomIndex % len(self.indexedLoopBottom)] + bottomPointNext = self.indexedLoopBottom[(bottomIndex + 1) % len(self.indexedLoopBottom)] + topIndex = self.startTop + getClosestDistanceIndexToPoint(bottomPointNext, self.indexedLoopTop[self.startTop :]).index + topIndexPlusOne = topIndex + 1 + betweenIndex = self.getBetweenIndex(bottomPoint, bottomPointNext, topIndexPlusOne) + betweenIndexPlusOne = betweenIndex + 1 + subsetStart = self.indexedLoopTop[self.startTop : betweenIndexPlusOne] + subsetEnd = self.indexedLoopTop[betweenIndex : topIndexPlusOne] + addFacesByConvexBottomTopLoop(faces, [bottomPoint], subsetStart[: : -1]) + addFacesByConvexBottomTopLoop(faces, [bottomPoint, bottomPointNext], [self.indexedLoopTop[betweenIndex]]) + addFacesByConvexBottomTopLoop(faces, [bottomPointNext], subsetEnd[: : -1]) + self.startTop = topIndex + + def getBetweenIndex(self, bottomPoint, bottomPointNext, topIndexPlusOne): + 'Get the index of the last point along the loop which is closer to the bottomPoint.' + betweenIndex = self.startTop + bottomPointComplex = bottomPoint.dropAxis() + bottomPointNextComplex = bottomPointNext.dropAxis() + for topPointIndex in xrange(self.startTop, topIndexPlusOne): + topPointComplex = self.indexedLoopTop[topPointIndex].dropAxis() + if abs(topPointComplex - bottomPointComplex) > abs(topPointComplex - bottomPointNextComplex): + return betweenIndex + betweenIndex = topPointIndex + return betweenIndex + + +class TriangleMesh( group.Group ): + 'A triangle mesh.' + def __init__(self): + 'Add empty lists.' + group.Group.__init__(self) + self.belowLoops = [] + self.edges = [] + self.faces = [] + self.importCoarseness = 1.0 + self.isCorrectMesh = True + self.loopLayers = [] + self.oldChainTetragrid = None + self.transformedVertexes = None + self.vertexes = [] + + def addXMLSection(self, depth, output): + 'Add the xml section for this object.' + xml_simple_writer.addXMLFromVertexes( depth, output, self.vertexes ) + xml_simple_writer.addXMLFromObjects( depth, self.faces, output ) + + def getCarveBoundaryLayers(self): + 'Get the boundary layers.' + if self.getMinimumZ() == None: + return [] + halfHeight = 0.5 * self.layerHeight + self.zoneArrangement = ZoneArrangement(self.layerHeight, self.getTransformedVertexes()) + layerTop = self.cornerMaximum.z - halfHeight * 0.5 + z = self.cornerMinimum.z + halfHeight + while z < layerTop: + getLoopLayerAppend(self.loopLayers, z).loops = self.getLoopsFromMesh(self.zoneArrangement.getEmptyZ(z)) + z += self.layerHeight + 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 getCarveLayerHeight(self): + 'Get the layer height.' + return self.layerHeight + + def getFabmetheusXML(self): + 'Return the fabmetheus XML.' + return None + + def getGeometryOutput(self): + 'Get geometry output dictionary.' + return getGeometryOutputByFacesVertexes(self.faces, self.vertexes) + + def getInterpretationSuffix(self): + 'Return the suffix for a triangle mesh.' + return 'xml' + + def getLoops(self, importRadius, z): + 'Get loops sliced through shape.' + self.importRadius = importRadius + return self.getLoopsFromMesh(z) + + def getLoopsFromMesh( self, z ): + 'Get loops from a carve of a mesh.' + originalLoops = [] + self.setEdgesForAllFaces() + if self.isCorrectMesh: + originalLoops = getLoopsFromCorrectMesh( self.edges, self.faces, self.getTransformedVertexes(), z ) + if len( originalLoops ) < 1: + originalLoops = getLoopsFromUnprovenMesh( self.edges, self.faces, self.importRadius, self.getTransformedVertexes(), z ) + loops = euclidean.getSimplifiedLoops(originalLoops, self.importRadius) + sortLoopsInOrderOfArea(True, loops) + return getOrientedLoops(loops) + + def getMinimumZ(self): + 'Get the minimum z.' + self.cornerMaximum = Vector3(-987654321.0, -987654321.0, -987654321.0) + self.cornerMinimum = Vector3(987654321.0, 987654321.0, 987654321.0) + transformedVertexes = self.getTransformedVertexes() + if len(transformedVertexes) < 1: + return None + for point in transformedVertexes: + self.cornerMaximum.maximize(point) + self.cornerMinimum.minimize(point) + return self.cornerMinimum.z + + def getTransformedVertexes(self): + 'Get all transformed vertexes.' + if self.elementNode == None: + return self.vertexes + chainTetragrid = self.getMatrixChainTetragrid() + if self.oldChainTetragrid != chainTetragrid: + self.oldChainTetragrid = matrix.getTetragridCopy(chainTetragrid) + self.transformedVertexes = None + if self.transformedVertexes == None: + if len(self.edges) > 0: + self.edges[0].zMinimum = None + self.transformedVertexes = matrix.getTransformedVector3s(chainTetragrid, self.vertexes) + return self.transformedVertexes + + def getTriangleMeshes(self): + 'Get all triangleMeshes.' + return [self] + + def getVertexes(self): + 'Get all vertexes.' + self.transformedVertexes = None + return self.vertexes + + 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 + + def setEdgesForAllFaces(self): + 'Set the face edges of all the faces.' + edgeTable = {} + for face in self.faces: + face.setEdgeIndexesToVertexIndexes( self.edges, edgeTable ) + + +class ZoneArrangement: + 'A zone arrangement.' + def __init__(self, layerHeight, vertexes): + 'Initialize the zone interval and the zZone table.' + self.zoneInterval = layerHeight / math.sqrt(len(vertexes)) / 1000.0 + self.zZoneSet = set() + for point in vertexes: + zoneIndexFloat = point.z / self.zoneInterval + self.zZoneSet.add(math.floor(zoneIndexFloat)) + self.zZoneSet.add(math.ceil(zoneIndexFloat )) + + def getEmptyZ(self, z): + 'Get the first z which is not in the zone table.' + zoneIndex = round(z / self.zoneInterval) + if zoneIndex not in self.zZoneSet: + return z + zoneAround = 1 + while 1: + zoneDown = zoneIndex - zoneAround + if zoneDown not in self.zZoneSet: + return zoneDown * self.zoneInterval + zoneUp = zoneIndex + zoneAround + if zoneUp not in self.zZoneSet: + return zoneUp * self.zoneInterval + zoneAround += 1 diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/solids/union.py b/SkeinPyPy/fabmetheus_utilities/geometry/solids/union.py new file mode 100644 index 0000000..7c30dea --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/solids/union.py @@ -0,0 +1,39 @@ +""" +Boolean geometry union of solids. + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.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 \nArt of Illusion ' +__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 union xml element.' + group.convertContainerElementNode(elementNode, geometryOutput, Union()) + +def getNewDerivation(elementNode): + 'Get new derivation.' + return evaluate.EmptyObject(elementNode) + +def processElementNode(elementNode): + 'Process the xml element.' + evaluate.processArchivable(Union, elementNode) + + +class Union(difference.Difference): + 'A difference object.' + def getLoopsFromObjectLoopsList(self, importRadius, visibleObjectLoopsList): + 'Get loops from visible object loops list.' + return self.getUnion(importRadius, visibleObjectLoopsList) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/statements/__init__.py b/SkeinPyPy/fabmetheus_utilities/geometry/statements/__init__.py new file mode 100644 index 0000000..1121e8a --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/statements/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 3 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/statements/_print.py b/SkeinPyPy/fabmetheus_utilities/geometry/statements/_print.py new file mode 100644 index 0000000..716dc95 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/statements/_print.py @@ -0,0 +1,63 @@ +""" +Print statement. + +There is also the print attribute in geometry_utilities/evaluate_fundamentals/print.py + +The model is xml_models/geometry_utilities/evaluate_fundamentals/print.xml + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_utilities import evaluate + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getLocalDictionary( attributesKey, elementNode): + "Get the local dictionary." + xmlProcessor = elementNode.getXMLProcessor() + if len( xmlProcessor.functions ) < 1: + return None + return xmlProcessor.functions[-1].localDictionary + +def printAttributesKey( attributesKey, elementNode): + "Print the attributesKey." + if attributesKey.lower() == '_localdictionary': + localDictionary = getLocalDictionary( attributesKey, elementNode) + if localDictionary != None: + localDictionaryKeys = localDictionary.keys() + attributeValue = elementNode.attributes[attributesKey] + if attributeValue != '': + attributeValue = ' - ' + attributeValue + print('Local Dictionary Variables' + attributeValue ) + localDictionaryKeys.sort() + for localDictionaryKey in localDictionaryKeys: + print('%s: %s' % ( localDictionaryKey, localDictionary[ localDictionaryKey ] ) ) + return + value = elementNode.attributes[attributesKey] + evaluatedValue = None + if value == '': + evaluatedValue = evaluate.getEvaluatedExpressionValue(elementNode, attributesKey) + else: + evaluatedValue = evaluate.getEvaluatedExpressionValue(elementNode, value) + print('%s: %s' % ( attributesKey, evaluatedValue ) ) + +def processElementNode(elementNode): + "Process the xml element." + if len(elementNode.getTextContent()) > 1: + print(elementNode.getTextContent()) + return + attributesKeys = elementNode.attributes.keys() + if len( attributesKeys ) < 1: + print('') + return + attributesKeys.sort() + for attributesKey in attributesKeys: + printAttributesKey( attributesKey, elementNode) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/statements/class.py b/SkeinPyPy/fabmetheus_utilities/geometry/statements/class.py new file mode 100644 index 0000000..cb74053 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/statements/class.py @@ -0,0 +1,19 @@ +""" +Class. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def processElementNode(elementNode): + 'Process the xml element.' + pass diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/statements/elif.py b/SkeinPyPy/fabmetheus_utilities/geometry/statements/elif.py new file mode 100644 index 0000000..5da7d59 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/statements/elif.py @@ -0,0 +1,25 @@ +""" +Polygon path. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_utilities import evaluate + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def processElementNode(elementNode): + "Process the xml element." + pass + +def processElse(elementNode): + "Process the else statement." + evaluate.processCondition(elementNode) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/statements/else.py b/SkeinPyPy/fabmetheus_utilities/geometry/statements/else.py new file mode 100644 index 0000000..708c167 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/statements/else.py @@ -0,0 +1,30 @@ +""" +Polygon path. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_utilities import evaluate + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def processElementNode(elementNode): + "Process the xml element." + pass + +def processElse(elementNode): + "Process the else statement." + functions = elementNode.getXMLProcessor().functions + if len(functions) < 1: + print('Warning, "else" element is not in a function in processElse in else.py for:') + print(elementNode) + return + functions[-1].processChildNodes(elementNode) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/statements/for.py b/SkeinPyPy/fabmetheus_utilities/geometry/statements/for.py new file mode 100644 index 0000000..954e9b2 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/statements/for.py @@ -0,0 +1,72 @@ +""" +Polygon path. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_utilities import evaluate + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def processChildNodesByIndexValue( elementNode, function, index, indexValue, value ): + "Process childNodes by index value." + if indexValue.indexName != '': + function.localDictionary[ indexValue.indexName ] = index + if indexValue.valueName != '': + function.localDictionary[ indexValue.valueName ] = value + function.processChildNodes(elementNode) + +def processElementNode(elementNode): + "Process the xml element." + if elementNode.xmlObject == None: + elementNode.xmlObject = IndexValue(elementNode) + if elementNode.xmlObject.inSplitWords == None: + return + xmlProcessor = elementNode.getXMLProcessor() + if len( xmlProcessor.functions ) < 1: + print('Warning, "for" element is not in a function in processElementNode in for.py for:') + print(elementNode) + return + function = xmlProcessor.functions[-1] + inValue = evaluate.getEvaluatedExpressionValueBySplitLine(elementNode, elementNode.xmlObject.inSplitWords) + if inValue.__class__ == list or inValue.__class__ == str: + for index, value in enumerate( inValue ): + processChildNodesByIndexValue( elementNode, function, index, elementNode.xmlObject, value ) + return + if inValue.__class__ == dict: + inKeys = inValue.keys() + inKeys.sort() + for inKey in inKeys: + processChildNodesByIndexValue( elementNode, function, inKey, elementNode.xmlObject, inValue[ inKey ] ) + + +class IndexValue: + "Class to get the in attribute, the index name and the value name." + def __init__(self, elementNode): + "Initialize." + self.inSplitWords = None + self.indexName = '' + if 'index' in elementNode.attributes: + self.indexName = elementNode.attributes['index'] + self.valueName = '' + if 'value' in elementNode.attributes: + self.valueName = elementNode.attributes['value'] + if 'in' in elementNode.attributes: + self.inSplitWords = evaluate.getEvaluatorSplitWords( elementNode.attributes['in'] ) + else: + print('Warning, could not find the "in" attribute in IndexValue in for.py for:') + print(elementNode) + return + if len( self.inSplitWords ) < 1: + self.inSplitWords = None + print('Warning, could not get split words for the "in" attribute in IndexValue in for.py for:') + print(elementNode) + diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/statements/function.py b/SkeinPyPy/fabmetheus_utilities/geometry/statements/function.py new file mode 100644 index 0000000..d758fa2 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/statements/function.py @@ -0,0 +1,19 @@ +""" +Polygon path. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def processElementNode(elementNode): + "Process the xml element." + pass diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/statements/if.py b/SkeinPyPy/fabmetheus_utilities/geometry/statements/if.py new file mode 100644 index 0000000..1519efd --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/statements/if.py @@ -0,0 +1,21 @@ +""" +Polygon path. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_utilities import evaluate + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def processElementNode(elementNode): + "Process the xml element." + evaluate.processCondition(elementNode) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/statements/return.py b/SkeinPyPy/fabmetheus_utilities/geometry/statements/return.py new file mode 100644 index 0000000..f8bb5c5 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/statements/return.py @@ -0,0 +1,33 @@ +""" +Polygon path. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_utilities import evaluate + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def processElementNode(elementNode): + "Process the xml element." + functions = elementNode.getXMLProcessor().functions + if len(functions) < 1: + return + function = functions[-1] + function.shouldReturn = True + if elementNode.xmlObject == None: + if 'return' in elementNode.attributes: + value = elementNode.attributes['return'] + elementNode.xmlObject = evaluate.getEvaluatorSplitWords(value) + else: + elementNode.xmlObject = [] + if len( elementNode.xmlObject ) > 0: + function.returnValue = evaluate.getEvaluatedExpressionValueBySplitLine(elementNode, elementNode.xmlObject) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/statements/statement.py b/SkeinPyPy/fabmetheus_utilities/geometry/statements/statement.py new file mode 100644 index 0000000..805d70e --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/statements/statement.py @@ -0,0 +1,50 @@ +""" +Polygon path. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_utilities import evaluate + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def processElementNode(elementNode): + "Process the xml element." + functions = elementNode.getXMLProcessor().functions + if len(functions) < 1: + print('Warning, there are no functions in processElementNode in statement for:') + print(elementNode) + return + function = functions[-1] + evaluate.setLocalAttribute(elementNode) + if elementNode.xmlObject.value == None: + print('Warning, elementNode.xmlObject.value is None in processElementNode in statement for:') + print(elementNode) + return + localValue = evaluate.getEvaluatedExpressionValueBySplitLine(elementNode, elementNode.xmlObject.value) + keywords = elementNode.xmlObject.key.split('.') + if len(keywords) == 0: + print('Warning, there are no keywords in processElementNode in statement for:') + print(elementNode) + return + firstWord = keywords[0] + if len(keywords) == 1: + function.localDictionary[firstWord] = localValue + return + attributeName = keywords[-1] + object = None + if firstWord == 'self': + object = function.classObject + else: + object = function.localDictionary[firstWord] + for keywordIndex in xrange(1, len(keywords) - 1): + object = object._getAccessibleAttribute(keywords[keywordIndex]) + object._setAccessibleAttribute(attributeName, localValue) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry/statements/while.py b/SkeinPyPy/fabmetheus_utilities/geometry/statements/while.py new file mode 100644 index 0000000..314f0df --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry/statements/while.py @@ -0,0 +1,34 @@ +""" +Polygon path. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_utilities import evaluate + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Art of Illusion ' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def processElementNode(elementNode): + "Process the xml element." + if elementNode.xmlObject == None: + if 'condition' in elementNode.attributes: + value = elementNode.attributes['condition'] + elementNode.xmlObject = evaluate.getEvaluatorSplitWords(value) + else: + elementNode.xmlObject = [] + if len( elementNode.xmlObject ) < 1: + return + xmlProcessor = elementNode.getXMLProcessor() + if len( xmlProcessor.functions ) < 1: + return + function = xmlProcessor.functions[-1] + while evaluate.getEvaluatedExpressionValueBySplitLine(elementNode, elementNode.xmlObject) > 0: + function.processChildNodes(elementNode) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry_plugins/__init__.py b/SkeinPyPy/fabmetheus_utilities/geometry_plugins/__init__.py new file mode 100644 index 0000000..2dc8ddc --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry_plugins/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 2 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry_plugins/creation/__init__.py b/SkeinPyPy/fabmetheus_utilities/geometry_plugins/creation/__init__.py new file mode 100644 index 0000000..1121e8a --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry_plugins/creation/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 3 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry_plugins/manipulation_matrix/__init__.py b/SkeinPyPy/fabmetheus_utilities/geometry_plugins/manipulation_matrix/__init__.py new file mode 100644 index 0000000..1121e8a --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry_plugins/manipulation_matrix/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 3 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry_plugins/manipulation_meta/__init__.py b/SkeinPyPy/fabmetheus_utilities/geometry_plugins/manipulation_meta/__init__.py new file mode 100644 index 0000000..1121e8a --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry_plugins/manipulation_meta/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 3 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry_plugins/manipulation_paths/__init__.py b/SkeinPyPy/fabmetheus_utilities/geometry_plugins/manipulation_paths/__init__.py new file mode 100644 index 0000000..1121e8a --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry_plugins/manipulation_paths/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 3 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/geometry_plugins/manipulation_shapes/__init__.py b/SkeinPyPy/fabmetheus_utilities/geometry_plugins/manipulation_shapes/__init__.py new file mode 100644 index 0000000..1121e8a --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/geometry_plugins/manipulation_shapes/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 3 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/hidden_scrollbar.py b/SkeinPyPy/fabmetheus_utilities/hidden_scrollbar.py new file mode 100644 index 0000000..5b25846 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/hidden_scrollbar.py @@ -0,0 +1,28 @@ +""" +Hidden scrollbar is in its own file so that even if Tkinter is not installed, settings can still be imported. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ +try: + import Tkinter + class HiddenScrollbar(Tkinter.Scrollbar): + 'A class to hide the scrollbar if it is not needed.' + def set(self, lo, hi): + 'Add to grid is needed, remove if not.' + if float(lo) <= 0.0 and float(hi) >= 1.0: + self.grid_remove() + self.visible = False + else: + self.grid() + self.visible = True + Tkinter.Scrollbar.set(self, lo, hi) +except: + pass + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/23/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' diff --git a/SkeinPyPy/fabmetheus_utilities/images/display_line.ppm b/SkeinPyPy/fabmetheus_utilities/images/display_line.ppm new file mode 100644 index 0000000..4738d03 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/images/display_line.ppm @@ -0,0 +1,5 @@ +P6 +# CREATOR: The GIMP's PNM Filter Version 1.0 +27 27 +255 +ðôôðóóðóóðóóðóóðóóðóóðóóðóóðóóðóóðóóðóóðóóðóóðóóðóóðóóðóóðóóðóóðóóðóóðóóðóóðóóðóóðóóîòòîòòîòòîòòîòòîòòîòòîòòîòòîòòîòòîòòîòòîòòîòòîòòîòòîòòîòòîòòîòòîòòîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòîòòîòòîòòíññÙØZäâHîìMõóP÷õRôòRíìcåéééíííññîòòîòòîòòîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòîòòíññÜÞ«ÖÔCæäHïíM÷õRüúVÿÿ\ÿÿkÿÿ\õóTáâ¦äèèëïïíññîòòîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòîòòëïïÎÍ@àÞEëéKõóPüúVÿÿ~ÿÿÉÿÿãÿÿÉÿÿ~õóTÐÔÔãççëïïîòòîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòíññÄÃRÖÕ@ãáFîìLøöSÿÿ\ÿÿÉÿÿþÿÿÛÿÿþÿÿÉÿÿ\åä\ÐÔÔäèèíññîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòéííÄÃ:×Ö@äâGïíMù÷TÿÿkÿÿãÿÿÛÿýYÿÿÛÿÿãÿÿkòðQº½½ÙÜÜéííîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòåééÇÆ:ÖÕ@ãáFîìLøöSÿÿ\ÿÿÉÿÿþÿÿÛÿÿþÿÿÉÿÿ\õóQ¢¥¥ËÏÏåééîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòàääÆÄ9ÔÒ?àÞEëéKõóPüúVÿÿ~ÿÿÉÿÿãÿÿÉÿÿ~üúVõóP”——ÃÆÆàääîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòÞâ⿾6ÏÍ=ÛÙBæäHïíM÷õRüúVÿÿ\ÿÿkÿÿ\üúV÷õRìêKŽ¾ÂÂÞââîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòÞâⱯ4ÈÇ:ÕÓ?ßÝDèæIïíMõóPøöSù÷TøöSõóPïíMàÞEŽ¾ÂÂÞââîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòÞâ⢢CÀ¿7ÌÊ<ÖÕ@ßÝDæäHëéKîìLïíMîìLëéKæäHÆÄGŽ¾ÂÂÞââîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòàääÃÆÆ©¨2ÂÀ8ÌÊ<ÕÓ?ÛÙBàÞEãáFäâGãáFàÞEÑÏ@rtt”——ÃÆÆàääîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòåééËÏÏœw¨¦2ÑÐ@óñRÿýYôòSæåWÿýYôòSÉÇ=ŠŠV}¢¥¥ËÏÏåééîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòéííÙÜܺ½½“––ÞÜCæäHðîNûùWòòtÿÿ×æåLfggwyy“––º½½ÙÜÜéííîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòíññäèèÐÔÔ¯²²ÞÜCëéJöôQÿýYÿÿÍÿÿãÿýYrttŽ¯²²ÐÔÔäèèíññîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòîòòëïïãççÊÍÍÞÜCëéJöôQÿýYÿÿÑÿÿåÿýYƒƒ¨««ÊÍÍãççëïïîòòîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòîòòíññëïïÙÜÜÞÜCëéJöôQÿýYÿÿÑÿÿåÿýY‰ŒŒ·ººÙÜÜëïïíññîòòîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòîòòîòòîòòÞâ⃃ƒ›››±±±ÄÄÄÓÓÓüüüÓÓÓŽ½ÁÁÞââîòòîòòîòòîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòîòòîòòîòòÞâ⎎™šš¬¬¬­®®¹ººÙÚÚ½½½’’¿ÃÃÞââîòòîòòîòòîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòîòòîòòîòòßãã‹‹‹ššš¬¬¬½½½ËËËñññÌÌÌ“••ÀÃÃßããîòòîòòîòòîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòîòòîòòîòòàää‘’’š››¤¤¤¶¶¶£¤¤ÐÑÑÂÃÙœœÃÆÆàääîòòîòòîòòîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòîòòîòòîòòâæ涸¸›››®®®ÀÀÀÂÃÃää䧨¨ž  ÆÉÉáååîòòîòòîòòîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòîòòîòòîòòåééÉÍÍ›››±±±ÄÄÄÓÓÓüüü™œœ¦©©ËÏÏãççîòòîòòîòòîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòîòòîòòîòòèììÕØص¸¸“••€‚‚z||„††•˜˜²µµÓÖÖçêêîòòîòòîòòîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòîòòîòòîòòìððßããÈËË«®®›žž”——œžž¬¯¯ÅÈÈÜààêîîîòòîòòîòòîòòîòòîòòîòòðóóîòòîòòîòòîòòîòòîòòîòòîòòíññçëëÜààÍÑÑÃÆÆ¿ÃÃÃÆÆÌÐÐÚÞÞæééíññîòòîòòîòòîòòîòòîòòîòò \ No newline at end of file diff --git a/SkeinPyPy/fabmetheus_utilities/images/dive.ppm b/SkeinPyPy/fabmetheus_utilities/images/dive.ppm new file mode 100644 index 0000000..39b6e11 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/images/dive.ppm @@ -0,0 +1,5 @@ +P6 +# CREATOR: The GIMP's PNM Filter Version 1.0 +16 16 +255 +ÿÿÿÿÿÿÿÿÿÿÿÿìôÿØéÿÉáÿ¾Ûÿ¾ÛÿÉáÿØéÿìôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöúÿÙêÿÇÝùÐÞïÕÛãÖØÚÕ×ÙÑØáÍÛìÆÜøÙêÿöúÿÿÿÿÿÿÿÿÿÿöúÿÓæÿÓáòÙÝâèìñóöúûýþûýþòõùåéîÏÔÚÎÜîÓæÿöúÿÿÿÿÿÿÿÙêÿÓáòÖÚà÷øûÿÿÿöùüîóúíóúôøüÿÿÿóöùÇÌÓÍÛìÙêÿÿÿÿìôÿÇÝùÙÝâ÷øûÿÿÿëñùãì÷èïùèïùæïøßéöÿÿÿóöùÄÉÏÃÙõìôÿØéÿÐÝîèìñÿÿÿëñùãì÷YŒÍèïùæïøßéöX‹Ëãì÷ÿÿÿØßçÀÎßØéÿÉáÿÔÚâóöúôøüáë÷S„ÃYŒÍåîøÝèöN{´X‹Ëáë÷ñõûêïö¶½ÅÉáÿ¾ÛÿÔÖØûýþéðùRƒÀRƒÁX‹ËßéöMz³Mz´WŠÊßéöãì÷ùûý«­¯¾Ûÿ¾ÛÿÓÕ×ûýþìòúZŽÑQ€½X‹ËéðùU†ÅP~¹VˆÈÝèöáë÷ñõú§©ª¾ÛÿÉáÿÐ×ßïóøòöûäí÷YÐYŒÏàêöáë÷X‹ËX‹ÍÜçõîóúÛäדּ»ÉáÿØéÿÌÙëáåìþþÿáë÷ãì÷\ÒÝèöÝèößéö[ÑÚåõúüþ¿ÉÖµÃÔØéÿìôÿÄÚ÷ÈÌÒíñ÷ÿÿÿÝèöàêöÜçõÛæõÚåõÛæõþþÿÜä±¾ÔðìôÿÿÿÿÙêÿÉ×è·¾Æèîôþþÿïôûáë÷àêöíóúúüþÛäî–¡¬»ÉÛÙêÿÿÿÿÿÿÿöúÿÓæÿÅÒ䱸¾ÈÑÜáèñòöûîóúÖàí¸ÃÒ¤­ºÈÚÓæÿöúÿÿÿÿÿÿÿÿÿÿöúÿÙêÿÀÖò·ÅÖ¨¯·œŸ˜šœ£ª³±¿Ð¼ÓïÙêÿöúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìôÿØéÿÉáÿ¾Ûÿ¾ÛÿÉáÿØéÿìôÿÿÿÿÿÿÿÿÿÿÿÿÿ \ No newline at end of file diff --git a/SkeinPyPy/fabmetheus_utilities/images/soar.ppm b/SkeinPyPy/fabmetheus_utilities/images/soar.ppm new file mode 100644 index 0000000..9fb815d --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/images/soar.ppm @@ -0,0 +1,5 @@ +P6 +# CREATOR: The GIMP's PNM Filter Version 1.0 +16 16 +255 +ÿÿÿÿÿÿÿÿÿÿÿÿìôÿØéÿÉáÿ¾Ûÿ¾ÛÿÉáÿØéÿìôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿöúÿÙêÿÇÝùÐÞïÕÛãÖØÚÕ×ÙÑØáÍÛìÆÜøÙêÿöúÿÿÿÿÿÿÿÿÿÿöúÿÓæÿÓáòÙÝâèìñóöúûýþûýþòõùåéîÏÔÚÎÜîÓæÿöúÿÿÿÿÿÿÿÙêÿÓáòÖÚà÷øûÿÿÿöùüîóúíóúôøüÿÿÿóöùÇÌÓÍÛìÙêÿÿÿÿìôÿÇÝùÙÝâ÷øûÿÿÿÜçõéðùèïùèïùÚåõåîøÿÿÿóöùÄÉÏÃÙõìôÿØéÿÐÝîèìñÿÿÿëñùT…ÃÚåõèïùæïøRƒÁÕãóãì÷ÿÿÿØßçÀÎßØéÿÉáÿÔÚâóöúôøüèïùS„ÃS„ÁØåôäí÷RƒÀRƒÀÔâóñõûêïö¶½ÅÉáÿ¾ÛÿÔÖØûýþìòúèïùRƒÁRƒÁVˆÈÛæõRÀR¾U†ÆÛæõùûý«­¯¾Ûÿ¾ÛÿÓÕ×ûýþëñùåîøRƒÁU‡ÆRƒÀìòúQ€¾T…ÃQ€½ìòúñõú§©ª¾ÛÿÉáÿÐ×ßïóøòöûãì÷U‡ÆRÀìòúßéöT…ÃQ€½ëñùîóúÛäדּ»ÉáÿØéÿÌÙëáåìþþÿáë÷R¾ìòúÝèöÝèöQ½éðùÚåõúüþ¿ÉÖµÃÔØéÿìôÿÄÚ÷ÈÌÒíñ÷ÿÿÿëñùÝèöÜçõÛæõéðùØåôþþÿÜä±¾ÔðìôÿÿÿÿÙêÿÉ×è·¾Æèîôþþÿïôûáë÷àêöíóúúüþÛäî–¡¬»ÉÛÙêÿÿÿÿÿÿÿöúÿÓæÿÅÒ䱸¾ÈÑÜáèñòöûîóúÖàí¸ÃÒ¤­ºÈÚÓæÿöúÿÿÿÿÿÿÿÿÿÿöúÿÙêÿÀÖò·ÅÖ¨¯·œŸ˜šœ£ª³±¿Ð¼ÓïÙêÿöúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìôÿØéÿÉáÿ¾Ûÿ¾ÛÿÉáÿØéÿìôÿÿÿÿÿÿÿÿÿÿÿÿÿ \ No newline at end of file diff --git a/SkeinPyPy/fabmetheus_utilities/images/stop.ppm b/SkeinPyPy/fabmetheus_utilities/images/stop.ppm new file mode 100644 index 0000000..d1b9813 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/images/stop.ppm @@ -0,0 +1,5 @@ +P6 +# CREATOR: The GIMP's PNM Filter Version 1.0 +16 16 +255 +ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿùâáï³¯íª¦íª¥í¨¤ë¤ží©¥÷ÙØþüüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøáàò¹²üÓÂÿÖÁÿѽÿÏ»ÿιüȷퟘòÅÃÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøáàð·±üÔÄÿ£ÿ¯‹ÿ«‰ÿ§„ÿ ~ÿ®‘ûÃ´í¢œöÖÕÿÿÿÿÿÿÿÿÿøáßñµ¯üÑÁÿ¿ ÿª†ÿ§…ÿ¢ÿ|ÿ˜xÿoÿ¡…ûº¬ê—öÓÒÿÿÿÿÿÿüͼÿ»œÿ¥ÿ¢ÿž|ÿ™xÿ“tÿŽoÿ‰iÿ€`ÿ“xû­žå…ÿÿÿÿÿÿê˜ÿ͸ÿ }ÿ|ÿ™xÿ“sÿŽnÿ‰iÿƒeÿ~`ÿxYÿpOÿª˜ârkÿÿÿÿÿÿéš•ÿıÿ˜vÿ“sÿŽnÿ‰iÿƒeÿ~_ÿyZÿsUÿnOÿgHÿ¡áohÿÿÿÿÿÿè”ÿ¾«ÿmÿ‰iÿƒeÿ~_ÿyZÿtUÿoPÿiKÿcFÿ\?ÿ›Šßibÿÿÿÿÿÿ抄ÿ»¦ÿ_ÿ~_ÿyZÿsUÿnPÿiKÿdFÿ^AÿY;ÿO1ÿ™‡Ý^Vÿÿÿÿÿÿ莈ü²¢ÿsÿpOÿnOÿiKÿdFÿ^AÿZ<ÿU7ÿK,ÿgNú“„Þf_ÿÿÿÿÿÿõÒÐ鎇û­Ÿÿgÿ^@ÿ^@ÿY<ÿU7ÿP3ýF(ýeJö–‰áneòÃÁÿÿÿÿÿÿÿÿÿôÌÊç†~ú¥–ÿtZÿP2ÿO0þI,úB%ù`Hô”†âpiñÀ½ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóÈÆç|tú—‰ÿ•„üúŽ}÷}ò‹}ÝYQꟛÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿóÆÄàkeÜYRÜ[SÚXPÚRIÜ`Yðº·þúúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ \ No newline at end of file diff --git a/SkeinPyPy/fabmetheus_utilities/images/view_move.ppm b/SkeinPyPy/fabmetheus_utilities/images/view_move.ppm new file mode 100644 index 0000000..d566b1e Binary files /dev/null and b/SkeinPyPy/fabmetheus_utilities/images/view_move.ppm differ diff --git a/SkeinPyPy/fabmetheus_utilities/images/view_rotate.ppm b/SkeinPyPy/fabmetheus_utilities/images/view_rotate.ppm new file mode 100644 index 0000000..49ca6be Binary files /dev/null and b/SkeinPyPy/fabmetheus_utilities/images/view_rotate.ppm differ diff --git a/SkeinPyPy/fabmetheus_utilities/images/zoom_in.ppm b/SkeinPyPy/fabmetheus_utilities/images/zoom_in.ppm new file mode 100644 index 0000000..6f4767f --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/images/zoom_in.ppm @@ -0,0 +1,5 @@ +P6 +# CREATOR: The GIMP's PNM Filter Version 1.0 +16 16 +255 +ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôìÜÝÑҮmÏ©eΧcШjÙ»ŒóèÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìÜ¿Ô°p×·}æѯ÷ðåöïääάҭwÌ¡gçÓºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôìÜÔ°påÏ©÷úýïõûêòúëóúñöûùûýßţɛcñæØÿÿÿÿÿÿÿÿÿÿÿÿÝ‘׶}÷úýëóúèñùéòùéòúêòúîõûùûýͤqÒ®„ÿÿÿÿÿÿÿÿÿÿÿÿÒ­læÑ®ðöûèñùéòùéòúêòúëóúëóúò÷üßħÒ[ÿÿÿÿÿÿÿÿÿÿÿÿϨc÷ïåëóúéòúêòúêòúëóúg¶\a°W\¨Rôëâ¿‹Pÿÿÿÿÿÿÿÿÿÿÿÿͦböïäìóúêòúêòúëóúëóú_­V¦ÕSžKôë⾇Mÿÿÿÿÿÿÿÿÿÿÿÿͧhä̬ñ÷üëóúëóúc²Y^«Tv·mžÑ•k¬cD‹=>ƒ7ÿÿÿÿÿÿÿÿÿÿÿÿع‹Ñªvùûýïõûìôú\¨RŸÒ—šÏ’†Å}ʈ‹Æƒ5y0ÿÿÿÿÿÿÿÿÿÿÿÿòçÙÊŸeÞÄ£ùûþóøüSžKM–Ee§^‹Ç…Z›S3v..o)ãÍ»ÿÿÿÿÿÿÿÿÿÿÿÿæѹȚ`Ê oÞçôëâôëâ>ƒ7‚Â}2t,ãÌ™ÞļƒOøñíÿÿÿÿÿÿÿÿÿÿÿÿñåØÑ«ƒÁZ¾‡M½…L5y00q+*k&èÕ£é×¥Ú»€¬e?÷ñíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâ˹ÁŽXßÅçÓ àÇ“É›a—aFÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáɸ¼„Q×µ}ÖµÑV¶{Qÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáɸ®mH¹€H·yRúöôÿÿÿ \ No newline at end of file diff --git a/SkeinPyPy/fabmetheus_utilities/images/zoom_out.ppm b/SkeinPyPy/fabmetheus_utilities/images/zoom_out.ppm new file mode 100644 index 0000000..108f893 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/images/zoom_out.ppm @@ -0,0 +1,5 @@ +P6 +# CREATOR: The GIMP's PNM Filter Version 1.0 +16 16 +255 +ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôìÜÝÑҮmÏ©eΧcШjÙ»ŒóèÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿìÜ¿Ô°p×·}æѯ÷ðåöïääάҭwÌ¡gçÓºÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôìÜÔ°påÏ©÷úýïõûêòúëóúñöûùûýßţɛcñæØÿÿÿÿÿÿÿÿÿÿÿÿÝ‘׶}÷úýëóúèñùéòùéòúêòúîõûùûýͤqÒ®„ÿÿÿÿÿÿÿÿÿÿÿÿÒ­læÑ®ðöûèñùéòùéòúêòúëóúëóúò÷üßħÒ[ÿÿÿÿÿÿÿÿÿÿÿÿϨc÷ïåëóúéòúêòúêòúëóúëóúìóúîõûôëâ¿‹Pÿÿÿÿÿÿÿÿÿÿÿÿͦböïäìóúêòúêòúëóúëóúìóúìôúïõûôë⾇Mÿÿÿÿÿÿÿÿÿÿÿÿͧhä̬ñ÷üëóúëóúþxtüomúcb÷TVôEHñ5:î&.ÿÿÿÿÿÿÿÿÿÿÿÿع‹ÑªvùûýïõûìôúüomøÈ¿÷À·ö¶®ô«¤ó ›ì#ÿÿÿÿÿÿÿÿÿÿÿÿòçÙÊŸeÞÄ£ùûþóøüúcb÷TVôEHñ5:î&.ì#êãÍ»ÿÿÿÿÿÿÿÿÿÿÿÿæѹȚ`Ê oÞçôëâôëâܾ¤ÁSÖ´yãÌ™ÞļƒOøñíÿÿÿÿÿÿÿÿÿÿÿÿñåØÑ«ƒÁZ¾‡M½…L»†R»LÛ¿‡èÕ£é×¥Ú»€¬e?÷ñíÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿâ˹ÁŽXßÅçÓ àÇ“É›a—aFÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿáɸ¼„Q×µ}ÖµÑV±rEÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàǶ¥\3¹€H³sKøòïÿÿÿ \ No newline at end of file diff --git a/SkeinPyPy/fabmetheus_utilities/intercircle.py b/SkeinPyPy/fabmetheus_utilities/intercircle.py new file mode 100644 index 0000000..3c4e602 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/intercircle.py @@ -0,0 +1,725 @@ +""" +Intercircle is a collection of utilities for intersecting circles, used to get smooth loops around a collection of points and inset & outset loops. + +""" + +from __future__ import absolute_import +try: + import psyco + psyco.full() +except: + pass +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +import math + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalDecreasingRadiusMultipliers = [1.0, 0.55, 0.35, 0.2] +globalIntercircleMultiplier = 1.04 # 1.02 is enough to stop known intersection + + +def addCircleIntersectionLoop(circleIntersectionLoop, circleIntersections): + 'Add a circle intersection loop.' + firstCircleIntersection = circleIntersectionLoop[0] + circleIntersectionAhead = firstCircleIntersection + for circleIntersectionIndex in xrange(len(circleIntersections) + 1): + circleIntersectionAhead = circleIntersectionAhead.getCircleIntersectionAhead() + if circleIntersectionAhead == firstCircleIntersection or circleIntersectionAhead == None: + firstCircleIntersection.steppedOn = True + return + circleIntersectionAhead.addToList(circleIntersectionLoop) + firstCircleIntersection.steppedOn = True + print('Warning, addCircleIntersectionLoop would have gone into an endless loop.') + print('circleIntersectionLoop') + for circleIntersection in circleIntersectionLoop: + print(circleIntersection) + print(circleIntersection.circleNodeAhead) + print(circleIntersection.circleNodeBehind) + print('firstCircleIntersection') + print(firstCircleIntersection) + print('circleIntersections') + for circleIntersection in circleIntersections: + print(circleIntersection) + +def addEndCap(begin, end, points, radius): + 'Get circular end cap.' + beginMinusEnd = begin - end + beginMinusEndLength = abs(beginMinusEnd) + if beginMinusEndLength <= 0.0: + points.append(begin) + return + beginMinusEnd *= radius / beginMinusEndLength + perpendicular = complex(-beginMinusEnd.imag, beginMinusEnd.real) + numberOfSides = 20 # to end up with close to unit length corners, 5 * 4 + numberOfPositiveSides = numberOfSides / 2 + totalAngle = 0.0 + angle = euclidean.globalTau / float(numberOfSides) + # dotProductMultiplier to compensate for the corner outset in addInsetPointFromClockwiseTriple + dotProductMultiplier = 2.0 - 1.0 / math.cos(0.5 * angle) + for sideIndex in xrange(numberOfPositiveSides + 1): + circumferentialPoint = math.sin(totalAngle) * beginMinusEnd + math.cos(totalAngle) * perpendicular + points.append(begin + circumferentialPoint * dotProductMultiplier) + totalAngle += angle + +def addHalfPath(path, points, radius, thresholdRatio=0.9): + 'Add the points from every point on a half path and between points.' + lessThanRadius = 0.75 * radius + for pointIndex in xrange(len(path) - 1): + begin = path[pointIndex] + center = path[pointIndex + 1] + centerBegin = getWiddershinsByLength(begin, center, radius) + if centerBegin != None: + addPointsFromSegment(begin + centerBegin, center + centerBegin, points, lessThanRadius, thresholdRatio) + endIndex = pointIndex + 2 + if endIndex < len(path): + end = path[endIndex] + centerEnd = getWiddershinsByLength(center, end, radius) + if centerBegin != None and centerEnd != None: + centerPerpendicular = 0.5 * (centerBegin + centerEnd) + points.append(center + centerPerpendicular) + if euclidean.getCrossProduct(centerBegin, centerEnd) < 0.0: + points.append(center + centerBegin) + points.append(center + centerEnd) + else: + points.append(center) + addEndCap(path[0], path[1], points, radius) + +def addInsetPointFromClockwiseTriple(begin, center, end, loop, radius): + 'Get inset point with possible intersection from clockwise triple, out from widdershins loop.' + centerMinusBegin = center - begin + centerMinusBeginLength = abs(centerMinusBegin) + centerMinusBeginClockwise = None + if centerMinusBeginLength > 0.0: + centerMinusBeginClockwise = complex(centerMinusBegin.imag, -centerMinusBegin.real) / centerMinusBeginLength + endMinusCenter = end - center + endMinusCenterLength = abs(endMinusCenter) + endMinusCenterClockwise = None + if endMinusCenterLength > 0.0: + endMinusCenterClockwise = complex(endMinusCenter.imag, -endMinusCenter.real) / endMinusCenterLength + if centerMinusBeginClockwise == None and endMinusCenterClockwise == None: + return + if centerMinusBeginClockwise == None: + loop.append(center + endMinusCenterClockwise * radius) + return + if endMinusCenterClockwise == None: + loop.append(center + centerMinusBeginClockwise * radius) + return + centerClockwise = 0.5 * (centerMinusBeginClockwise + endMinusCenterClockwise) + dotProduct = euclidean.getDotProduct(centerMinusBeginClockwise, centerClockwise) + loop.append(center + centerClockwise * radius / max(0.4, abs(dotProduct))) # 0.4 to avoid pointy corners + +def addOrbits( distanceFeedRate, loop, orbitalFeedRatePerSecond, temperatureChangeTime, z ): + 'Add orbits with the extruder off.' + timeInOrbit = 0.0 + while timeInOrbit < temperatureChangeTime: + for point in loop: + distanceFeedRate.addGcodeMovementZWithFeedRate( 60.0 * orbitalFeedRatePerSecond, point, z ) + timeInOrbit += euclidean.getLoopLength(loop) / orbitalFeedRatePerSecond + +def addOrbitsIfLarge( distanceFeedRate, loop, orbitalFeedRatePerSecond, temperatureChangeTime, z ): + 'Add orbits with the extruder off if the orbits are large enough.' + if orbitsAreLarge( loop, temperatureChangeTime ): + addOrbits( distanceFeedRate, loop, orbitalFeedRatePerSecond, temperatureChangeTime, z ) + +def addPointsFromSegment( pointBegin, pointEnd, points, radius, thresholdRatio=0.9 ): + 'Add point complexes between the endpoints of a segment.' + if radius <= 0.0: + print('This should never happen, radius should never be zero or less in addPointsFromSegment in intercircle.') + thresholdRadius = radius * thresholdRatio # a higher number would be faster but would leave bigger dangling loops and extra dangling loops. + thresholdDiameter = thresholdRadius + thresholdRadius + segment = pointEnd - pointBegin + segmentLength = abs(segment) + extraCircles = int( math.floor( segmentLength / thresholdDiameter ) ) + if extraCircles < 1: + return + if segmentLength == 0.0: + print('Warning, segmentLength = 0.0 in intercircle.') + print('pointBegin') + print(pointBegin) + print(pointEnd) + return + if extraCircles < 2: + lengthIncrement = segmentLength / ( float(extraCircles) + 1.0 ) + segment *= lengthIncrement / segmentLength + pointBegin += segment + else: + pointBegin += segment * thresholdDiameter / segmentLength + remainingLength = segmentLength - thresholdDiameter - thresholdDiameter + lengthIncrement = remainingLength / ( float(extraCircles) - 1.0 ) + segment *= lengthIncrement / segmentLength + for circleIndex in xrange(extraCircles): + points.append( pointBegin ) + pointBegin += segment + +def directLoop(isWiddershins, loop): + 'Direct the loop.' + if euclidean.isWiddershins(loop) != isWiddershins: + loop.reverse() + +def directLoopLists(isWiddershins, loopLists): + 'Direct the loop lists.' + for loopList in loopLists: + directLoops(isWiddershins, loopList) + +def directLoops(isWiddershins, loops): + 'Direct the loops.' + for loop in loops: + directLoop(isWiddershins, loop) + +def getAroundsFromLoop(loop, radius, thresholdRatio=0.9): + 'Get the arounds from the loop.' + return getAroundsFromPoints(getPointsFromLoop(loop, radius, thresholdRatio), radius) + +def getAroundsFromLoops( loops, radius, thresholdRatio=0.9 ): + 'Get the arounds from the loops.' + return getAroundsFromPoints(getPointsFromLoops(loops, radius, thresholdRatio), radius) + +def getAroundsFromPath(path, radius, thresholdRatio=0.9): + 'Get the arounds from the path.' + radius = abs(radius) + points = getPointsFromPath(path, radius, thresholdRatio) + return getAroundsFromPathPoints(points, radius, thresholdRatio=0.9) + +def getAroundsFromPathPoints(points, radius, thresholdRatio=0.9): + 'Get the arounds from the path.' + centers = getCentersFromPoints(points, 0.8 * radius) + arounds = [] + for center in centers: + if euclidean.isWiddershins(center): + arounds.append(euclidean.getSimplifiedPath(center, radius)) + return arounds + +def getAroundsFromPaths(paths, radius, thresholdRatio=0.9): + 'Get the arounds from the path.' + radius = abs(radius) + points = [] + for path in paths: + points += getPointsFromPath(path, radius, thresholdRatio) + return getAroundsFromPathPoints(points, radius, thresholdRatio=0.9) + +def getAroundsFromPoints( points, radius ): + 'Get the arounds from the points.' + arounds = [] + radius = abs(radius) + centers = getCentersFromPoints(points, globalIntercircleMultiplier * radius) + for center in centers: + inset = getSimplifiedInsetFromClockwiseLoop(center, radius) + if isLargeSameDirection(inset, center, radius): + arounds.append(inset) + return arounds + +def getCentersFromCircleNodes( circleNodes, radius ): + 'Get the complex centers of the circle intersection loops from circle nodes.' + if len( circleNodes ) < 2: + return [] + circleIntersections = getCircleIntersectionsFromCircleNodes( circleNodes ) + circleIntersectionLoops = getCircleIntersectionLoops( circleIntersections ) + return getCentersFromIntersectionLoops( circleIntersectionLoops, radius ) + +def getCentersFromIntersectionLoop(circleIntersectionLoop, radius): + 'Get the centers from the intersection loop.' + loop = [] + for circleIntersection in circleIntersectionLoop: + loop.append(circleIntersection.circleNodeAhead.actualPoint) + return loop + +def getCentersFromIntersectionLoops( circleIntersectionLoops, radius ): + 'Get the centers from the intersection loops.' + centers = [] + for circleIntersectionLoop in circleIntersectionLoops: + centers.append( getCentersFromIntersectionLoop( circleIntersectionLoop, radius ) ) + return centers + +def getCentersFromLoop( loop, radius ): + 'Get the centers of the loop.' + circleNodes = getCircleNodesFromLoop( loop, radius ) + return getCentersFromCircleNodes( circleNodes, radius ) + +def getCentersFromLoopDirection( isWiddershins, loop, radius ): + 'Get the centers of the loop which go around in the given direction.' + centers = getCentersFromLoop( loop, radius ) + return getLoopsFromLoopsDirection( isWiddershins, centers ) + +def getCentersFromPoints(points, radius): + 'Get the centers from the points.' + circleNodes = getCircleNodesFromPoints(points, abs(radius)) + return getCentersFromCircleNodes(circleNodes, abs(radius)) + +def getCircleIntersectionLoops( circleIntersections ): + 'Get all the loops going through the circle intersections.' + circleIntersectionLoops = [] + for circleIntersection in circleIntersections: + if not circleIntersection.steppedOn: + circleIntersectionLoop = [ circleIntersection ] + circleIntersectionLoops.append( circleIntersectionLoop ) + addCircleIntersectionLoop( circleIntersectionLoop, circleIntersections ) + return circleIntersectionLoops + +def getCircleIntersectionsFromCircleNodes(circleNodes): + 'Get all the circle intersections which exist between all the circle nodes.' + if len( circleNodes ) < 1: + return [] + circleIntersections = [] + index = 0 + pixelTable = {} + for circleNode in circleNodes: + euclidean.addElementToPixelListFromPoint(circleNode, pixelTable, circleNode.dividedPoint) + accumulatedCircleNodeTable = {} + for circleNodeIndex in xrange(len(circleNodes)): + circleNodeBehind = circleNodes[circleNodeIndex] + circleNodeIndexMinusOne = circleNodeIndex - 1 + if circleNodeIndexMinusOne >= 0: + circleNodeAdditional = circleNodes[circleNodeIndexMinusOne] + euclidean.addElementToPixelListFromPoint(circleNodeAdditional, accumulatedCircleNodeTable, 0.5 * circleNodeAdditional.dividedPoint) + withinNodes = circleNodeBehind.getWithinNodes(accumulatedCircleNodeTable) + for circleNodeAhead in withinNodes: + circleIntersectionForward = CircleIntersection(circleNodeAhead, index, circleNodeBehind) + if not circleIntersectionForward.isWithinCircles(pixelTable): + circleIntersections.append(circleIntersectionForward) + circleNodeBehind.circleIntersections.append(circleIntersectionForward) + index += 1 + circleIntersectionBackward = CircleIntersection(circleNodeBehind, index, circleNodeAhead) + if not circleIntersectionBackward.isWithinCircles(pixelTable): + circleIntersections.append(circleIntersectionBackward) + circleNodeAhead.circleIntersections.append(circleIntersectionBackward) + index += 1 + return circleIntersections + +def getCircleNodesFromLoop(loop, radius, thresholdRatio=0.9): + 'Get the circle nodes from every point on a loop and between points.' + radius = abs(radius) + points = getPointsFromLoop( loop, radius, thresholdRatio ) + return getCircleNodesFromPoints( points, radius ) + +def getCircleNodesFromPoints(points, radius): + 'Get the circle nodes from a path.' + if radius == 0.0: + print('Warning, radius is 0 in getCircleNodesFromPoints in intercircle.') + print(points) + return [] + circleNodes = [] + oneOverRadius = 1.000001 / radius # to avoid problem of accidentally integral radius + points = euclidean.getAwayPoints(points, radius) + for point in points: + circleNodes.append(CircleNode(oneOverRadius, point)) + return circleNodes + +def getInsetLoopsFromLoop(loop, radius, thresholdRatio=0.9): + 'Get the inset loops, which might overlap.' + if radius == 0.0: + return [loop] + isInset = radius > 0 + insetLoops = [] + isLoopWiddershins = euclidean.isWiddershins(loop) + arounds = getAroundsFromLoop(loop, radius, thresholdRatio) + for around in arounds: + leftPoint = euclidean.getLeftPoint(around) + shouldBeWithin = (isInset == isLoopWiddershins) + if euclidean.isPointInsideLoop(loop, leftPoint) == shouldBeWithin: + if isLoopWiddershins != euclidean.isWiddershins(around): + around.reverse() + insetLoops.append(around) + return insetLoops + +def getInsetLoopsFromLoops(loops, radius): + 'Get the inset loops, which might overlap.' + insetLoops = [] + for loop in loops: + insetLoops += getInsetLoopsFromLoop(loop, radius) + return insetLoops + +def getInsetLoopsFromVector3Loop(loop, radius, thresholdRatio=0.9): + 'Get the inset loops from vector3 loop, which might overlap.' + if len(loop) < 2: + return [loop] + loopComplex = euclidean.getComplexPath(loop) + loopComplexes = getInsetLoopsFromLoop(loopComplex, radius) + return euclidean.getVector3Paths(loopComplexes, loop[0].z) + +def getInsetSeparateLoopsFromLoops(loops, radius, thresholdRatio=0.9): + 'Get the separate inset loops.' + if radius == 0.0: + return loops + isInset = radius > 0 + insetSeparateLoops = [] + arounds = getAroundsFromLoops(loops, abs(radius), thresholdRatio) + for around in arounds: + if isInset == euclidean.getIsInFilledRegion(loops, around[0]): + if isInset: + around.reverse() + insetSeparateLoops.append(around) + return insetSeparateLoops + +def getInsetSeparateLoopsFromAroundLoops(loops, radius, radiusAround, thresholdRatio=0.9): + 'Get the separate inset loops.' + if radius == 0.0: + return loops + isInset = radius > 0 + insetSeparateLoops = [] + radius = abs(radius) + radiusAround = max(abs(radiusAround), radius) + points = getPointsFromLoops(loops, radiusAround, thresholdRatio) + centers = getCentersFromPoints(points, globalIntercircleMultiplier * radiusAround) + for center in centers: + inset = getSimplifiedInsetFromClockwiseLoop(center, radius) + if isLargeSameDirection(inset, center, radius): + if isInset == euclidean.getIsInFilledRegion(loops, inset[0]): + if isInset: + inset.reverse() + insetSeparateLoops.append(inset) + return insetSeparateLoops + +def getIsLarge(loop, radius): + 'Determine if the loop is large enough.' + return euclidean.getMaximumSpan(loop) > 2.01 * abs(radius) + +def getLargestCenterOutsetLoopFromLoop(loop, radius, thresholdRatio=0.9): + 'Get the largest circle outset loop from the loop.' + if radius == 0.0: + return loop + radius = abs(radius) + points = getPointsFromLoop(loop, radius, thresholdRatio) + centers = getCentersFromPoints(points, globalIntercircleMultiplier * radius) + largestCenterOutset = None + largestOutsetArea = -987654321.0 + for center in centers: + outset = getSimplifiedInsetFromClockwiseLoop(center, radius) + if isLargeSameDirection(outset, center, radius): + if euclidean.isPathInsideLoop(loop, outset) != euclidean.isWiddershins(loop): + centerOutset = CenterOutset(center, outset) + outsetArea = abs(euclidean.getAreaLoop(outset)) + if outsetArea > largestOutsetArea: + largestOutsetArea = outsetArea + largestCenterOutset = centerOutset + if largestCenterOutset == None: + return None + largestCenterOutset.center = euclidean.getSimplifiedLoop(largestCenterOutset.center, radius) + return largestCenterOutset + +def getLargestCenterOutsetLoopFromLoopRegardless(loop, radius): + 'Get the largest circle outset loop from the loop, even if the radius has to be shrunk and even if there is still no outset loop.' + global globalDecreasingRadiusMultipliers + for decreasingRadiusMultiplier in globalDecreasingRadiusMultipliers: + decreasingRadius = radius * decreasingRadiusMultiplier + largestCenterOutsetLoop = getLargestCenterOutsetLoopFromLoop(loop, decreasingRadius) + if largestCenterOutsetLoop != None: + return largestCenterOutsetLoop + return CenterOutset(loop, loop) + +def getLargestInsetLoopFromLoop(loop, radius): + 'Get the largest inset loop from the loop.' + loops = getInsetLoopsFromLoop(loop, radius) + return euclidean.getLargestLoop(loops) + +def getLargestInsetLoopFromLoopRegardless( loop, radius ): + 'Get the largest inset loop from the loop, even if the radius has to be shrunk and even if there is still no inset loop.' + global globalDecreasingRadiusMultipliers + for decreasingRadiusMultiplier in globalDecreasingRadiusMultipliers: + decreasingRadius = radius * decreasingRadiusMultiplier + largestInsetLoop = getLargestInsetLoopFromLoop( loop, decreasingRadius ) + if len( largestInsetLoop ) > 0: + return largestInsetLoop + print('This should never happen, there should always be a largestInsetLoop in getLargestInsetLoopFromLoopRegardless in intercircle.') + print(loop) + return loop + +def getLoopsFromLoopsDirection( isWiddershins, loops ): + 'Get the loops going round in a given direction.' + directionalLoops = [] + for loop in loops: + if euclidean.isWiddershins(loop) == isWiddershins: + directionalLoops.append(loop) + return directionalLoops + +def getPointsFromLoop(loop, radius, thresholdRatio=0.9): + 'Get the points from every point on a loop and between points.' + if radius == 0.0: + print('Warning, radius is 0 in getPointsFromLoop in intercircle.') + print(loop) + return loop + radius = abs(radius) + points = [] + for pointIndex in xrange(len(loop)): + pointBegin = loop[pointIndex] + pointEnd = loop[(pointIndex + 1) % len(loop)] + points.append( pointBegin ) + addPointsFromSegment( pointBegin, pointEnd, points, radius, thresholdRatio ) + return points + +def getPointsFromLoops(loops, radius, thresholdRatio=0.9): + 'Get the points from every point on a loop and between points.' + points = [] + for loop in loops: + points += getPointsFromLoop(loop, radius, thresholdRatio) + return points + +def getPointsFromPath(path, radius, thresholdRatio=0.9): + 'Get the points from every point on a path and between points.' + if len(path) < 1: + return [] + if len(path) < 2: + return path + radius = abs(radius) + points = [] + addHalfPath(path, points, radius, thresholdRatio) + addHalfPath(path[: : -1], points, radius, thresholdRatio) + return points + +def getSimplifiedInsetFromClockwiseLoop(loop, radius): + 'Get loop inset from clockwise loop, out from widdershins loop.' + inset = [] + for pointIndex, begin in enumerate(loop): + center = loop[(pointIndex + 1) % len(loop)] + end = loop[(pointIndex + 2) % len(loop)] + addInsetPointFromClockwiseTriple(begin, center, end, inset, radius) + return getWithoutIntersections(euclidean.getSimplifiedLoop(inset, radius)) + +def getWiddershinsByLength(begin, end, length): + 'Get the widdershins by length.' + endMinusBegin = end - begin + endMinusBeginLength = abs(endMinusBegin) + if endMinusBeginLength <= 0.0: + return None + endMinusBegin *= length / endMinusBeginLength + return complex(-endMinusBegin.imag, endMinusBegin.real) + +def getWithoutIntersections( loop ): + 'Get loop without intersections.' + lastLoopLength = len( loop ) + while lastLoopLength > 3: + removeIntersection( loop ) + if len( loop ) == lastLoopLength: + return loop + lastLoopLength = len( loop ) + return loop + +def isLargeSameDirection(inset, loop, radius): + 'Determine if the inset is in the same direction as the loop and it is large enough.' + if euclidean.isWiddershins(inset) != euclidean.isWiddershins(loop): + return False + return getIsLarge(inset, radius) and len(inset) > 2 + +def isLoopIntersectingLoop( anotherLoop, loop ): + 'Determine if the a loop is intersecting another loop.' + for pointIndex in xrange(len(loop)): + pointFirst = loop[pointIndex] + pointSecond = loop[(pointIndex + 1) % len(loop)] + segment = pointFirst - pointSecond + normalizedSegment = euclidean.getNormalized(segment) + segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag) + segmentFirstPoint = segmentYMirror * pointFirst + segmentSecondPoint = segmentYMirror * pointSecond + if euclidean.isLoopIntersectingInsideXSegment( anotherLoop, segmentFirstPoint.real, segmentSecondPoint.real, segmentYMirror, segmentFirstPoint.imag ): + return True + return False + +def orbitsAreLarge( loop, temperatureChangeTime ): + 'Determine if the orbits are large enough.' + if len(loop) < 1: + print('Zero length loop which was skipped over, this should never happen.') + return False + return temperatureChangeTime > 1.5 + +def removeIntersection( loop ): + 'Get loop without the first intersection.' + for pointIndex, ahead in enumerate(loop): + behind = loop[ ( pointIndex + len( loop ) - 1 ) % len( loop ) ] + behindEnd = loop[ ( pointIndex + len( loop ) - 2 ) % len( loop ) ] + behindMidpoint = 0.5 * ( behind + behindEnd ) + aheadEnd = loop[ (pointIndex + 1) % len( loop ) ] + aheadMidpoint = 0.5 * ( ahead + aheadEnd ) + normalizedSegment = behind - behindMidpoint + normalizedSegmentLength = abs( normalizedSegment ) + if normalizedSegmentLength > 0.0: + normalizedSegment /= normalizedSegmentLength + segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag) + behindRotated = segmentYMirror * behind + behindMidpointRotated = segmentYMirror * behindMidpoint + aheadRotated = segmentYMirror * ahead + aheadMidpointRotated = segmentYMirror * aheadMidpoint + y = behindRotated.imag + xIntersection = euclidean.getXIntersectionIfExists( aheadRotated, aheadMidpointRotated, y ) + if xIntersection != None: + if xIntersection > min( behindMidpointRotated.real, behindRotated.real ) and xIntersection < max( behindMidpointRotated.real, behindRotated.real ): + intersectionPoint = normalizedSegment * complex( xIntersection, y ) + loop[ ( pointIndex + len( loop ) - 1 ) % len( loop ) ] = intersectionPoint + del loop[pointIndex] + return + + +class BoundingLoop: + 'A class to hold a bounding loop composed of a minimum complex, a maximum complex and an outset loop.' + def __eq__(self, other): + 'Determine whether this bounding loop is identical to other one.' + if other == None: + return False + return self.minimum == other.minimum and self.maximum == other.maximum and self.loop == other.loop + + def __repr__(self): + 'Get the string representation of this bounding loop.' + return '%s, %s, %s' % ( self.minimum, self.maximum, self.loop ) + + def getFromLoop( self, loop ): + 'Get the bounding loop from a path.' + self.loop = loop + self.maximum = euclidean.getMaximumByComplexPath(loop) + self.minimum = euclidean.getMinimumByComplexPath(loop) + return self + + def getOutsetBoundingLoop( self, outsetDistance ): + 'Outset the bounding rectangle and loop by a distance.' + outsetBoundingLoop = BoundingLoop() + outsetBoundingLoop.maximum = self.maximum + complex( outsetDistance, outsetDistance ) + outsetBoundingLoop.minimum = self.minimum - complex( outsetDistance, outsetDistance ) + greaterThanOutsetDistance = 1.1 * outsetDistance + centers = getCentersFromLoopDirection( True, self.loop, greaterThanOutsetDistance ) + outsetBoundingLoop.loop = getSimplifiedInsetFromClockwiseLoop( centers[0], outsetDistance ) + return outsetBoundingLoop + + def isEntirelyInsideAnother( self, anotherBoundingLoop ): + 'Determine if this bounding loop is entirely inside another bounding loop.' + if self.minimum.imag < anotherBoundingLoop.minimum.imag or self.minimum.real < anotherBoundingLoop.minimum.real: + return False + if self.maximum.imag > anotherBoundingLoop.maximum.imag or self.maximum.real > anotherBoundingLoop.maximum.real: + return False + for point in self.loop: + if euclidean.getNumberOfIntersectionsToLeft( anotherBoundingLoop.loop, point ) % 2 == 0: + return False + return not isLoopIntersectingLoop( anotherBoundingLoop.loop, self.loop ) #later check for intersection on only acute angles + + def isOverlappingAnother( self, anotherBoundingLoop ): + 'Determine if this bounding loop is intersecting another bounding loop.' + if self.isRectangleMissingAnother( anotherBoundingLoop ): + return False + for point in self.loop: + if euclidean.getNumberOfIntersectionsToLeft( anotherBoundingLoop.loop, point ) % 2 == 1: + return True + for point in anotherBoundingLoop.loop: + if euclidean.getNumberOfIntersectionsToLeft( self.loop, point ) % 2 == 1: + return True + return isLoopIntersectingLoop( anotherBoundingLoop.loop, self.loop ) #later check for intersection on only acute angles + + def isOverlappingAnotherInList( self, boundingLoops ): + 'Determine if this bounding loop is intersecting another bounding loop in a list.' + for boundingLoop in boundingLoops: + if self.isOverlappingAnother( boundingLoop ): + return True + return False + + def isRectangleMissingAnother( self, anotherBoundingLoop ): + 'Determine if the rectangle of this bounding loop is missing the rectangle of another bounding loop.' + if self.maximum.imag < anotherBoundingLoop.minimum.imag or self.maximum.real < anotherBoundingLoop.minimum.real: + return True + return self.minimum.imag > anotherBoundingLoop.maximum.imag or self.minimum.real > anotherBoundingLoop.maximum.real + + +class CenterOutset: + 'A class to hold a center and an outset.' + def __init__(self, center, outset): + 'Set the center and outset.' + self.center = center + self.outset = outset + + def __repr__(self): + 'Get the string representation of this CenterOutset.' + return '%s\n%s' % (self.center, self.outset) + + +class CircleIntersection: + 'An intersection of two complex circles.' + def __init__( self, circleNodeAhead, index, circleNodeBehind ): + self.aheadMinusBehind = 0.5 * ( circleNodeAhead.dividedPoint - circleNodeBehind.dividedPoint ) + self.circleNodeAhead = circleNodeAhead + self.circleNodeBehind = circleNodeBehind + self.index = index + self.steppedOn = False + demichordWidth = math.sqrt( 1.0 - self.aheadMinusBehind.real * self.aheadMinusBehind.real - self.aheadMinusBehind.imag * self.aheadMinusBehind.imag ) + rotatedClockwiseQuarter = complex( self.aheadMinusBehind.imag, - self.aheadMinusBehind.real ) + rotatedClockwiseQuarterLength = abs( rotatedClockwiseQuarter ) + if rotatedClockwiseQuarterLength == 0: + print('Warning, rotatedClockwiseQuarter in getDemichord in intercircle is 0') + print(circleNodeAhead.dividedPoint) + print(circleNodeBehind.dividedPoint) + self.demichord = 0.0 + else: + self.demichord = rotatedClockwiseQuarter * demichordWidth / rotatedClockwiseQuarterLength + self.positionRelativeToBehind = self.aheadMinusBehind + self.demichord + + def __repr__(self): + 'Get the string representation of this CircleIntersection.' + return '%s, %s, %s, %s' % (self.index, self.getAbsolutePosition(), self.circleNodeBehind, self.circleNodeAhead) + + def addToList( self, circleIntersectionPath ): + 'Add this to the circle intersection path, setting stepped on to be true.' + self.steppedOn = True + circleIntersectionPath.append(self) + + def getAbsolutePosition(self): + 'Get the absolute position.' + return self.positionRelativeToBehind + self.circleNodeBehind.dividedPoint + + def getCircleIntersectionAhead(self): + 'Get the first circle intersection on the circle node ahead.' + circleIntersections = self.circleNodeAhead.circleIntersections + circleIntersectionAhead = None + largestDot = -912345678.0 + for circleIntersection in circleIntersections: + if not circleIntersection.steppedOn: + circleIntersectionRelativeToMidpoint = euclidean.getNormalized(circleIntersection.positionRelativeToBehind + self.aheadMinusBehind) + dot = euclidean.getDotProduct(self.demichord, circleIntersectionRelativeToMidpoint) + if dot > largestDot: + largestDot = dot + circleIntersectionAhead = circleIntersection + if circleIntersectionAhead == None: + print('Warning, circleIntersectionAhead in getCircleIntersectionAhead in intercircle is None for:') + print(self.circleNodeAhead.dividedPoint) + print('circleIntersectionsAhead') + for circleIntersection in circleIntersections: + print(circleIntersection.circleNodeAhead.dividedPoint) + print('circleIntersectionsBehind') + for circleIntersection in self.circleNodeBehind.circleIntersections: + print(circleIntersection.circleNodeAhead.dividedPoint) + print('This may lead to a loop not being sliced.') + print('If this is a problem, you may as well send a bug report, even though I probably can not fix this particular problem.') + return circleIntersectionAhead + + def isWithinCircles(self, pixelTable): + 'Determine if this circle intersection is within the circle node circles.' + absolutePosition = self.getAbsolutePosition() + squareValues = euclidean.getSquareValuesFromPoint(pixelTable, absolutePosition) + for squareValue in squareValues: + if abs(squareValue.dividedPoint - absolutePosition) < 1.0: + if squareValue != self.circleNodeAhead and squareValue != self.circleNodeBehind: + return True + return False + + +class CircleNode: + 'A complex node of complex circle intersections.' + def __init__(self, oneOverRadius, point): + self.actualPoint = point + self.circleIntersections = [] + self.dividedPoint = point * oneOverRadius +# self.index = index # when debugging bring back index + + def __repr__(self): + 'Get the string representation of this CircleNode.' +# return '%s, %s, %s' % (self.index, self.dividedPoint, len(self.circleIntersections)) # when debugging bring back index + return '%s, %s' % (self.dividedPoint, len(self.circleIntersections)) + + def getWithinNodes(self, pixelTable): + 'Get the nodes this circle node is within.' + withinNodes = [] + squareValues = euclidean.getSquareValuesFromPoint(pixelTable, 0.5 * self.dividedPoint) + for squareValue in squareValues: + if abs(self.dividedPoint - squareValue.dividedPoint) < 2.0: + withinNodes.append(squareValue) + return withinNodes diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Drill.bsh b/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Drill.bsh new file mode 100644 index 0000000..413891a --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Drill.bsh @@ -0,0 +1,203 @@ +/* + + + +*/ +scene = window.getScene(); + +shapeChoice = new BComboBox (new String [] { + "Round", "Hex (point down)", "Hex (flat down)" }); +slopeField = new ValueField (45, ValueField.POSITIVE); +sizeField = new ValueField (10, ValueField.POSITIVE); +lengthField = new ValueField (20, ValueField.POSITIVE); +errorField = new ValueField (0.05, ValueField.POSITIVE); + +dlg = new ComponentsDialog (window, "Drill Tool", + new Widget [] {shapeChoice, slopeField, sizeField, lengthField, errorField}, + new String [] {"Shape:", "Max slope:", "Size:", "Length:", "Max error:"}); +if (!dlg.clickedOk()) { return; } + +shape = shapeChoice.getSelectedIndex(); +double theta, size, length, maxError; +theta = slopeField.getValue() * Math.PI / 180.0; +size = sizeField.getValue(); +length = lengthField.getValue(); +maxError = errorField.getValue(); +name = ""; + +double phi, angle; +switch (shape) +{ + case 0: // round + { + radius = size / 2; + errorAngle = Math.acos((radius - maxError) / radius); + numberSides = Math.ceil((Math.PI + 2 * theta) / errorAngle); + phi = (Math.PI + 2 * theta) / (numberSides - 2); + angle = ((Math.PI / 2.0) - theta); + y0 = radius / Math.sin(theta); + + name = "Teardrop"; + break; + } + case 1: // hex, point down + { + if (theta > (Math.PI / 3.0)) { theta = Math.PI / 3.0; } + radius = size / Math.sqrt(3); + numberSides = 6; + phi = Math.PI / 3.0; + angle = Math.PI / 3.0; + y0 = (radius / 2.0) + (size / (2.0 * Math.tan(theta))); + smooth = Mesh.NO_SMOOTHING; + + name = "Hexdrop(pointy)"; + break; + } + case 2: // hex, flat down + { + radius = size / Math.sqrt(3); + phi = Math.PI / 3.0; + y0 = radius / Math.tan(theta); + if (theta <= (Math.PI / 6.0)) + { + numberSides = 5; + angle = Math.PI / 2.0; + } + else + { + numberSides = 7; + angle = Math.PI / 6.0; + y0 += size; + y0 /= 2; + } + smooth = Mesh.NO_SMOOTHING; + + name = "Hexdrop(flat)"; + break; + } +} +name += " size " + size; + +// number of faces: +// for each edge in our perimeter, two triangles to connect +// the front and back, so 2*numberSides +// for each end cap, we connect each vertex to the point. The +// first and last vertices are connected by a perimeter edge, +// leaving numberSides-2 edges to connect across the middle, +// creating a face, for numberSides-2 triangles per end cap, +// or (numberSides-2)*2 triangles for both end caps +// Thus: +// 2*numberSides + 2*(numberSides-2) +// 2*numberSides + 2*numberSides - 4 +// 4*numberSides - 4 +Vec3[] v = new Vec3[numberSides * 2]; +int[][] faces = new int[4*numberSides-4][3]; +v[0] = new Vec3(0, y0, 0); +v[1] = new Vec3(0, y0, -length); +for (i = 0; i < (numberSides - 1); i++) +{ + j = i * 2; + x = Math.sin(angle + phi * i); + y = Math.cos(angle + phi * i); + v[j+2] = new Vec3(x, y, 0); + v[j+2].scale (radius); + v[j+3] = new Vec3(x, y, -length/radius); + v[j+3].scale (radius); + + faces[j][0] = j; + faces[j][1] = j+2; + faces[j][2] = j+1; + faces[j+1][0] = j+1; + faces[j+1][1] = j+2; + faces[j+1][2] = j+3; +} +//j = (numberSides-1)*2; +j = i*2; +faces[j][0] = 0; +faces[j][1] = j+1; +faces[j][2] = j; +faces[j+1][0] = 0; +faces[j+1][1] = 1; +faces[j+1][2] = j+1; + +k = j; + +for (i = 1; i < (numberSides - 1); i++) +{ + j = i * 2; + faces[j+k][0] = 0; + faces[j+k][1] = j+2; + faces[j+k][2] = j; + faces[j+k+1][0] = 1; + faces[j+k+1][1] = j+1; + faces[j+k+1][2] = j+3; +} + +mesh = new TriangleMesh (v, faces); +mesh.setSmoothingMethod(Mesh.NO_SMOOTHING); + +/* +// At this point, we've finished constructing the mesh. Now we need +// to set its smoothing properly. +if (shape != 0) // hex +{ + mesh.setSmoothingMethod(Mesh.NO_SMOOTHING); +} +else // round +{ + mesh.setSmoothingMethod(Mesh.APPROXIMATING); + edge = mesh.getEdges(); + face = mesh.getFaces(); + vert = mesh.getVertices(); + cutoffAngle = (theta < (Math.PI/4.0))?(theta*2.0):(Math.PI/2.0); + cutoff = Math.cos (cutoffAngle); + for (int i = 0; i < edge.length; i++) + { + ed = edge[i]; + f1 = face[ed.f1]; + f2 = face[ed.f2]; + norm1 = vert[f1.v1].r.minus(vert[f1.v2].r).cross(vert[f1.v1].r.minus(vert[f1.v3].r)); + norm2 = vert[f2.v1].r.minus(vert[f2.v2].r).cross(vert[f2.v1].r.minus(vert[f2.v3].r)); + norm1.normalize(); + norm2.normalize(); + ed.smoothness = (norm1.dot(norm2) < cutoff)?0.0f:1.0f; + } +} +*/ +window.addObject (mesh, new CoordinateSystem(), name, null); +undo = new UndoRecord (window, false, UndoRecord.DELETE_OBJECT, new Object[] { new Integer(window.getScene().getNumObjects()-1) }); +window.setUndoRecord (undo); diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Export GNU Triangulated Surface.bsh b/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Export GNU Triangulated Surface.bsh new file mode 100644 index 0000000..2aa1dac --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Export GNU Triangulated Surface.bsh @@ -0,0 +1,499 @@ +/* + + + +*/ +//later once stable remove unused preferences, all the preferences will be kept in the unused Skeinforge, finish comments for this and import +/** +Get the info for all the selected curves and meshes. +*/ + +ObjectInfo[] getCurvesMeshesInfo() +{ + scene = window.getScene(); + selection = scene.getSelection(); + Vector objectInfoVector = new Vector(); + + for ( int objectIndex = 0; objectIndex < selection.length; objectIndex++ ) { + objectInfo = scene.getObject( selection[ objectIndex ] ); + + if ( objectInfo.object instanceof TriangleMesh ) { + objectInfoVector.add( objectInfo ); + } + } + + if ( objectInfoVector.size() > 0 ) { + return objectInfoVector.toArray( new ObjectInfo[ objectInfoVector.size() ] ); + } + + for ( int objectIndex = 0; objectIndex < scene.getNumObjects(); objectIndex++ ) { + objectInfo = scene.getObject( objectIndex ); + + if ( objectInfo.object instanceof TriangleMesh ) { + return new ObjectInfo[] { objectInfo }; + } + } + + return new ObjectInfo[ 0 ]; +} + +String getEdgeString( edges, edgeTriple, vertexIndexFirst, vertexIndexSecond ) +{ + for ( int edgeTripleIndex = 0; edgeTripleIndex < edgeTriple.length; edgeTripleIndex++ ) { + edgeIndex = edgeTriple[ edgeTripleIndex ]; + edge = edges[ edgeIndex ]; + + if ( edge.v1 == vertexIndexFirst && edge.v2 == vertexIndexSecond ) { + return getPlusOneToString( edgeIndex ); + } + + if ( edge.v1 == vertexIndexSecond && edge.v2 == vertexIndexFirst ) { + return getPlusOneToString( edgeIndex ); + } + } + + print( "Inconsistent triangle mesh." ); + print( edgeTriple ); + print( vertexIndexFirst ); + print( vertexIndexSecond ); +} + +String getPlusOneToString( int number ) +{ + return ( number + 1 ).toString() ; +} + +/** +Get Vec3 rotated around X axis from counterclockwise angle and vector. + +@param angle counterclockwise angle from 1, 0 +@param vector3 Vec3 whose rotation will be returned +@return vector3 rotated around X axis +*/ + +Vec3 getRoundXAxis( double angle, Vec3 vector3 ) +{ + x = Math.cos( angle ); + y = Math.sin( angle ); + + return new Vec3( vector3.x, vector3.y * x - vector3.z * y, vector3.y * y + vector3.z * x ); +} + +/** +Get Vec3 rotated around Y axis from counterclockwise angle and vector. + +@param angle counterclockwise angle from 1, 0 +@param vector3 Vec3 whose rotation will be returned +@return vector3 rotated around Y axis +*/ + +Vec3 getRoundYAxis( double angle, Vec3 vector3 ) +{ + x = Math.cos( angle ); + y = Math.sin( angle ); + + return new Vec3( vector3.x * x - vector3.z * y, vector3.y, vector3.x * y + vector3.z * x ); +} + +/** +Get Vec3 rotated around Z axis from counterclockwise angle and vector. + +@param angle counterclockwise angle from 1, 0 +@param vector3 Vec3 whose rotation will be returned +@return vector3 rotated around Z axis +*/ + +Vec3 getRoundZAxis( double angle, Vec3 vector3 ) +{ + x = Math.cos( angle ); + y = Math.sin( angle ); + + return new Vec3( vector3.x * x - vector3.y * y, vector3.x * y + vector3.y * x, vector3.z ); +} + +/** +Output the curves and/or meshes. +*/ + +void outputCurvesMeshes( ObjectInfo[] objectInfoArray ) +{ + if ( objectInfoArray.length < 1 ) { + return; + } + + for ( int objectIndex = 0; objectIndex < objectInfoArray.length; objectIndex++ ) { + objectInfo = objectInfoArray[ objectIndex ]; + + if ( objectInfoArray.length > 1 && exportSelectionCheckbox.getState() ) { + + if ( bufferedWriter != null ) { + //Close the output stream + bufferedWriter.close(); + bufferedWriter = null; + } + + file = null; + parentFile = new File( gnuSurfaceFilenameTextField.getText() ).getParentFile(); + + if ( parentFile != null ) { + file = new File( parentFile, objectInfo.name + ".gts" ); + } + + if ( file != null ) { + bufferedWriter = new BufferedWriter( new FileWriter( file ) ); + } + } + + if ( file != null && bufferedWriter != null ) { + print( objectInfo.name + " is being saved as the GNU Triangulated Surface file " + file.getAbsolutePath() ); + } + + outputCurveMesh( objectInfo ); + + if ( bufferedWriter != null ) { + //Close the output stream + bufferedWriter.close(); + bufferedWriter = null; + } + } +} + +/** +Output the curve and/or mesh. +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 vertices, 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 vertices. Follows ne lines containing the two indices (starting from one) of the vertices 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, vertices, edges or faces). When read with different object classes, these extra attributes are just ignored." +*/ + +void outputCurveMesh( ObjectInfo objectInfo ) +{ + triangleMesh = objectInfo.object; + edges = triangleMesh.getEdges(); + faces = triangleMesh.getFaces(); + vertexPositions = triangleMesh.getVertexPositions(); + origin = objectInfo.coords.getOrigin(); + orientation = objectInfo.coords.getRotationAngles(); + orientationXRadians = orientation[ 0 ] * Math.PI / 180.0; + orientationYRadians = orientation[ 1 ] * Math.PI / 180.0; + orientationZRadians = orientation[ 2 ] * Math.PI / 180.0; + outputString( vertexPositions.length + " " + edges.length + " " + faces.length + " Number of Vertices, Number of Edges, Number of Faces" ); + + for ( int vertexIndex = 0; vertexIndex < vertexPositions.length; vertexIndex++ ) { + vertexPosition = vertexPositions[ vertexIndex ]; + vertexPosition = getRoundZAxis( - orientationZRadians, vertexPosition ); + vertexPosition = getRoundXAxis( - orientationXRadians, vertexPosition ); + vertexPosition = getRoundYAxis( orientationYRadians, vertexPosition ); + vertexPosition.add( origin ); + vertexLine = vertexPosition.x.toString() + " " + vertexPosition.y.toString() + " " + vertexPosition.z.toString(); + + if ( vertexIndex == 0 ) { + vertexLine += " Vertex Coordinates XYZ"; + } + + outputString( vertexLine ); + } + + for ( int edgeIndex = 0; edgeIndex < edges.length; edgeIndex++ ) { + edge = edges[ edgeIndex ]; + edgeLine = getPlusOneToString( edge.v1 ) + " " + getPlusOneToString( edge.v2 ); + + if ( edgeIndex == 0 ) { + edgeLine += " Edge Vertex Indices Starting from 1"; + } + + outputString( edgeLine ); + } + + for ( int faceIndex = 0; faceIndex < faces.length; faceIndex++ ) { + face = faces[ faceIndex ]; + int[] edgeTriple = { face.e1, face.e2, face.e3 }; + + faceLine = getEdgeString( edges, edgeTriple, face.v1, face.v2 ) + " " + getEdgeString( edges, edgeTriple, face.v2, face.v3 ) + " " + getEdgeString( edges, edgeTriple, face.v3, face.v1 ); + + if ( faceIndex == 0 ) { + faceLine += " Face Edge Indices Starting from 1"; + } + + outputString( faceLine ); + } +} + +/** +Output comma separated strings followed by a linefeed. +*/ + +void outputString( string ) +{ + if ( printSelectionCheckbox.getState() ) { + print( string ); + } + + if ( bufferedWriter != null ) { + bufferedWriter.write( string.replace( ", ", "," ) + "\n" ); + } +} + +/** +Add radio button groups to the preference widgets. + +@param radioButtonGroups radio button groups which will be added to the memorable widgets +@param widgetVector memorable widgets +*/ + +void preferencesAddRadioButtonGroups( RadioButtonGroup[] radioButtonGroups, Vector widgetVector ) +{ + for ( int radioIndex = 0; radioIndex < radioButtonGroups.length; radioIndex++ ) { + radioButtonGroup = radioButtonGroups[ radioIndex ]; + radioButtonGroupIterator = radioButtonGroup.getRadioButtons(); + + while ( radioButtonGroupIterator.hasNext() ) { + radioButton = radioButtonGroupIterator.next(); + preferencesAddWidgetWithString( radioButton, radioButton.getText(), widgetVector ); + } + } +} + +/** +Add widgets which have titles. + +@param widgets widgets which have titles +@param widgetStrings widget titles +@param widgetVector memorable widgets +*/ + +void preferencesAddWidgetsWithStrings( Widget[] widgets, String[] widgetStrings, Vector widgetVector ) +{ + for ( int widgetIndex = 0; widgetIndex < widgets.length; widgetIndex++ ) { + widget = widgets[ widgetIndex ]; + + if ( widget instanceof BCheckBox || widget instanceof BOutline || widget instanceof BTextField || widget instanceof ValueField ) { + preferencesAddWidgetWithString( widget, widgetStrings[ widgetIndex ], widgetVector ); + } + } +} + +/** +Give the widget a name and add it to the widget vector. + +@param widget widget which will be given a name +@param widgetStrings widget name +@param widgetVector memorable widgets +*/ + +void preferencesAddWidgetWithString( Widget widget, String widgetString, Vector widgetVector ) +{ + widget.setName( widgetString ); + widgetVector.add( widget ); +} + +/** +Read widget settings from preferences file. + +@param preferencesFilename preferences filename +@param widgetVector memorable widgets +*/ + +void preferencesRead( String preferencesFilename, Vector widgetVector ) +{ + preferencesFile = new File( preferencesFilename ); + + if ( !preferencesFile.canRead() ) { + return; + } + + BufferedReader preferencesReader = new BufferedReader( new FileReader( preferencesFile ) ); + + line = preferencesReader.readLine(); + + while ( line != null ) { + preferencesReadLine( line, widgetVector ); + line = preferencesReader.readLine(); + } +} + +/** +Read line of preferences and set widget to that line. + +@param line line of preferences +@param widgetVector memorable widgets +*/ + +void preferencesReadLine( String line, Vector widgetVector ) +{ + splitLine = line.split( "\t" ); + + if ( splitLine.length < 2 ) { + return; + } + + name = splitLine[ 0 ]; + + for ( int widgetIndex = 0; widgetIndex < widgetVector.size(); widgetIndex++ ) { + widget = widgetVector.elementAt( widgetIndex ); + + if ( widget.getName().equals( name ) ) { + preferencesReadWidget( splitLine[ 1 ], widget ); + + return; + } + } +} + +/** +Set widget to preferences value. + +@param value preferences value +@param widget widget to be set to value +*/ + +void preferencesReadWidget( String value, Widget widget ) +{ + if ( widget instanceof BCheckBox || widget instanceof BRadioButton ) { + widget.setState( Boolean.valueOf( value ) ); + + return; + } + + if ( widget instanceof BOutline ) { // it would be better to save the value instead of index because the list might change, but I'm lazy + bList = widget.getContent().getContent(); + selectedIndex = Integer.valueOf( value ); + bList.setSelected( selectedIndex, true ); + bList.scrollToItem( selectedIndex ); + + return; + } + + if ( widget instanceof BTextField ) { + widget.setText( value ); + + return; + } + + if ( widget instanceof ValueField ) { + widget.setValue( Double.valueOf( value ) ); + } +} + +/** +Write widget settings to preferences file. + +@param preferencesFilename preferences filename +@param widgetVector memorable widgets +*/ + +void preferencesWrite( String preferencesFilename, Vector widgetVector ) +{ + preferencesFile = new File( preferencesFilename ); + + if ( preferencesFile == null ) { + print( "Can not write preferences to " + preferencesFilename ); + + return; + } + + BufferedWriter preferencesWriter = new BufferedWriter( new FileWriter( preferencesFile ) ); + + for ( int widgetIndex = 0; widgetIndex < widgetVector.size(); widgetIndex++ ) { + widget = widgetVector.elementAt( widgetIndex ); + preferencesWriteWidget( preferencesWriter, widget ); + } + + //Close the output stream + preferencesWriter.close(); +} + +/** +Write widget settings to line of preferences. + +@param preferencesWriter buffered preferences file writer +@param widget widget to be written +*/ + +void preferencesWriteWidget( BufferedWriter preferencesWriter, Widget widget ) +{ + widgetString = widget.getName() + "\t"; + + if ( widget instanceof BCheckBox || widget instanceof BRadioButton ) { + preferencesWriter.write( widgetString + widget.getState().toString() + "\n" ); + + return; + } + + if ( widget instanceof BOutline ) { // it would be better to save the value because the list might change, but I'm lazy + BList bList = widget.getContent().getContent(); + bList = widget.getContent().getContent(); + preferencesWriter.write( widgetString + bList.getSelectedIndex().toString() + "\n" ); + + return; + } + + if ( widget instanceof BTextField ) { + preferencesWriter.write( widgetString + widget.getText() + "\n" ); + + return; + } + + if ( widget instanceof ValueField ) { + preferencesWriter.write( widgetString + widget.getValue().toString() + "\n" ); + } +} + +// Set default parameters. +bufferedWriter = null; +exportSelectionCheckbox = new BCheckBox( "", false ); +file = null; +gnuSurfaceFilenameTextField = new BTextField( "triangle_mesh.gts" ); +String preferencesFilename = "gnu_triangulated_surface_preferences.csv"; +printSelectionCheckbox = new BCheckBox( "", true ); + +Widget[] widgets = new Widget[] { exportSelectionCheckbox, + printSelectionCheckbox }; + +String[] widgetStrings = new String[] { "Export Selection:", + "Print Selection:" }; + +// change the user interface parameters from default to preferences +Vector widgetVector = new Vector(); +preferencesAddWidgetsWithStrings( widgets, widgetStrings, widgetVector ); +preferencesAddWidgetWithString( gnuSurfaceFilenameTextField, "GNU Triangulated Surface Filename:", widgetVector ); +preferencesRead( preferencesFilename, widgetVector ); + +curvesMeshesInfo = getCurvesMeshesInfo(); + +if ( curvesMeshesInfo.length < 1 ) { + print( "There is no triangle mesh in the scene, so nothing will be done." ); + print( "It may be that there is a solid shape which is not a triangle mesh, in which case convert that shape to a triangle mesh." ); + + return; +} + +dialog = new ComponentsDialog( window, "Export and/or Print Selection", widgets, widgetStrings ); + +if ( !dialog.clickedOk() ) return; + +if ( exportSelectionCheckbox.getState() ) { + fileDescriptor = new BFileChooser( BFileChooser.SAVE_FILE, "Select name for the GNU Triangulated Surface file."); + fileDescriptor.setSelectedFile( new File( gnuSurfaceFilenameTextField.getText() ) ); + fileDescriptor.showDialog( window ); + file = fileDescriptor.getSelectedFile(); + + if ( file != null ) { + gnuSurfaceFilenameTextField.setText( file.getAbsolutePath() ); + bufferedWriter = new BufferedWriter( new FileWriter( file ) ); + } +} + +preferencesWrite( preferencesFilename, widgetVector ); +outputCurvesMeshes( curvesMeshesInfo ); diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Export Topology.bsh b/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Export Topology.bsh new file mode 100644 index 0000000..c698b39 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Export Topology.bsh @@ -0,0 +1,170 @@ +/* + + + +*/ + +/** +Get the info for all the selected curves and meshes. +*/ + +ObjectInfo[] getCurvesMeshesInfo() +{ + scene = window.getScene(); + selection = scene.getSelection(); + Vector objectInfoVector = new Vector(); + + for ( int objectIndex = 0; objectIndex < selection.length; objectIndex++ ) { + objectInfo = scene.getObject( selection[ objectIndex ] ); + + if ( objectInfo.object instanceof Curve || objectInfo.object instanceof TriangleMesh ) { + objectInfoVector.add( objectInfo ); + } + } + + if ( objectInfoVector.size() > 0 ) { + return objectInfoVector.toArray( new ObjectInfo[ objectInfoVector.size() ] ); + } + + for ( int objectIndex = 0; objectIndex < scene.getNumObjects(); objectIndex++ ) { + objectInfo = scene.getObject( objectIndex ); + + if ( objectInfo.object instanceof Curve || objectInfo.object instanceof TriangleMesh ) { + return new ObjectInfo[] { objectInfo }; + } + } + + return new ObjectInfo[ 0 ]; +} + +/** +Output the curves and/or meshes. +*/ + +void outputCurvesMeshes( ObjectInfo[] objectInfoArray ) +{ + if ( objectInfoArray.length < 1 ) { + return; + } + + outputCurveMesh( objectInfoArray[ 0 ] ); + + for ( int objectIndex = 1; objectIndex < objectInfoArray.length; objectIndex++ ) { + outputString( "" ); + outputCurveMesh( objectInfoArray[ objectIndex ] ); + } + + if ( bufferedWriter != null ) { + //Close the output stream + bufferedWriter.close(); + } +} + +/** +Output the curve and/or mesh. +*/ + +void outputCurveMesh( ObjectInfo objectInfo ) +{ + className = objectInfo.object.getClass().getName(); + classNameEnd = className.substring( className.lastIndexOf( "." ) + 1 ); + outputString( "Class, " + classNameEnd ); + outputString( "Name, " + objectInfo.name ); + origin = objectInfo.coords.getOrigin(); + outputString( "Origin, Vector (xyz), " + origin.x + ", " + origin.y + ", " + origin.z ); + orientation = objectInfo.coords.getRotationAngles(); + outputString( "Orientation, Vector (xyz), " + orientation[ 0 ] + ", " + orientation[ 1 ] + ", " + orientation[ 2 ] ); + outputVertexPositions( objectInfo ); + + if ( objectInfo.object instanceof Curve ) { + return; + } + + outputString( "Faces, Face Index, Vertex 1, Vertex 2, Vertex 3" ); + faces = objectInfo.object.getFaces(); + + for ( int faceIndex = 0; faceIndex < faces.length; faceIndex++ ) { + face = faces[ faceIndex ]; + outputString( "Face, " + faceIndex.toString() + ", " + face.v1 + ", " + face.v2 + ", " + face.v3 ); + } + + outputString( "Edges, Edge Index, Face 1, Face 2, Smoothness, Vertex 1, Vertex 2" ); + edges = objectInfo.object.getEdges(); + + for ( int edgeIndex = 0; edgeIndex < edges.length; edgeIndex++ ) { + edge = edges[ edgeIndex ]; + outputString( "Edge, " + edgeIndex.toString() + ", " + edge.f1 + ", " + edge.f2 + ", " + edge.smoothness + ", " + edge.v1 + ", " + edge.v2 ); + } +} + +/** +Output comma separated strings followed by a linefeed. +*/ + +void outputString( string ) +{ + if ( printSelection.getState() || exportPrintSelection.getState() ) { + print( string ); + } + + if ( bufferedWriter != null ) { + bufferedWriter.write( string.replace( ", ", "," ) + "\n" ); + } +} + +/** +Output the vertex positions. +*/ + +void outputVertexPositions( ObjectInfo objectInfo ) +{ + outputString( "VertexPositions, Vertex Index, x, y, z" ); + vertexPositions = objectInfo.object.getVertexPositions(); + + for ( int vertexIndex = 0; vertexIndex < vertexPositions.length; vertexIndex++ ) { + vertexPosition = vertexPositions[ vertexIndex ]; + outputString( "Vertex Position, " + vertexIndex.toString() + ", " + vertexPosition.x + ", " + vertexPosition.y + ", " + vertexPosition.z ); + } +} + +// Get parameters. + +actionRadioButtonGroup = new RadioButtonGroup(); +exportSelection = new BRadioButton( "Export Selection", false, actionRadioButtonGroup ); +exportPrintSelection = new BRadioButton( "Export and Print Selection", true, actionRadioButtonGroup ); +printSelection = new BRadioButton( "Print Selection", false, actionRadioButtonGroup ); +actionGridContainer = new GridContainer( 1, 3 ); +actionGridContainer.setDefaultLayout( new LayoutInfo( LayoutInfo.WEST, LayoutInfo.NONE, new Insets( 2, 2, 2, 2 ), null ) ); +actionGridContainer.add( exportSelection, 0, 0 ); +actionGridContainer.add( exportPrintSelection, 0, 1 ); +actionGridContainer.add( printSelection, 0, 2 ); + +bufferedWriter = null; +dlg = new ComponentsDialog( window, "Export and/or Print Selection", +new Widget [] { actionGridContainer }, +new String [] { "Action:" } ); + +if ( !dlg.clickedOk() ) return; + +if ( exportSelection.getState() || exportPrintSelection.getState() ) { + fileDescriptor = new BFileChooser( BFileChooser.SAVE_FILE, "Select name for the topology file."); + fileDescriptor.setSelectedFile( new File( "polygon.csv" ) ); + fileDescriptor.showDialog( window ); + filename = fileDescriptor.getSelectedFile(); + + if ( filename != null ) { + bufferedWriter = new BufferedWriter( new FileWriter( filename ) ); + } +} + +curvesMeshesInfo = getCurvesMeshesInfo(); +outputCurvesMeshes( curvesMeshesInfo ); \ No newline at end of file diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Gearweaver.bsh b/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Gearweaver.bsh new file mode 100644 index 0000000..dc9efbc --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Gearweaver.bsh @@ -0,0 +1,1290 @@ +/* + + + +*/ + +/** +Add a side to the bottom of the truncated shaft. + +@param bisectionRadius outer radius of shaft +@param bottomShaftPolygon bottom shaft polygon +@param currentAngle angle around the polygon +@param nodeVector nodes of the trimesh +@param sideAngle angle subtended by one side +*/ + +void addBottomSide( bisectionRadius, bottomShaftPolygon, currentAngle, nodeVector, sideAngle ) +{ + bottom = - shaftRadius * ( 1.0 - shaftTruncationBottom ); + currentSide = getPolar( currentAngle, bisectionRadius ); + nextSide = getPolar( currentAngle + sideAngle, bisectionRadius ); + + if ( bottom > nextSide.y ) { + return; + } + + if ( currentSide.y > bottom ) { + node = new Vec3( currentSide.x, currentSide.y, 0 ); + bottomShaftPolygon.add( nodeVector.size() ); + nodeVector.add( node ); + + return; + } + + shaftMinusCurrent = bottom - currentSide.y; + shaftMinusCurrent /= nextSide.y - currentSide.y; + intersection = nextSide.minus( currentSide ); + intersection.scale( shaftMinusCurrent ); + intersection.add( currentSide ); + node = new Vec3( intersection.x, intersection.y, 0 ); + bottomShaftPolygon.add( nodeVector.size() ); + nodeVector.add( node ); +} + +/** +Adds a clockwise triangle to the face array. + +@param face three index array +@param beginIndex beginning index of the triangle +@param centerIndex center index of the triangle +@param endIndex end index of the triangle +*/ + +void addFace( int beginIndex, int centerIndex, int endIndex, Vector faces ) +{ + int[] face = new int[ 3 ]; + faces.add( face ); + face[ 0 ] = beginIndex; + face[ 1 ] = centerIndex; + face[ 2 ] = endIndex; +} + +/** +Add faces between two layers of a convex concentric clockwise polygon and a convex anticlockwise polygon, both of which go around the origin. + +@param bottomOuterPolygon bottom outer polygon +@param bottomInnerPolygon bottom inner polygon +@param faceVector faces of the trimesh +@param nodeVector nodes of the trimesh +@param topOuterPolygon top outer polygon +@param topInnerPolygon top inner polygon +*/ + +void addFacesBetweenConcentricPolygons( Vector bottomInnerPolygon, Vector bottomOuterPolygon, Vector faceVector, Vector nodeVector, Vector topInnerPolygon, Vector topOuterPolygon ) +{ + oldClosestIndex = getClosestInnerIndex( bottomInnerPolygon, nodeVector, bottomOuterPolygon.elementAt( bottomOuterPolygon.size() - 1 ), bottomOuterPolygon.elementAt( 0 ) ); + + for ( int pointIndex = 0; pointIndex < bottomOuterPolygon.size(); pointIndex++ ) { + rimPlusOneRemainder = ( pointIndex + 1 ) % bottomOuterPolygon.size(); + bottomRimFirst = bottomOuterPolygon.elementAt( pointIndex ); + bottomRimSecond = bottomOuterPolygon.elementAt( rimPlusOneRemainder ); + topRimFirst = topOuterPolygon.elementAt( pointIndex ); + closestInnerIndex = getClosestInnerIndex( bottomInnerPolygon, nodeVector, bottomRimFirst, bottomRimSecond ); + addFace( bottomInnerPolygon.elementAt( closestInnerIndex ), bottomRimFirst, bottomRimSecond, faceVector ); + addFace( topInnerPolygon.elementAt( closestInnerIndex ), topOuterPolygon.elementAt( rimPlusOneRemainder ), topRimFirst, faceVector ); + + if ( closestInnerIndex > oldClosestIndex ) { + closestInnerIndex -= bottomInnerPolygon.size(); + } + + for ( int sideIndex = closestInnerIndex; sideIndex < oldClosestIndex; sideIndex++ ) { + sideRemainder = ( sideIndex + bottomInnerPolygon.size() ) % bottomInnerPolygon.size(); + sidePlusOneRemainder = ( sideIndex + bottomInnerPolygon.size() + 1 ) % bottomInnerPolygon.size(); + addFace( bottomInnerPolygon.elementAt( sidePlusOneRemainder ), bottomRimFirst, bottomInnerPolygon.elementAt( sideRemainder ), faceVector ); + addFace( topInnerPolygon.elementAt( sidePlusOneRemainder ), topInnerPolygon.elementAt( sideRemainder ), topRimFirst, faceVector ); + } + + oldClosestIndex = closestInnerIndex; + } + + return faceIndex; +} + +/** +Add faces between a bottom and top polygon. + +@param bottomPolygon bottom polygon +@param faceVector faces of the trimesh +@param topPolygon top polygon +*/ + +void addFacesBetweenPolygons( Vector bottomPolygon, Vector faceVector, Vector topPolygon ) +{ + for ( int beginIndex = 0; beginIndex < bottomPolygon.size(); beginIndex++ ) { + beginBottomIndex = bottomPolygon.elementAt( beginIndex ); + beginTopIndex = topPolygon.elementAt( beginIndex ); + endBottomIndex = bottomPolygon.elementAt( ( beginIndex + 1 ) % bottomPolygon.size() ); + endTopIndex = topPolygon.elementAt( ( beginIndex + 1 ) % bottomPolygon.size() ); + addFace( beginBottomIndex, beginTopIndex, endTopIndex, faceVector ); + addFace( beginBottomIndex, endTopIndex, endBottomIndex, faceVector ); + } +} + +/** +Add gear outline to scene. + +@param coordinateSystem coordinate system of the gear +@param name name of the gear +@param numberOfTeeth number of teeth in the gear +@param toothProfile profile of one of the teeth of the gear +*/ + +void addGear( CoordinateSystem coordinateSystem, String name, double numberOfTeeth, Vec2[] toothProfile ) +{ + int numberOfCircumferencePoints = toothProfile.length * numberOfTeeth; + Vec3[] gearVertexPositions = new Vec3[ numberOfCircumferencePoints ]; + float[] smoothness = new float[ numberOfCircumferencePoints ]; + int index = 0; + double toothAngleRadian = 2.0 * Math.PI / numberOfTeeth; + + for ( int tooth = 0; tooth < numberOfTeeth; tooth++ ) { + double totalToothAngle = - toothAngleRadian * tooth; + + for ( int toothSurface = 0; toothSurface < toothProfile.length; toothSurface++ ) { + rotatedVector = getRotatedVector( totalToothAngle, toothProfile[ toothSurface ] ); + gearVertexPositions[ index ] = new Vec3( rotatedVector.x, rotatedVector.y, 0 ); + smoothness[ index ] = 0; + index++; + } + } + + curve = new Curve( gearVertexPositions, smoothness, Mesh.APPROXIMATING, true ); + window.addObject( curve, coordinateSystem, name, null ); +} + +/** +Add the top side of the truncated shaft. + +@param bisectionRadius outer radius of the shaft +@param bottomShaftPolygon bottom shaft polygon +@param currentAngle current angle of the polygon point +@param nodeVector nodes of the trimesh +@param sideAngle angle subtended by a side of the polygon +*/ + +void addTopSide( bisectionRadius, bottomShaftPolygon, currentAngle, nodeVector, sideAngle ) +{ + top = shaftRadius; + + if ( shaftSides % 2 == 1 ) { + top = bisectionRadius; + } + + top *= ( 1.0 - shaftTruncationTop ); + currentSide = getPolar( currentAngle, bisectionRadius ); + lastSide = getPolar( currentAngle - sideAngle, bisectionRadius ); + + if ( lastSide.y > top ) { + return; + } + + if ( currentSide.y < top ) { + gearNode = new Vec3( currentSide.x, currentSide.y, 0 ); + bottomShaftPolygon.add( nodeVector.size() ); + nodeVector.add( gearNode ); + return; + } + + shaftMinusLast = top - lastSide.y; + shaftMinusLast /= currentSide.y - lastSide.y; + intersection = currentSide.minus( lastSide ); + intersection.scale( shaftMinusLast ); + intersection.add( lastSide ); + node = new Vec3( intersection.x, intersection.y, 0 ); + bottomShaftPolygon.add( nodeVector.size() ); + nodeVector.add( node ); +} + +/** +Add triangle pairs in two layers of a convex polygon. + +@param bottomPolygon bottom polygon +@param faceVector faces of the trimesh +@param topPolygon top polygon +*/ + +void addTrianglePairsInPolygon( Vector bottomPolygon, Vector faceVector, Vector topPolygon ) +{ + for ( int begin = 0; begin < ( bottomPolygon.size() - 1 ) / 2; begin++ ) { + end = bottomPolygon.size() - begin - 1; + centerIndex = bottomPolygon.elementAt( end - 1 ); + quadrilateralBegin = bottomPolygon.elementAt( begin ); + addFace( quadrilateralBegin, centerIndex, bottomPolygon.elementAt( end ), faceVector ); + centerIndex = topPolygon.elementAt( end - 1 ); + quadrilateralBegin = topPolygon.elementAt( begin ); + addFace( quadrilateralBegin, topPolygon.elementAt( end ), centerIndex, faceVector ); + } + + for ( int begin = 0; begin < bottomPolygon.size() / 2 - 1; begin++ ) { + end = bottomPolygon.size() - begin - 1; + centerIndex = bottomPolygon.elementAt( end - 1 ); + quadrilateralBegin = bottomPolygon.elementAt( begin ); + addFace( quadrilateralBegin, bottomPolygon.elementAt( begin + 1 ), centerIndex, faceVector ); + centerIndex = topPolygon.elementAt( end - 1 ); + quadrilateralBegin = topPolygon.elementAt( begin ); + addFace( quadrilateralBegin, centerIndex, topPolygon.elementAt( begin + 1 ), faceVector ); + } +} + +/** +Convert gear profile to thick gear. + +@param coordinateSystem coordinate system of the gear +@param numberOfTeeth number of teeth in the gear +@param pitchRadius pitch radius of the gear +@param shovelDirection direction that the teeth curve +@param thickName name of the solid gear, the name of the gear profile is the name of the solid gear plus " Profile" +*/ + +void convertToSolid( CoordinateSystem coordinateSystem, int numberOfTeeth, double pitchRadius, double shovelDirection, String thickName ) +{ + scene = window.getScene(); + + gearProfile = scene.getObject( thickName + " Profile" ); + gearVertexPositions = gearProfile.object.getVertexPositions(); + innerRadius = getInnerRadius( numberOfTeeth, pitchRadius ); + layers = 1 + shovelSegments; + topVertexPositionOffsets = gearVertexPositions.length * shovelSegments; + + Vector gearFaceVector = new Vector(); + Vector gearNodeVector = new Vector(); + + for ( int vertexIndex = 0; vertexIndex < gearVertexPositions.length; vertexIndex++ ) { + gearNode = new Vec3( gearVertexPositions[ vertexIndex ] ); + gearNodeVector.add( gearNode ); + } + + for ( int segmentIndex = 1; segmentIndex < layers; segmentIndex++ ) { + polarShovel = getPolar( shovelAngle, 1.0 ); + shovelWidth = 2.0 * polarShovel.y; + upRatio = ( double )segmentIndex / ( double )shovelSegments; + digY = polarShovel.y - upRatio * shovelWidth; + digRatio = Math.sqrt( 1.0 - digY * digY ) - polarShovel.x; + digRotation = shovelDirection * digRatio * thickness / pitchRadius; + up = upRatio * thickness; + + for ( int vertexIndex = 0; vertexIndex < gearVertexPositions.length; vertexIndex++ ) { + gearNode = gearNodeVector.elementAt( vertexIndex ); + rotatedGearNode = getRotatedVector( digRotation, new Vec2( gearNode.x, gearNode.y ) ); + layerNode = new Vec3( rotatedGearNode.x, rotatedGearNode.y, up ); + gearNodeVector.add( layerNode ); + } + } + + Vector bottomShaftVector = new Vector(); + Vector topShaftVector = new Vector(); + bisectionRadius = shaftRadius; + + if ( bisectionRadius > innerRadius ) { + bisectionRadius = 0.0; + shaftRadius = 0.0; + print( "The requested shaft is larger than the gear, so the shaft will not be removed." ); + } + + if ( shaftRadius > 0.0 ) { + halfSideAngle = Math.PI / shaftSides; + shaftStart = 1.5 * Math.PI + halfSideAngle; + sideAngle = 2.0 * halfSideAngle; + bisectionRadius /= Math.cos( halfSideAngle ); + int sideIndex = 0; + + for ( ; shaftStart + sideIndex * sideAngle < 2 * Math.PI; sideIndex++ ) { + currentAngle = shaftStart + sideIndex * sideAngle; + addBottomSide( bisectionRadius, bottomShaftVector, currentAngle, gearNodeVector, sideAngle ); + } + + for ( ; shaftStart + sideIndex * sideAngle < 2.5 * Math.PI + halfSideAngle / 2; sideIndex++ ) { + currentAngle = shaftStart + sideIndex * sideAngle; + addTopSide( bisectionRadius, bottomShaftVector, currentAngle, gearNodeVector, sideAngle ); + } + + originalHalfShaftLength = bottomShaftVector.size(); + mirrorStart = originalHalfShaftLength; + + if ( Math.abs( gearNodeVector.elementAt( bottomShaftVector.elementAt( originalHalfShaftLength - 1 ) ).x ) < 0.00001 ) { + mirrorStart++; + } + + for ( int sideIndex = mirrorStart; sideIndex < 2 * originalHalfShaftLength; sideIndex++ ) { + gearShaftNode = gearNodeVector.elementAt( bottomShaftVector.elementAt( 2 * originalHalfShaftLength - sideIndex - 1 ) ); + bottomShaftVector.add( gearNodeVector.size() ); + gearNodeVector.add( new Vec3( - gearShaftNode.x, gearShaftNode.y, gearShaftNode.z ) ); + } + + copyHigh( bottomShaftVector, gearNodeVector, thickness, topShaftVector ); + } + + pointsPerTooth = ( int )Math.round( gearVertexPositions.length / numberOfTeeth ); + + // Add faces around the perimeter. + + for ( int segmentIndex = 0; segmentIndex < shovelSegments; segmentIndex++ ) { + vertexPositionOffset = segmentIndex * gearVertexPositions.length; + Vector bottomPerimeter = new Vector(); + Vector topPerimeter = new Vector(); + + for ( int beginIndex = vertexPositionOffset; beginIndex < vertexPositionOffset + gearVertexPositions.length; beginIndex++ ) { + bottomPerimeter.add( beginIndex ); + topPerimeter.add( beginIndex + gearVertexPositions.length ); + } + + addFacesBetweenPolygons( bottomPerimeter, gearFaceVector, topPerimeter ); + } + + // Add faces to the top and bottom. + + Vector bottomRim = new Vector(); + Vector topRim = new Vector(); + + for ( int tooth = 0; tooth < numberOfTeeth; tooth++ ) { + toothStart = tooth * pointsPerTooth; + bottomRim.add( toothStart ); + topRim.add( toothStart + topVertexPositionOffsets ); + + Vector bottomTooth = new Vector(); + Vector topTooth = new Vector(); + + for ( int beginIndex = 0; beginIndex <= pointsPerTooth; beginIndex++ ) { + toothStartBegin = ( toothStart + beginIndex ) % gearVertexPositions.length; + bottomTooth.add( toothStartBegin ); + topTooth.add( toothStartBegin + topVertexPositionOffsets ); + } + + addTrianglePairsInPolygon( bottomTooth, gearFaceVector, topTooth ); + } + + // Add faces around cutout circles. + + rimThickness = minimumRimThickness * innerRadius; + centerThickness = bisectionRadius + rimThickness; + remainingCutout = ( 1.0 - 2 * minimumRimThickness ); + holeRadius = ( innerRadius - rimThickness - centerThickness ) / 2; + + if ( holeRadius > minimumRimCutoutRadius ) { + int cutoutCircles = 5; + int cutoutSides = 9; + double cutoutCircleAngle = 2.0 * Math.PI / ( double )cutoutCircles; + int sideBetweenStart = ( int )Math.round( ( double )cutoutSides / 7.0 ); + int sideBetweenStartMirror = cutoutSides - sideBetweenStart; + int sideBetweenEnd = ( int )Math.round( 3.0 * ( double )cutoutSides / 8.0 ); + int sideBetweenEndMirror = cutoutSides - sideBetweenEnd; + double cutoutSideAngle = 2.0 * Math.PI / ( double )cutoutSides; + double holeY = innerRadius - rimThickness - holeRadius; + double sinHalfCutoutCircleAngle = Math.sin( cutoutCircleAngle / 2 ); + double overlap = holeRadius + rimThickness / 2 - holeY * sinHalfCutoutCircleAngle; + Vector[] bottomCutoutVector = new Vector[ cutoutCircles ]; + Vector[] topCutoutVector = new Vector[ cutoutCircles ]; + + if ( overlap > 0.0 ) { + holeRadius -= overlap / ( 1.0 + sinHalfCutoutCircleAngle ); + holeY = innerRadius - rimThickness - holeRadius; + } + + for ( int circle = 0; circle < cutoutCircles; circle++ ) { + bottomCutoutVector[ circle ] = new Vector(); + topCutoutVector[ circle ] = new Vector(); + + for ( int side = 0; side < cutoutSides; side++ ) { + cutoutPlanePoint = getPolar( Math.PI * 1.5 + cutoutSideAngle * side, holeRadius ); + cutoutPlanePoint.y += holeY; + cutoutPlanePoint = getRotatedVector( - cutoutCircleAngle * circle, cutoutPlanePoint ); + gearNode = new Vec3( cutoutPlanePoint.x, cutoutPlanePoint.y, 0.0 ); + bottomCutoutVector[ circle ].add( gearNodeVector.size() ); + gearNodeVector.add( gearNode ); + } + + copyHigh( bottomCutoutVector[ circle ], gearNodeVector, thickness, topCutoutVector[ circle ] ); + + addFacesBetweenPolygons( bottomCutoutVector[ circle ], gearFaceVector, topCutoutVector[ circle ] ); + } + + Vector bottomOuterVector = new Vector(); + Vector topOuterVector = new Vector(); + + for ( int circle = cutoutCircles - 1; circle >= 0; circle-- ) { + + for ( int side = sideBetweenEnd; side <= sideBetweenEndMirror; side++ ) { + bottomOuterVector.add( bottomCutoutVector[ circle ].elementAt( side ) ); + topOuterVector.add( topCutoutVector[ circle ].elementAt( side ) ); + } + } + + addFacesBetweenConcentricPolygons( bottomOuterVector, bottomRim, gearFaceVector, gearNodeVector, topOuterVector, topRim ); + + // Add outside of cutout circles. + + bottomRim = new Vector(); + topRim = new Vector(); + + for ( int side = 0; side <= sideBetweenStart; side++ ) { + bottomRim.add( bottomCutoutVector[ 0 ].elementAt( side ) ); + topRim.add( topCutoutVector[ 0 ].elementAt( side ) ); + } + + for ( int circle = 1; circle < cutoutCircles; circle++ ) { + + for ( int side = sideBetweenStartMirror; side <= sideBetweenStart + cutoutSides; side++ ) { + sideRemainder = side % cutoutSides; + bottomRim.add( bottomCutoutVector[ circle ].elementAt( sideRemainder ) ); + topRim.add( topCutoutVector[ circle ].elementAt( sideRemainder ) ); + } + } + + for ( int side = sideBetweenStartMirror; side < cutoutSides; side++ ) { + bottomRim.add( bottomCutoutVector[ 0 ].elementAt( side ) ); + topRim.add( topCutoutVector[ 0 ].elementAt( side ) ); + } + + for ( int circle = 0; circle < cutoutCircles; circle++ ) { + Vector bottomSides = new Vector(); + Vector topSides = new Vector(); + + for ( int side = sideBetweenStart; side <= sideBetweenEnd; side++ ) { + bottomSides.add( bottomCutoutVector[ circle ].elementAt( side ) ); + topSides.add( topCutoutVector[ circle ].elementAt( side ) ); + } + + for ( int side = sideBetweenEndMirror; side <= sideBetweenStartMirror; side++ ) { + nextCircle = ( circle + 1 ) % cutoutCircles; + bottomSides.add( bottomCutoutVector[ nextCircle ].elementAt( side ) ); + topSides.add( topCutoutVector[ nextCircle ].elementAt( side ) ); + } + + addTrianglePairsInPolygon( bottomSides, gearFaceVector, topSides ); + } + } + + if ( shaftRadius == 0.0 ) { + addTrianglePairsInPolygon( bottomRim, gearFaceVector, topRim ); + } + else { + addFacesBetweenConcentricPolygons( bottomShaftVector, bottomRim, gearFaceVector, gearNodeVector, topShaftVector, topRim ); + addFacesBetweenPolygons( bottomShaftVector, gearFaceVector, topShaftVector ); + } + + int[][] gearFaceArray = gearFaceVector.toArray( new int[ gearFaceVector.size() ][ 3 ] ); + Vec3[] gearNodeArray = gearNodeVector.toArray( new Vec3[ gearNodeVector.size() ] ); + mesh = new TriangleMesh ( gearNodeArray, gearFaceArray ); + mesh.setSmoothingMethod( Mesh.NO_SMOOTHING ); + window.addObject( mesh, coordinateSystem, thickName, null ); + window.removeObject( scene.indexOf( gearProfile ), null ); +} + +/** +Copy the bottom polygon into a top polygon which is thickness high. + +@param bottomPolygon bottom polygon +@param nodeVector nodes of the trimesh +@param thickness height of the top polygon +@param topPolygon top polygon +*/ + +void copyHigh( Vector bottomPolygon, Vector nodeVector, double thickness, Vector topPolygon ) +{ + for ( int pointIndex = 0; pointIndex < bottomPolygon.size(); pointIndex++ ) { + node = nodeVector.elementAt( bottomPolygon.elementAt( pointIndex ) ); + topPolygon.add( nodeVector.size() ); + nodeVector.add( new Vec3( node.x, node.y, thickness ) ); + } +} + +/** +End the turning gear profile simulation and create the finished gear. +*/ + +void endTurnGear() +{ + scene = window.getScene(); + timer.cancel(); + firstGearCoordinateSystem.copyCoords( firstGearCoordinateSystemOld ); + secondGearCoordinateSystem.copyCoords( secondGearCoordinateSystemOld ); + + if ( sceneCameraInfo != null ) { + sceneCameraInfo.coords.copyCoords( sceneCameraCoordsOld ); + + for ( propertyIndex = 0; propertyIndex < sceneCameraPropertiesOld.length; propertyIndex++ ) { + sceneCameraInfo.object.setPropertyValue( propertyIndex, sceneCameraPropertiesOld[ propertyIndex ] ); + } + + scene.objectModified( sceneCameraInfo.object ); + } + + window.updateImage(); + + if ( thickness > 0.0 ) { + convertToSolid( firstGearCoordinateSystemOld, ( int )numberOfTeethFirstGear, pitchRadiusFirstGear, 1.0, "First Gear" ); + convertToSolid( secondGearCoordinateSystemOld, ( int )numberOfTeethSecondGear, pitchRadiusSecondGear, - 1.0, "Second Gear" ); + } + + undoAdd = new UndoRecord ( window, false, UndoRecord.DELETE_OBJECT, new Object[] { new Integer( scene.getNumObjects() - 2 ) } ); + window.setUndoRecord( undoAdd ); + undoAdd = new UndoRecord ( window, false, UndoRecord.DELETE_OBJECT, new Object[] { new Integer( scene.getNumObjects() - 1 ) } ); + window.setUndoRecord( undoAdd ); + window.updateImage(); + +// number of faces: +// for each edge in our perimeter, two triangles to connect +// the front and back, so 2*numberSides +// for each end cap, we connect each vertex to the point. The +// first and last vertices are connected by a perimeter edge, +// leaving numberSides-2 edges to connect across the middle, +// creating a face, for numberSides-2 triangles per end cap, +// or (numberSides-2)*2 triangles for both end caps +// Thus: +// 2*numberSides + 2*(numberSides-2) +// 2*numberSides + 2*numberSides - 4 +// 4*numberSides - 4 +//Vec3[] v = new Vec3[numberSides * 2]; +//int[][] faces = new int[4*numberSides-4][3]; +//v[0] = new Vec3(0, y0, 0); +//v[1] = new Vec3(0, y0, -length); +} + +/** +Get the Camera Info from the first camera in the scene. +*/ + +ObjectInfo getCameraInfo() +{ + scene = window.getScene(); + for ( int objectIndex = 0; objectIndex < scene.getNumObjects(); objectIndex++ ) { + objectInfo = scene.getObject( objectIndex ); + if ( objectInfo.object instanceof SceneCamera ) { + return objectInfo; + } + } + + return null; +} + +/** +Get the index of the point on the inner polygon which is closest to the average angle of the first and second points on the outer polygon. + +@param innerPolygon inner polygon index array +@param nodes Vector of nodes +@param outerPolygonFirstIndex index of first point on outer polygon +@param outerPolygonSecondIndex index of second point on outer polygon +@return closest index on the inner polygon to the average angle of the first and second points on the outer polygon +*/ + +int getClosestInnerIndex( Vector innerPolygon, Vector nodes, int outerPolygonFirstIndex, int outerPolygonSecondIndex ) +{ + closestIndex = 0; + largestDot = - 9999999.0; + rimFirstPlane = nodes.elementAt( outerPolygonFirstIndex ).dropAxis( 2 ); + rimSecondPlane = nodes.elementAt( outerPolygonSecondIndex ).dropAxis( 2 ); + rimFirstPlane.normalize(); + rimSecondPlane.normalize(); + rimFirstPlane.add( rimSecondPlane ); + rimFirstPlane.normalize(); + + for ( int innerIndex = 0; innerIndex < innerPolygon.size(); innerIndex++ ) { + innerPlane = nodes.elementAt( innerPolygon.elementAt( innerIndex ) ).dropAxis( 2 ); + innerPlane.normalize(); + innerDot = innerPlane.dot( rimFirstPlane ); + + if ( innerDot > largestDot ) { + largestDot = innerDot; + closestIndex = innerIndex; + } + } + + return closestIndex; +} + +/** +Get number of teeth from the field value if it is not zero bounded to a minimum of 3, otherwise get number from the prime list. + +@param primeList scroll list of prime numbers +@param numberOfTeethValueField number of gear teeth value field +@return number of gear teeth +*/ + +double getNumberOfTeeth( BList primeList, ValueField numberOfTeethValueField ) +{ + if ( numberOfTeethValueField.getValue() < 1.0 ) { + return Double.valueOf( primeList.getSelectedValue() ); + } + + return Math.max( 2, Math.round( numberOfTeethValueField.getValue() ) ); +} + +/** +Get gear inner radius. + +@param numberOfTeeth number of gear teeth +@param pitchRadius pitch radius of the gear +@return gear inner radius +*/ + +double getInnerRadius( double numberOfTeeth, double pitchRadius ) +{ + return pitchRadius * ( 1 - Math.PI / numberOfTeeth ); +} + +/** +Get ratio outside of the pitch. + +@param numberOfTeeth number of gear teeth +@param pitchRadius pitch radius of the gear +@return ratio outside of the pitch +*/ + +double getOutsidePitch( double numberOfTeeth ) +{ + return 2.0 / numberOfTeeth; +} + +/** +Get ratio of second gear according to the number of teeth. + +@param numberOfTeethFirstGear number of gear teeth on the first gear +@param numberOfTeethSecondGear number of gear teeth on the second gear +@param pitchRadiusFirstGear pitch radius of the first gear +@return ratio of second gear +*/ + +double getPitchRadiusSecondGear( double numberOfTeethFirstGear, double numberOfTeethSecondGear, double pitchRadiusFirstGear ) +{ + return pitchRadiusFirstGear * numberOfTeethSecondGear / numberOfTeethFirstGear;; +} + +/** +Get polar Vec2 from counterclockwise angle from 1, 0 and radius. + +@param angle counterclockwise angle from 1, 0 +@param radius radius of vector +@return polar vector +*/ + +Vec2 getPolar( double angle, double radius ) +{ + return new Vec2( radius * Math.cos( angle ), radius * Math.sin( angle ) ); +} + +/** +Get scroll list of prime numbers, with visible selection. + +@param selectedIndex selected index of the scroll list +@return scroll list of prime numbers +*/ + +BList getPrimeList( int selectedIndex ) +{ + String[] primeNumberStrings = new String[ primeNumbers.length ]; + + for ( int primeIndex = 0; primeIndex < primeNumbers.length; primeIndex++ ) { + primeNumberStrings[ primeIndex ] = primeNumbers[ primeIndex ].toString(); + } + + primeList = new BList( primeNumberStrings ); + primeList.setPreferredVisibleRows( ( int )Math.max( 4.0, selectedIndex + 1 ) ); + primeList.setMultipleSelectionEnabled( false ); + primeList.setSelected( selectedIndex, true ); + primeList.scrollToItem( selectedIndex ); + + return primeList; +} + +/** +Get rotated Vec2 from counterclockwise angle and vector. + +@param angle counterclockwise angle from 1, 0 +@param vector2 plane vector +@return rotated vector +*/ + +Vec2 getRotatedVector( double angle, Vec2 vector2 ) +{ + x = Math.cos( angle ); + y = Math.sin( angle ); + + return new Vec2( vector2.x * x - vector2.y * y, vector2.x * y + vector2.y * x ); +} + +/** +Get profile for one tooth. + +@param numberOfTeeth number of gear teeth +@param pitchRadius pitch radius of the gear +@param pressureAngle pressure angle of the gear tooth +@param profileDefinitionSurfaces profile definition surfaces, which should be at least six +@return profile for one tooth +*/ + +Vec2[] getToothProfile( double numberOfTeeth, double pitchRadius, double pressureAngle, int profileDefinitionSurfaces ) +{ + double toothAngleRadian = 2.0 * Math.PI / numberOfTeeth; + double innerRadius = getInnerRadius( numberOfTeeth, pitchRadius ); + double outerRadius = pitchRadius * ( 1 + getOutsidePitch( numberOfTeeth ) ); + double bevelRadius = pitchRadius * Math.cos( pressureAngle ); + double angleBeginRadian = Math.PI / 2 - toothAngleRadian / 4 + pressureAngle - pitchRadius * Math.sin( pressureAngle ) / bevelRadius; + double angleEndRadian = Math.sqrt( outerRadius * outerRadius - bevelRadius * bevelRadius ) / bevelRadius + angleBeginRadian; + double angleDifferenceRadian = ( angleEndRadian - angleBeginRadian ) / ( profileDefinitionSurfaces - 1 ); + Vector toothProfileVector = new Vector(); + + toothProfileVector.add( getPolar( Math.PI - angleBeginRadian, innerRadius ) ); + + angleRadian = angleBeginRadian; + abovePoint = null; + pointMoved = false; + + for ( int profileIndex = 0; profileIndex < profileDefinitionSurfaces; profileIndex++ ) { + length = bevelRadius * ( angleRadian - angleBeginRadian ); + toothPoint = new Vec2( - bevelRadius, length ); + toothPoint = getRotatedVector( angleRadian, toothPoint ); + toothPoint.y = - toothPoint.y; + toothProfileVector.add( toothPoint ); + angleRadian = angleRadian + angleDifferenceRadian; + + if ( toothPoint.length() > innerRadius ) { + + if ( abovePoint == null ) { + abovePoint = toothPoint; + } + } + } + + // Move out, remove or replace the points which are inside the inner radius. + + for ( int profileIndex = profileDefinitionSurfaces-1; profileIndex >= 1; profileIndex-- ) { + toothProfileLength = toothProfileVector.elementAt( profileIndex ).length(); + + if ( toothProfileLength < innerRadius ) { + + if ( pointMoved ) { + toothProfileVector.removeElementAt( profileIndex ); + } + else { + lengthBelow = innerRadius - toothProfileLength; + toothPoint = toothProfileVector.elementAt( profileIndex ); + lastMinusShort = abovePoint.minus( toothPoint ); + lastMinusShort.scale( lengthBelow / lastMinusShort.length() ); + toothPoint.add( lastMinusShort ); + pointMoved = true; + } + } + } + + if ( pointMoved ) { + toothProfileVector.removeElementAt( 0 ); + } + + // Mirror the tooth profile. + + int toothProfileLimit = toothProfileVector.size() - 1; + + for ( int profileIndex = toothProfileLimit; profileIndex >= 0; profileIndex-- ) { + toothPoint = toothProfileVector.elementAt( profileIndex ); + toothProfileVector.add( new Vec2( - toothPoint.x, toothPoint.y ) ); + } + + return toothProfileVector.toArray( new Vec2[ toothProfileVector.size() ] ); +} + +/** +Add radio button groups to the preference widgets. + +@param radioButtonGroups radio button groups which will be added to the memorable widgets +@param widgetVector memorable widgets +*/ + +void preferencesAddRadioButtonGroups( RadioButtonGroup[] radioButtonGroups, Vector widgetVector ) +{ + for ( int radioIndex = 0; radioIndex < radioButtonGroups.length; radioIndex++ ) { + radioButtonGroup = radioButtonGroups[ radioIndex ]; + radioButtonGroupIterator = radioButtonGroup.getRadioButtons(); + + while ( radioButtonGroupIterator.hasNext() ) { + radioButton = radioButtonGroupIterator.next(); + preferencesAddWidgetWithString( radioButton, radioButton.getText(), widgetVector ); + } + } +} + +/** +Add widgets which have titles. + +@param widgets widgets which have titles +@param widgetStrings widget titles +@param widgetVector memorable widgets +*/ + +void preferencesAddWidgetsWithStrings( Widget[] widgets, String[] widgetStrings, Vector widgetVector ) +{ + for ( int widgetIndex = 0; widgetIndex < widgets.length; widgetIndex++ ) { + widget = widgets[ widgetIndex ]; + + if ( widget instanceof BCheckBox || widget instanceof BOutline || widget instanceof BTextField || widget instanceof ValueField ) { + preferencesAddWidgetWithString( widget, widgetStrings[ widgetIndex ], widgetVector ); + } + } +} + +/** +Give the widget a name and add it to the widget vector. + +@param widget widget which will be given a name +@param widgetStrings widget name +@param widgetVector memorable widgets +*/ + +void preferencesAddWidgetWithString( Widget widget, String widgetString, Vector widgetVector ) +{ + widget.setName( widgetString ); + widgetVector.add( widget ); +} + +/** +Read widget settings from preferences file. + +@param preferencesFilename preferences filename +@param widgetVector memorable widgets +*/ + +void preferencesRead( String preferencesFilename, Vector widgetVector ) +{ + preferencesFile = new File( preferencesFilename ); + + if ( !preferencesFile.canRead() ) { + return; + } + + BufferedReader preferencesReader = new BufferedReader( new FileReader( preferencesFile ) ); + + line = preferencesReader.readLine(); + + while ( line != null ) { + preferencesReadLine( line, widgetVector ); + line = preferencesReader.readLine(); + } +} + +/** +Read line of preferences and set widget to that line. + +@param line line of preferences +@param widgetVector memorable widgets +*/ + +void preferencesReadLine( String line, Vector widgetVector ) +{ + splitLine = line.split( "\t" ); + + if ( splitLine.length < 2 ) { + return; + } + + name = splitLine[ 0 ]; + + for ( int widgetIndex = 0; widgetIndex < widgetVector.size(); widgetIndex++ ) { + widget = widgetVector.elementAt( widgetIndex ); + + if ( widget.getName().equals( name ) ) { + preferencesReadWidget( splitLine[ 1 ], widget ); + + return; + } + } +} + +/** +Set widget to preferences value. + +@param value preferences value +@param widget widget to be set to value +*/ + +void preferencesReadWidget( String value, Widget widget ) +{ + if ( widget instanceof BCheckBox || widget instanceof BRadioButton ) { + widget.setState( Boolean.valueOf( value ) ); + + return; + } + + if ( widget instanceof BOutline ) { // it would be better to save the value instead of index because the list might change, but I'm lazy + bList = widget.getContent().getContent(); + selectedIndex = Integer.valueOf( value ); + bList.setSelected( selectedIndex, true ); + bList.scrollToItem( selectedIndex ); + + return; + } + + if ( widget instanceof BTextField ) { + widget.setText( value ); + + return; + } + + if ( widget instanceof ValueField ) { + widget.setValue( Double.valueOf( value ) ); + } +} + +/** +Write widget settings to preferences file. + +@param preferencesFilename preferences filename +@param widgetVector memorable widgets +*/ + +void preferencesWrite( String preferencesFilename, Vector widgetVector ) +{ + preferencesFile = new File( preferencesFilename ); + + if ( preferencesFile == null ) { + print( "Can not write preferences to " + preferencesFilename ); + + return; + } + + BufferedWriter preferencesWriter = new BufferedWriter( new FileWriter( preferencesFile ) ); + + for ( int widgetIndex = 0; widgetIndex < widgetVector.size(); widgetIndex++ ) { + widget = widgetVector.elementAt( widgetIndex ); + preferencesWriteWidget( preferencesWriter, widget ); + } + + //Close the output stream + preferencesWriter.close(); +} + +/** +Write widget settings to line of preferences. + +@param preferencesWriter buffered preferences file writer +@param widget widget to be written +*/ + +void preferencesWriteWidget( BufferedWriter preferencesWriter, Widget widget ) +{ + widgetString = widget.getName() + "\t"; + + if ( widget instanceof BCheckBox || widget instanceof BRadioButton ) { + preferencesWriter.write( widgetString + widget.getState().toString() + "\n" ); + + return; + } + + if ( widget instanceof BOutline ) { // it would be better to save the value because the list might change, but I'm lazy + BList bList = widget.getContent().getContent(); + bList = widget.getContent().getContent(); + preferencesWriter.write( widgetString + bList.getSelectedIndex().toString() + "\n" ); + + return; + } + + if ( widget instanceof BTextField ) { + preferencesWriter.write( widgetString + widget.getText() + "\n" ); + + return; + } + + if ( widget instanceof ValueField ) { + preferencesWriter.write( widgetString + widget.getValue().toString() + "\n" ); + } +} + +/** +Set the coordinate system to a rotation around the z axis in degrees. + +@param coordinateSystem coordinate system with rotations defined around the x, y, z axes in degrees +@param rotationDegree axis rotation angle in degrees +*/ + +void setCoordinateSystemToRotationDegree( CoordinateSystem coordinateSystem, double rotationDegree ) +{ + coordinateSystem.setOrientation( 0.0, 0.0, rotationDegree ); +} + +// Trim outer tooth of the first gear which collides with the second gear. + +void trimCollision( double numberOfTeethFirstGear, double numberOfTeethSecondGear, double pitchRadiusFirstGear, Vec2[] toothProfileFirstGear, Vec2[] toothProfileSecondGear ) +{ + maximumAngle = Math.PI / 8; + + if ( numberOfTeethFirstGear > numberOfTeethSecondGear ) { + maximumAngle *= numberOfTeethSecondGear / numberOfTeethFirstGear; + } + + for ( double angle = 0.0; angle < maximumAngle; angle += 0.01 ) { + trimCollisionAtAngle( angle, numberOfTeethFirstGear, numberOfTeethSecondGear, pitchRadiusFirstGear, toothProfileFirstGear, toothProfileSecondGear ); + } +} + +// Trim outer tooth of the first gear which collides with the second gear at an angle. + +void trimCollisionAtAngle( double angle, double numberOfTeethFirstGear, double numberOfTeethSecondGear, double pitchRadiusFirstGear, Vec2[] toothProfileFirstGear, Vec2[] toothProfileSecondGear ) +{ + double pitchRadiusSecondGear = getPitchRadiusSecondGear( numberOfTeethFirstGear, numberOfTeethSecondGear, pitchRadiusFirstGear ); + + toothProfileSecondGearRotated = new Vec2[ toothProfileSecondGear.length ]; + rotationSecondGear = Math.PI - Math.PI / numberOfTeethSecondGear - angle * numberOfTeethFirstGear / numberOfTeethSecondGear; + + for ( int profilePointIndex = 0; profilePointIndex < toothProfileSecondGear.length; profilePointIndex++ ) { + rotated = getRotatedVector( rotationSecondGear, toothProfileSecondGear[ profilePointIndex ] ); + rotated.y += pitchRadiusFirstGear + pitchRadiusSecondGear; + toothProfileSecondGearRotated[ profilePointIndex ] = getRotatedVector( - angle, rotated ); + } + + for ( int profilePointIndex = 0; profilePointIndex < toothProfileFirstGear.length; profilePointIndex++ ) { + trimCollisionProfilePoint( profilePointIndex, toothProfileFirstGear, toothProfileSecondGearRotated ); + } +} + +// Trim point from first gear which collides with the second gear. + +void trimCollisionProfilePoint( int profilePointIndex, Vec2[] toothProfileFirstGear, Vec2[] toothProfileSecondGearRotated ) +{ + firstGearProfilePoint = toothProfileFirstGear[ profilePointIndex ]; + + if ( firstGearProfilePoint.length() < pitchRadiusFirstGear ) { + return; + } + + if ( firstGearProfilePoint.x > 0.0 ) { + return; + } + + for ( int secondGearPointIndex = 0; secondGearPointIndex < toothProfileSecondGearRotated.length - 1; secondGearPointIndex++ ) { + firstPointSecondGear = toothProfileSecondGearRotated[ secondGearPointIndex ]; + secondPointSecondGear = toothProfileSecondGearRotated[ secondGearPointIndex + 1 ]; + firstPointAbove = firstPointSecondGear.y - firstGearProfilePoint.y; + secondPointAbove = secondPointSecondGear.y - firstGearProfilePoint.y; + + if ( firstPointAbove * secondPointAbove <= 0.0 ) { + verticalSeparation = firstPointSecondGear.y - secondPointSecondGear.y; + horizontalToothAxisIntersection = firstPointSecondGear.minus( secondPointSecondGear ); + horizontalToothAxisIntersection.scale( - secondPointAbove / verticalSeparation ); + horizontalToothAxisIntersection.add( secondPointSecondGear ); + collision = horizontalToothAxisIntersection.x - firstGearProfilePoint.x; + + if ( collision > 0.0 ) { + firstGearProfilePoint.x += collision; + mirrorPointIndex = toothProfileFirstGear.length - 1 - profilePointIndex; + toothProfileFirstGear[ mirrorPointIndex ].x -= collision; + } + } + } +} + +// Trim outer tooth by tolerance. + +void trimTolerance( double numberOfTeeth, double pitchRadius, double tolerance, Vec2[] toothProfile ) +{ + if ( tolerance == 0.0 ) { + return; + } + + double otherGearStart = pitchRadius * ( 1 - getOutsidePitch( numberOfTeeth ) ) - tolerance; + double innerRadius = getInnerRadius( numberOfTeeth, pitchRadius ); + + for ( int profilePointIndex = 0; profilePointIndex < toothProfile.length; profilePointIndex++ ) { + toothProfilePoint = toothProfile[ profilePointIndex ]; + toothProfilePointLength = toothProfilePoint.length(); + shortenRatio = 1.0; + + if ( toothProfilePointLength < otherGearStart ) { + shortenRatio = ( toothProfilePointLength - innerRadius ) / ( otherGearStart - innerRadius ); + shortenRatio = Math.max( 0.0, shortenRatio ); + } + + xAbsolute = Math.abs( toothProfilePoint.x ); + xAbsoluteTrimmed = xAbsolute - tolerance * shortenRatio; + xAbsoluteTrimmed = Math.max( 0.0, xAbsoluteTrimmed ); + + if ( xAbsolute > 0.0 ) { + toothProfilePoint.x *= xAbsoluteTrimmed / xAbsolute; + } + } +} + +// Get parameters. + +int[] primeNumbers = { 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149 }; +firstGearPrimeList = getPrimeList( 6 ); +numberOfTeethFirstGearValueField = new ValueField( 0, ValueField.NONNEGATIVE ); +pitchRadiusFirstGearValueField = new ValueField( 75, ValueField.NONNEGATIVE ); +secondGearPrimeList = getPrimeList( 1 ); +numberOfTeethSecondGearValueField = new ValueField( 0, ValueField.NONNEGATIVE ); +minimumRimCutoutRadiusValueField = new ValueField( 2.0, ValueField.NONNEGATIVE ); +minimumRimThicknessValueField = new ValueField( 0.2, ValueField.NONNEGATIVE ); +String preferencesFilename = "gearweaver_preferences.csv"; +pressureAngleDegreeValueField = new ValueField( 20, ValueField.NONNEGATIVE ); +profileDefinitionValueField = new ValueField( 9, ValueField.NONNEGATIVE ); +shaftRadiusValueField = new ValueField( 5.0, ValueField.NONNEGATIVE); +shaftSidesValueField = new ValueField( 4.0, ValueField.NONNEGATIVE); +shaftTruncationBottomValueField = new ValueField( 0.0, ValueField.NONNEGATIVE); +shaftTruncationTopValueField = new ValueField( 0.0, ValueField.NONNEGATIVE); +shovelAngleValueField = new ValueField( 20.0, ValueField.NONNEGATIVE); +shovelSegmentsValueField = new ValueField( 3, ValueField.NONNEGATIVE); +//simulationDurationValueField = new ValueField( 10, ValueField.NONNEGATIVE); +simulationDurationValueField = new ValueField( 0, ValueField.NONNEGATIVE); +simulationRotationRateValueField = new ValueField( 0.1, ValueField.NONNEGATIVE ); +thicknessValueField = new ValueField( 30.0, ValueField.NONNEGATIVE ); +//thicknessValueField = new ValueField( 0.0, ValueField.NONNEGATIVE ); +toleranceValueField = new ValueField( 0.0, ValueField.NONNEGATIVE ); + +Widget[] widgets = new Widget [] { UIUtilities.createScrollingList( firstGearPrimeList ), + numberOfTeethFirstGearValueField, + pitchRadiusFirstGearValueField, + UIUtilities.createScrollingList( secondGearPrimeList ), + numberOfTeethSecondGearValueField, + minimumRimCutoutRadiusValueField, + minimumRimThicknessValueField, + pressureAngleDegreeValueField, + profileDefinitionValueField, + shaftRadiusValueField, + shaftSidesValueField, + shaftTruncationBottomValueField, + shaftTruncationTopValueField, + shovelAngleValueField, + shovelSegmentsValueField, + simulationDurationValueField, + simulationRotationRateValueField, + thicknessValueField, + toleranceValueField }; + +String[] widgetStrings = new String [] { "Number of Teeth for First Gear (prime):", + "or Number of Teeth for First Gear (integer):", + "Pitch Radius First Gear (mm):", + "Number of Teeth for Second Gear (prime):", + "or Number of Teeth for Second Gear (integer):", + "Minimum Rim Cutout Radius (mm):", + "Minimum Rim Thickness (ratio):", + "Pressure Angle (degrees):", + "Profile Definition (surfaces):", + "Shaft Radius (mm):", + "Shaft Sides (surfaces):", + "Shaft Truncation Bottom (ratio):", + "Shaft Truncation Top (ratio):", + "Shovel Angle (degrees):", + "Shovel Segments (surfaces):", + "Simulation Duration (seconds):", + "Simulation Rotation Rate (teeth / second):", + "Thickness (mm):", + "Tolerance (mm):" }; + +// change the user interface parameters from default to preferences +Vector widgetVector = new Vector(); +preferencesAddWidgetsWithStrings( widgets, widgetStrings, widgetVector ); +preferencesRead( preferencesFilename, widgetVector ); + +dialog = new ComponentsDialog( window, "Gearweaver Generator", widgets, widgetStrings ); + +if ( !dialog.clickedOk() ) return; + +preferencesWrite( preferencesFilename, widgetVector ); + +double numberOfTeethFirstGear = getNumberOfTeeth( firstGearPrimeList, numberOfTeethFirstGearValueField ); +double numberOfTeethSecondGear = getNumberOfTeeth( secondGearPrimeList, numberOfTeethSecondGearValueField ); +double pitchRadiusFirstGear = pitchRadiusFirstGearValueField.getValue(); +double pitchRadiusSecondGear = getPitchRadiusSecondGear( numberOfTeethFirstGear, numberOfTeethSecondGear, pitchRadiusFirstGear ); +double minimumRimCutoutRadius = minimumRimCutoutRadiusValueField.getValue(); +double minimumRimThickness = minimumRimThicknessValueField.getValue(); +int profileDefinitionSurfaces = ( int )Math.max( 1.0, Math.min( 30.0, profileDefinitionValueField.getValue() ) ); +double pressureAngle = pressureAngleDegreeValueField.getValue() * Math.PI / 180.0; +double shaftRadius = shaftRadiusValueField.getValue(); +int shaftSides = ( int )Math.max( 3.0, shaftSidesValueField.getValue() ); +double shaftTruncationBottom = shaftTruncationBottomValueField.getValue(); +double shaftTruncationTop = shaftTruncationTopValueField.getValue(); +double shovelAngle = shovelAngleValueField.getValue() * Math.PI / 180.0; +int shovelSegments = ( int )Math.max( 1.0, shovelSegmentsValueField.getValue() ); +double simulationDuration = simulationDurationValueField.getValue(); +double simulationRotationRate = simulationRotationRateValueField.getValue(); +double thickness = thicknessValueField.getValue(); +double tolerance = toleranceValueField.getValue(); + +// Create gear pair. + +toothProfileFirstGear = getToothProfile( numberOfTeethFirstGear, pitchRadiusFirstGear, pressureAngle, profileDefinitionSurfaces ); +toothProfileSecondGear = getToothProfile( numberOfTeethSecondGear, pitchRadiusSecondGear, pressureAngle, profileDefinitionSurfaces ); + +trimCollision( numberOfTeethFirstGear, numberOfTeethSecondGear, pitchRadiusFirstGear, toothProfileFirstGear, toothProfileSecondGear ); +trimCollision( numberOfTeethSecondGear, numberOfTeethFirstGear, pitchRadiusSecondGear, toothProfileSecondGear, toothProfileFirstGear ); + +firstGearCoordinateSystem = new CoordinateSystem(); +firstGearCoordinateSystemOld = firstGearCoordinateSystem.duplicate(); +trimTolerance( numberOfTeethFirstGear, pitchRadiusFirstGear, tolerance, toothProfileFirstGear ); +addGear( firstGearCoordinateSystem, "First Gear Profile", numberOfTeethFirstGear, toothProfileFirstGear ); + +secondGearCoordinateSystem = new CoordinateSystem(); +secondGearCoordinateSystem.setOrigin( new Vec3( 0.0, pitchRadiusFirstGear + pitchRadiusSecondGear, 0.0 ) ); +secondGearCoordinateOffsetDegree = 180.0 * ( numberOfTeethSecondGear % 2 + 1 ) / numberOfTeethSecondGear; +setCoordinateSystemToRotationDegree( secondGearCoordinateSystem, secondGearCoordinateOffsetDegree ); +secondGearCoordinateSystemOld = secondGearCoordinateSystem.duplicate(); +trimTolerance( numberOfTeethSecondGear, pitchRadiusSecondGear, tolerance, toothProfileSecondGear ); +addGear( secondGearCoordinateSystem, "Second Gear Profile", numberOfTeethSecondGear, toothProfileSecondGear ); + +import java.util.Timer; +import java.util.TimerTask; + +Timer timer = new Timer(); + +startTime = System.currentTimeMillis(); +sceneCameraInfo = getCameraInfo(); +sceneCameraCoordsOld = null; +sceneCameraPropertiesOld = null; + +if ( sceneCameraInfo != null ) { + sceneCameraCoordsOld = sceneCameraInfo.coords.duplicate(); + sceneCamera = sceneCameraInfo.object; + properties = sceneCamera.getProperties(); + propertiesLength = properties.length; + sceneCameraPropertiesOld = new double[ propertiesLength ]; + + for ( propertyIndex = 0; propertyIndex < propertiesLength; propertyIndex++ ) { + sceneCameraPropertiesOld[ propertyIndex ] = sceneCamera.getPropertyValue( propertyIndex ); + propertyName = properties[ propertyIndex ].getName(); + + if ( propertyName.equals( "Depth of Field" ) ) { + sceneCamera.setPropertyValue( propertyIndex, 10.0 ); + } + + if ( propertyName.equals( "Field of View" ) ) { + sceneCamera.setPropertyValue( propertyIndex, 30.0 ); + } + + if ( propertyName.equals( "Focal Distance" ) ) { + sceneCamera.setPropertyValue( propertyIndex, 20.0 ); + } + } + sceneCameraInfo.coords.setOrigin( new Vec3( 0.0, pitchRadiusFirstGear, 15.0 * pitchRadiusFirstGear / numberOfTeethFirstGear ) ); + sceneCameraInfo.coords.setOrientation( 0.0, 180.0, 0.0 ); + sceneCameraInfo.coords.setOrientation( 0.0, 180.0, 0.0 ); +} + +public final class TurnGear extends TimerTask { + /** + * Implements TimerTask's abstract run method. + */ + public void run() { + elapsed = ( System.currentTimeMillis() - startTime ) / 1000.0; + elapsedRotation = 360.0 * elapsed * simulationRotationRate; + setCoordinateSystemToRotationDegree( firstGearCoordinateSystem, - elapsedRotation / numberOfTeethFirstGear ); + setCoordinateSystemToRotationDegree( secondGearCoordinateSystem, elapsedRotation / numberOfTeethSecondGear + secondGearCoordinateOffsetDegree ); + scene = window.getScene(); + scene.objectModified( scene.getObject( "First Gear Profile" ).object ); + scene.objectModified( scene.getObject( "Second Gear Profile" ).object ); + window.updateImage(); + + if ( elapsed > simulationDuration ) { + endTurnGear(); + } + } +} + +TimerTask turnGear = new TurnGear(); +timer.scheduleAtFixedRate( turnGear, 0, 13 ); \ No newline at end of file diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Import GNU Triangulated Surface.bsh b/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Import GNU Triangulated Surface.bsh new file mode 100644 index 0000000..343bf52 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Import GNU Triangulated Surface.bsh @@ -0,0 +1,399 @@ +/* + + + +*/ + +/** +Add a delete the last object of the scene undo record, to the scene. +*/ + +void addUndoRecord() { + scene = window.getScene(); + undoAdd = new UndoRecord ( window, false, UndoRecord.DELETE_OBJECT, new Object[] { new Integer( scene.getNumObjects() - 1 ) } ); + window.setUndoRecord( undoAdd ); +} + +int getSameVertexIndex( edgeFirst, edgeSecond ) +{ + for ( int endpointIndex = 0; endpointIndex < 2; endpointIndex++ ) { + endpoint = edgeFirst[ endpointIndex ]; + + if ( endpoint == edgeSecond[ 0 ] ) { + return endpoint; + } + + if ( endpoint == edgeSecond[ 1 ] ) { + return endpoint; + } + } + + print( "Inconsistent GNU Triangulated Surface" ); + print( edgeFirst ); + print( edgeSecond ); + return 0; +} + +/** +Import GNU Triangulated Surface from a file. +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 vertices, 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 vertices. Follows ne lines containing the two indices (starting from one) of the vertices 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, vertices, edges or faces). When read with different object classes, these extra attributes are just ignored." +*/ + +void importFile( gnuFile ) +{ + bufferedReader = new BufferedReader( new FileReader( gnuFile ) ); + Vector lineVector = new Vector(); + line = bufferedReader.readLine(); + name = gnuFile.getName(); + int lastIndexOfDot = name.lastIndexOf( '.' ); + + if ( lastIndexOfDot > 0 ) { + name = name.substring( 0, lastIndexOfDot ); + } + + while ( line != null ) { + + if ( line.length() > 0 ) { + firstCharacter = line.charAt( 0 ); + + if ( firstCharacter != '#' && firstCharacter != '!' ) { + lineVector.add( line ); + } + } + + line = bufferedReader.readLine(); + } + + splitLine = lineVector.get( 0 ).split( " " ); + int numberOfVertices = Integer.valueOf( splitLine[ 0 ] ); + int numberOfEdges = Integer.valueOf( splitLine[ 1 ] ); + int numberOfFaces = Integer.valueOf( splitLine[ 2 ] ); + edges = new int[ numberOfEdges ][ 2 ]; + faces = new int[ numberOfFaces ][ 3 ]; + vertices = new Vec3[ numberOfVertices ]; + + for ( int vertexIndex = 0; vertexIndex < numberOfVertices; vertexIndex++ ) { + line = lineVector.get( vertexIndex + 1 ); + splitLine = line.split( " " ); + vertex = new Vec3( Double.valueOf( splitLine[ 0 ] ), Double.valueOf( splitLine[ 1 ] ), Double.valueOf( splitLine[ 2 ] ) ); + vertices[ vertexIndex ] = vertex; + } + + int edgeStart = numberOfVertices + 1; + + for ( int edgeIndex = 0; edgeIndex < numberOfEdges; edgeIndex++ ) { + line = lineVector.get( edgeIndex + edgeStart ); + splitLine = line.split( " " ); + int[] edge = { Integer.valueOf( splitLine[ 0 ] ) - 1, Integer.valueOf( splitLine[ 1 ] ) - 1 }; + edges[ edgeIndex ] = edge; + } + + int faceStart = edgeStart + numberOfEdges; + + for ( int faceIndex = 0; faceIndex < numberOfFaces; faceIndex++ ) { + line = lineVector.get( faceIndex + faceStart ); + splitLine = line.split( " " ); + edgeFirst = edges[ Integer.valueOf( splitLine[ 0 ] ) - 1 ]; + edgeSecond = edges[ Integer.valueOf( splitLine[ 1 ] ) - 1 ]; + edgeThird = edges[ Integer.valueOf( splitLine[ 2 ] ) - 1 ]; + int[] vertexIndices = { getSameVertexIndex( edgeFirst, edgeSecond ), getSameVertexIndex( edgeSecond, edgeThird ), getSameVertexIndex( edgeThird, edgeFirst ) }; + faces[ faceIndex ] = vertexIndices; + } + + CoordinateSystem coordinateSystem = new CoordinateSystem(); + + while ( window.getScene().getObject( name ) != null ) { + name += "2"; + } + + mesh = new TriangleMesh ( vertices, faces ); + mesh.setSmoothingMethod( Mesh.NO_SMOOTHING ); + window.addObject( mesh, coordinateSystem, name, null ); + addUndoRecord(); +} + +/** +Import GNU Triangulated Surface from a file or from files in a directory. +*/ + +void importFiles() +{ + if ( openFileButton.getState() ) { + importFile( file ); + return; + } + + filesInDirectory = file.getParentFile().listFiles(); + + for ( int fileIndex = 0; fileIndex < filesInDirectory.length; fileIndex++ ) { + directoryFile = filesInDirectory[ fileIndex ]; + String directoryFileName = directoryFile.getName(); + + if ( directoryFileName.endsWith( ".gts" ) ) { + importFile( directoryFile ); + } + } +} + +/** +Add radio button groups to the preference widgets. + +@param radioButtonGroups radio button groups which will be added to the memorable widgets +@param widgetVector memorable widgets +*/ + +void preferencesAddRadioButtonGroups( RadioButtonGroup[] radioButtonGroups, Vector widgetVector ) +{ + for ( int radioIndex = 0; radioIndex < radioButtonGroups.length; radioIndex++ ) { + radioButtonGroup = radioButtonGroups[ radioIndex ]; + radioButtonGroupIterator = radioButtonGroup.getRadioButtons(); + + while ( radioButtonGroupIterator.hasNext() ) { + radioButton = radioButtonGroupIterator.next(); + preferencesAddWidgetWithString( radioButton, radioButton.getText(), widgetVector ); + } + } +} + +/** +Add widgets which have titles. + +@param widgets widgets which have titles +@param widgetStrings widget titles +@param widgetVector memorable widgets +*/ + +void preferencesAddWidgetsWithStrings( Widget[] widgets, String[] widgetStrings, Vector widgetVector ) +{ + for ( int widgetIndex = 0; widgetIndex < widgets.length; widgetIndex++ ) { + widget = widgets[ widgetIndex ]; + + if ( widget instanceof BCheckBox || widget instanceof BOutline || widget instanceof BTextField || widget instanceof ValueField ) { + preferencesAddWidgetWithString( widget, widgetStrings[ widgetIndex ], widgetVector ); + } + } +} + +/** +Give the widget a name and add it to the widget vector. + +@param widget widget which will be given a name +@param widgetStrings widget name +@param widgetVector memorable widgets +*/ + +void preferencesAddWidgetWithString( Widget widget, String widgetString, Vector widgetVector ) +{ + widget.setName( widgetString ); + widgetVector.add( widget ); +} + +/** +Read widget settings from preferences file. + +@param preferencesFilename preferences filename +@param widgetVector memorable widgets +*/ + +void preferencesRead( String preferencesFilename, Vector widgetVector ) +{ + preferencesFile = new File( preferencesFilename ); + + if ( !preferencesFile.canRead() ) { + return; + } + + BufferedReader preferencesReader = new BufferedReader( new FileReader( preferencesFile ) ); + + line = preferencesReader.readLine(); + + while ( line != null ) { + preferencesReadLine( line, widgetVector ); + line = preferencesReader.readLine(); + } +} + +/** +Read line of preferences and set widget to that line. + +@param line line of preferences +@param widgetVector memorable widgets +*/ + +void preferencesReadLine( String line, Vector widgetVector ) +{ + splitLine = line.split( "\t" ); + + if ( splitLine.length < 2 ) { + return; + } + + name = splitLine[ 0 ]; + + for ( int widgetIndex = 0; widgetIndex < widgetVector.size(); widgetIndex++ ) { + widget = widgetVector.elementAt( widgetIndex ); + + if ( widget.getName().equals( name ) ) { + preferencesReadWidget( splitLine[ 1 ], widget ); + + return; + } + } +} + +/** +Set widget to preferences value. + +@param value preferences value +@param widget widget to be set to value +*/ + +void preferencesReadWidget( String value, Widget widget ) +{ + if ( widget instanceof BCheckBox || widget instanceof BRadioButton ) { + widget.setState( Boolean.valueOf( value ) ); + + return; + } + + if ( widget instanceof BOutline ) { // it would be better to save the value instead of index because the list might change, but I'm lazy + bList = widget.getContent().getContent(); + selectedIndex = Integer.valueOf( value ); + bList.setSelected( selectedIndex, true ); + bList.scrollToItem( selectedIndex ); + + return; + } + + if ( widget instanceof BTextField ) { + widget.setText( value ); + + return; + } + + if ( widget instanceof ValueField ) { + widget.setValue( Double.valueOf( value ) ); + } +} + +/** +Write widget settings to preferences file. + +@param preferencesFilename preferences filename +@param widgetVector memorable widgets +*/ + +void preferencesWrite( String preferencesFilename, Vector widgetVector ) +{ + preferencesFile = new File( preferencesFilename ); + + if ( preferencesFile == null ) { + print( "Can not write preferences to " + preferencesFilename ); + + return; + } + + BufferedWriter preferencesWriter = new BufferedWriter( new FileWriter( preferencesFile ) ); + + for ( int widgetIndex = 0; widgetIndex < widgetVector.size(); widgetIndex++ ) { + widget = widgetVector.elementAt( widgetIndex ); + preferencesWriteWidget( preferencesWriter, widget ); + } + + //Close the output stream + preferencesWriter.close(); +} + +/** +Write widget settings to line of preferences. + +@param preferencesWriter buffered preferences file writer +@param widget widget to be written +*/ + +void preferencesWriteWidget( BufferedWriter preferencesWriter, Widget widget ) +{ + widgetString = widget.getName() + "\t"; + + if ( widget instanceof BCheckBox || widget instanceof BRadioButton ) { + preferencesWriter.write( widgetString + widget.getState().toString() + "\n" ); + + return; + } + + if ( widget instanceof BOutline ) { // it would be better to save the value because the list might change, but I'm lazy + BList bList = widget.getContent().getContent(); + bList = widget.getContent().getContent(); + preferencesWriter.write( widgetString + bList.getSelectedIndex().toString() + "\n" ); + + return; + } + + if ( widget instanceof BTextField ) { + preferencesWriter.write( widgetString + widget.getText() + "\n" ); + + return; + } + + if ( widget instanceof ValueField ) { + preferencesWriter.write( widgetString + widget.getValue().toString() + "\n" ); + } +} + +// Set default parameters. +directoryRadioButtonGroup = new RadioButtonGroup(); +openDirectoryButton = new BRadioButton( "Import All GNU Triangulated Surface Files in a Directory", false, directoryRadioButtonGroup ); +openFileButton = new BRadioButton( "Import File", true, directoryRadioButtonGroup ); +directoryGridContainer = new GridContainer( 1, 2 ); +directoryGridContainer.setDefaultLayout( new LayoutInfo( LayoutInfo.WEST, LayoutInfo.NONE, new Insets( 2, 2, 2, 2 ), null ) ); +directoryGridContainer.add( openDirectoryButton, 0, 0 ); +directoryGridContainer.add( openFileButton, 0, 1 ); +gnuSurfaceFilenameTextField = new BTextField( "" ); +String preferencesFilename = "import_gnu_triangulated_surface_preferences.csv"; + +Widget[] widgets = new Widget[] { directoryGridContainer }; +String[] widgetStrings = new String[] { "Open File or Directory:" }; + +// change the user interface parameters from default to preferences +Vector widgetVector = new Vector(); +RadioButtonGroup[] radioButtonGroups = new RadioButtonGroup[] { directoryRadioButtonGroup }; +preferencesAddRadioButtonGroups( radioButtonGroups, widgetVector ); +preferencesAddWidgetsWithStrings( widgets, widgetStrings, widgetVector ); +preferencesAddWidgetWithString( gnuSurfaceFilenameTextField, "GCode Filename:", widgetVector ); +preferencesRead( preferencesFilename, widgetVector ); + +dialog = new ComponentsDialog( window, "Import GNU Triangulated Surface File or Directory", widgets, widgetStrings ); + +if ( !dialog.clickedOk() ) return; + +fileChooser = new BFileChooser( BFileChooser.OPEN_FILE, "Import GNU Triangulated Surface File"); +gnuSurfaceFile = new File( gnuSurfaceFilenameTextField.getText() ); + +if ( gnuSurfaceFile.canRead() ) { + fileChooser.setSelectedFile( gnuSurfaceFile ); +} + +fileChooser.showDialog( window ); +file = fileChooser.getSelectedFile(); + +if ( file == null ) { + return; +} + +gnuSurfaceFilenameTextField.setText( file.getAbsolutePath() ); +preferencesWrite( preferencesFilename, widgetVector ); +importFiles(); diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Import Topology.bsh b/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Import Topology.bsh new file mode 100644 index 0000000..cb7b7d1 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Import Topology.bsh @@ -0,0 +1,225 @@ +/* + + + +*/ + +String addShape( BufferedReader bufferedReader, String line ) +{ + importShape = ImportShape(); + importShape.setBufferedReaderLine( bufferedReader, line ); + + while ( importShape.splitLine.length > 1 ) { + importShape.shouldRead = true; + + switch( importShape.firstLower ) { + + case "class": + importShape.setClass(); + break; + + case "faces": + importShape.setFaces(); + break; + + case "name": + importShape.setName(); + break; + + case "orientation": + importShape.setOrientation(); + break; + + case "origin": + importShape.setOrigin(); + break; + + case "vertexpositions": + importShape.setVertexPositions(); + break; + } + + if ( importShape.shouldRead ) { + importShape.readLine(); + } + } + + importShape.addShapeIfParametersSet(); + + return importShape.bufferedReader.readLine(); +} + +/** +Add curves and/or meshes from the file through the buffered reader. +*/ + +void addShapes( BufferedReader bufferedReader ) +{ + line = bufferedReader.readLine(); + + while ( line != null ) { + line = addShape( bufferedReader, line ); + } +} + +/** +Get a Vec3 from a string array. +*/ + +Vec3 getFromStringArray( String[] stringArray ) +{ + return new Vec3( Double.valueOf( stringArray[ 2 ] ), Double.valueOf( stringArray[ 3 ] ), Double.valueOf( stringArray[ 4 ] ) ); +} + +/** +Import the topology of a shape from the file through the buffered reader. +*/ + +ImportShape() { + BufferedReader bufferedReader = null; + String className = null; + Vector facesVector = null; + String firstLower = null; + String line = null; + String name = null; + Vec3 orientation = null; + Vec3 origin = null; + boolean shouldRead = true; + String[] splitLine = null; + Vector vertexPositionsVector = null; + +/** +Add the topology of a shape from the file through the buffered reader if all the parameters have been set. +*/ + + addShapeIfParametersSet() { + if ( className == null || name == null || orientation == null || origin == null || vertexPositionsVector == null ) { + print( "Shape has a null parameter and was not imported." ); + print( className ); + print( name ); + print( orientation ); + print( origin ); + print( vertexPositionsVector ); + return; + } + + vertexPositionsArray = vertexPositionsVector.toArray( new Vec3[ vertexPositionsVector.size() ] ); + CoordinateSystem coordinateSystem = new CoordinateSystem(); + coordinateSystem.setOrientation( orientation.x, orientation.y, orientation.z ); + coordinateSystem.setOrigin( origin ); + + while ( window.getScene().getObject( name ) != null ) { + name += "a"; + } + + if ( className.equals( "curve" ) ) { + float[] smoothness = new float[ vertexPositionsArray.length ]; + curve = new Curve( vertexPositionsArray, smoothness, Mesh.APPROXIMATING, true ); + window.addObject( curve, coordinateSystem, name, null ); + addUndoRecord(); + return; + } + + if ( facesVector == null ) { + return; + } + + if ( className.equals( "trianglemesh" ) ) { + facesArray = facesVector.toArray( new int[ facesVector.size() ][ 3 ] ); + mesh = new TriangleMesh ( vertexPositionsArray, facesArray ); + mesh.setSmoothingMethod( Mesh.NO_SMOOTHING ); + window.addObject( mesh, coordinateSystem, name, null ); + addUndoRecord(); + } + } + + addUndoRecord() { + scene = window.getScene(); + undoAdd = new UndoRecord ( window, false, UndoRecord.DELETE_OBJECT, new Object[] { new Integer( scene.getNumObjects() - 1 ) } ); + window.setUndoRecord( undoAdd ); + } + + readLine() { + line = bufferedReader.readLine(); + setSplitLine(); + } + + setBufferedReaderLine( BufferedReader argumentBufferedReader, String argumentLine ) { + bufferedReader = argumentBufferedReader; + line = argumentLine; + setSplitLine(); + } + + setClass() { + className = splitLine[ 1 ].toLowerCase(); + } + + setFaces() { + readLine(); + shouldRead = false; + facesVector = new Vector(); + + while ( splitLine[ 0 ].toLowerCase().equals( "face" ) ) { + int[] face = { Integer.valueOf( splitLine[ 2 ] ), Integer.valueOf( splitLine[ 3 ] ), Integer.valueOf( splitLine[ 4 ] ) }; + facesVector.add( face ); + readLine(); + } + } + + setName() { + name = splitLine[ 1 ]; + } + + setOrientation() { + orientation = getFromStringArray( splitLine ); + } + + setOrigin() { + origin = getFromStringArray( splitLine ); + } + + setSplitLine() { + if ( line == null ) { + splitLine = new String[] { "" }; + return; + } + + splitLine = line.split( "," ); + firstLower = splitLine[ 0 ].toLowerCase(); + } + + setVertexPositions() { + readLine(); + shouldRead = false; + vertexPositionsVector = new Vector(); + + while ( splitLine[ 0 ].toLowerCase().equals( "vertex position" ) ) { + vertexPositionsVector.add( getFromStringArray( splitLine ) ); + readLine(); + } + } + + return this; +} + +// Get parameters. + +fileChooser = new BFileChooser( BFileChooser.OPEN_FILE, "Import Comma Separated Values Topology"); +fileChooser.showDialog( window ); +filename = fileChooser.getSelectedFile(); + +if ( filename == null ) { + return; +} + +bufferedReader = new BufferedReader( new FileReader( filename ) ); +addShapes( bufferedReader ); \ No newline at end of file diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Platonic Solid.bsh b/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Platonic Solid.bsh new file mode 100644 index 0000000..6913a27 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Platonic Solid.bsh @@ -0,0 +1,391 @@ +/* + + + +*/ + +/* Copyright (C) 2004 by Fran�ois Guillet + + This program is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + PARTICULAR PURPOSE. See the GNU General Public License for more details. */ + +//Script localization test + +HashMap translations = new HashMap(); +if (Translate.getLocale().equals(Locale.FRENCH)) +{ + translations.put("Hexahedron", "Hexah\u00E8dre (6)"); + translations.put("Tetrahedron", "Tetrah\u00E8dre (4)"); + translations.put("Octahedron", "Octah\u00E8dre (8)"); + translations.put("Dodecahedron", "Dodecah\u00E8dre (12)"); + translations.put("Icosahedron", "Icosah\u00E8dre (20)"); + translations.put("centeredFaces", "sommets aux centres des faces"); + translations.put("selectPolyhedron", "S\u00E9lectionnez un polyh\u00E8dre :"); + translations.put("noSmoothing", "Pas de lissage" ); + translations.put("smoothShading", "Ombrage doux" ); + translations.put("interpolating", "Interpolation" ); + translations.put("approximating", "Approximation" ); + translations.put("polyhedron", "Polyh\u00E8dre:" ); + translations.put("faceCenteredMesh", "Maillage face centr\u00E9e" ); + translations.put("radius", "Rayon :" ); + translations.put("smoothingMethod", "M\u00E9thode de lissage:"); + translations.put("verticesSmoothness", "Lissage des sommets :"); + translations.put("edgesSmoothness", "Lissages des ar\u00EAtes :"); + translations.put("texture", "Texture :"); + translations.put("material", "Mat\u00E9riau :"); + translations.put("hexahedron", "Hexah\u00E8dre"); + translations.put("tetrahedron", "Tetrah\u00E8dre"); + translations.put("octahedron", "Octah\u00E8dre"); + translations.put("dodecahedron", "Dodecah\u00E8dre"); + translations.put("icosahedron", "Icosah\u00E8dre"); +} +else +{ + translations.put("Hexahedron", "Hexahedron (6)"); + translations.put("Tetrahedron", "Tetrahedron (4)"); + translations.put("Octahedron", "Octahedron (8)"); + translations.put("Dodecahedron", "Dodecahedron (12)"); + translations.put("Icosahedron", "Icosahedron (20)"); + translations.put("centeredFaces", "vertices at faces centers"); + translations.put("selectPolyhedron", "Select a polyhedron:"); + translations.put("noSmoothing", "No Smoothing" ); + translations.put("smoothShading", "Smooth Shading" ); + translations.put("interpolating", "Interpolating" ); + translations.put("approximating", "Approximating" ); + translations.put("polyhedron", "Polyhedron:" ); + translations.put("faceCenteredMesh", "Face centered mesh" ); + translations.put("radius", "Radius:" ); + translations.put("smoothingMethod", "Smoothing Method:"); + translations.put("verticesSmoothness", "Vertices Smoothness:"); + translations.put("edgesSmoothness", "Edges smoothness:"); + translations.put("texture", "Texture:"); + translations.put("material", "Material:"); + translations.put("hexahedron", "Hexahedron"); + translations.put("tetrahedron", "Tetrahedron"); + translations.put("octahedron", "Octahedron"); + translations.put("dodecahedron", "Dodecahedron"); + translations.put("icosahedron", "Icosahedron"); +} +//end of script localization test + + +//given a set of faces, this function computes the centers of the faces and adds them to the list of vertices +Vec3[] completeVertices(Vec3[] vert, int[][] polyFaces, int vertNum) +{ vertices = new Vec3[vert.length + polyFaces.length]; + + for (i = 0 ; i < vert.length ; ++i) + vertices[i] = vert[i]; + + for (i = vertNum; i < vertNum + polyFaces.length; ++i) + { v = new Vec3(); + for (j = 0 ; j < polyFaces[i - vertNum].length; ++j) + v.add(vertices[polyFaces[i - vertNum][j]]); + v.scale(1.0/polyFaces[i - vertNum].length); + vertices[i] = v; + } + + return vertices; +} + +//given a set of faces and a set of vertices, +//this function computes centered faces to form a triangular mesh +//presumably equivalent to subdivide face command in the mesh editor +TriangleMesh triangulateFaces(Vec3[] vertices, int[][] polyFaces, int vertNum) +{ vertices = completeVertices(vertices, polyFaces, vertNum); + + faces = new int[polyFaces.length * polyFaces[0].length][3]; + + f = 0; + for (i = 0 ; i < polyFaces.length ; ++i) + { for (j = 0; j < polyFaces[i].length ; ++j) + { if (j < polyFaces[i].length-1) + { faces[f][0] = polyFaces[i][j]; + faces[f][1] = polyFaces[i][j+1]; + faces[f][2] = vertNum + i; + } + else + { faces[f][0] = polyFaces[i][j]; + faces[f][1] = polyFaces[i][0]; + faces[f][2] = vertNum + i; + } + ++f; + } + } + mesh = new TriangleMesh(vertices, faces); + return mesh; +} + + +scene = window.getScene(); + +//setup the list of polyhedra +polyList = new BList(new String [] { + translations.get("Tetrahedron"), + translations.get("Hexahedron"), + translations.get("Octahedron"), + translations.get("Dodecahedron"), + translations.get("Icosahedron") +}); +polyList.setPreferredVisibleRows(5); +polyList.setSelected(0, true); + +//face centered checkbox +centeredCheckbox = new BCheckBox(translations.get("centeredFaces"), false); + +//radius value field +radiusValueField = new ValueField(1.0, ValueField.NONNEGATIVE); + +//smoothing method +smoothList = new BList(new String [] { + translations.get("noSmoothing"), + translations.get("smoothShading"), + translations.get("interpolating"), + translations.get("approximating") +}); +smoothList.setPreferredVisibleRows(4); +smoothList.setSelected(0, true); + +//vertices smoothness value field +vertSmoothnessValueField = new ValueField(1.0, ValueField.NONNEGATIVE); + +//edges smoothness value field +edgesSmoothnessValueField = new ValueField(1.0, ValueField.NONNEGATIVE); + +// get list of scene textures +numTex=scene.getNumTextures(); +textureList=new BList(); +textureList.setPreferredVisibleRows(3 < numTex ? 3 : numTex); +for (i = 0 ; i < numTex ; i++) +{ + iTex=scene.getTexture(i); + name=iTex.getName(); + textureList.add(name); +} +textureList.setSelected(0, true); + +// get list of materials +numMat=scene.getNumMaterials(); +materialList=new BList(); +materialList.setPreferredVisibleRows(3 < numMat + 1 ? 3 : numMat + 1); +materialList.add("none"); +for (i = 0 ; i < numMat ; i++) +{ + iMat=scene.getMaterial(i); + name=iMat.getName(); + materialList.add(name); +} +materialList.setSelected(0, true); + + +dlg = new ComponentsDialog(window, translations.get("selectPolyhedron"), +new Widget [] { UIUtilities.createScrollingList(polyList), centeredCheckbox, radiusValueField, UIUtilities.createScrollingList(smoothList), vertSmoothnessValueField, edgesSmoothnessValueField, UIUtilities.createScrollingList(textureList), UIUtilities.createScrollingList(materialList)}, +new String [] { translations.get("polyhedron"), translations.get("faceCenteredMesh"), translations.get("radius"), translations.get("smoothingMethod"), translations.get("verticesSmoothness"), translations.get("edgesSmoothness"), translations.get("texture"), translations.get("material") } ); + +if (!dlg.clickedOk()) return; + +centered = centeredCheckbox.getState(); + +switch (polyList.getSelectedIndex()) +{ case 0 : //tetrahedra + vertices = new Vec3[4]; // 4 vertices + 4 faces + vertices[0] = new Vec3(0, 0, 1); + vertices[1] = new Vec3(2*Math.sqrt(2)/3, 0, -1.0/3.0); + vertices[2] = new Vec3(-Math.sqrt(2)/3, Math.sqrt(6)/3, -1.0/3.0); + vertices[3] = new Vec3(-Math.sqrt(2)/3, -Math.sqrt(6)/3, -1.0/3.0); + int[][] faces = {{ 0, 1, 2 }, { 0, 2, 3 }, { 0, 3, 1 }, { 1, 3, 2 }}; + if (centered) + mesh = triangulateFaces(vertices, faces, 4); + else + mesh = new TriangleMesh(vertices, faces); + name = translations.get("tetrahedron") + (centered ? " FC" : ""); + break; + + case 1 : //hexahedron + vertices = new Vec3[8]; // 8 vertices + 6 faces + vertices[0] = new Vec3(-1, -1, -1); + vertices[1] = new Vec3(1, -1, -1); + vertices[2] = new Vec3(1, 1, -1); + vertices[3] = new Vec3(-1, 1, -1); + vertices[4] = new Vec3(-1, -1, 1); + vertices[5] = new Vec3(1, -1, 1); + vertices[6] = new Vec3(1, 1, 1); + vertices[7] = new Vec3(-1, 1, 1); + for (i = 0 ; i < 8 ; ++i) + vertices[i].scale(1.0/Math.sqrt(3)); + + //polygon faces when different from triangular faces + int[][] hexaFaces = {{ 0, 3, 2, 1 }, { 0, 1, 5, 4 }, { 0, 4, 7, 3 }, + { 6, 5, 1, 2 }, { 6, 2, 3, 7 }, { 6, 7, 4, 5 } }; + + //triangular faces + int[][] faces = {{ 0, 3, 2}, { 0, 2, 1}, { 0, 1, 5}, { 0, 5, 4}, { 0, 4, 7}, {0, 7, 3}, + { 6, 5, 1}, { 6, 1, 2}, { 6, 2, 3}, { 6, 3, 7}, { 6, 7, 4}, {6, 4, 5}}; + if (centered) + mesh = triangulateFaces(vertices, hexaFaces, 8); + else + mesh = new TriangleMesh(vertices, faces); + name = translations.get("hexahedron") + (centered ? " FC" : ""); + break; + + case 2 : //octahedron + vertices = new Vec3[6]; // 6 vertices + 8 faces + vertices[0] = new Vec3(1, 0, 0); + vertices[1] = new Vec3(-1, 0, 0); + vertices[2] = new Vec3(0, 1, 0); + vertices[3] = new Vec3(0, -1, 0); + vertices[4] = new Vec3(0, 0, 1); + vertices[5] = new Vec3(0, 0, -1); + + int[][] faces = { + { 4, 0, 2 }, { 4, 2, 1 }, { 4, 1, 3 }, { 4, 3, 0 }, + { 5, 2, 0 }, { 5, 1, 2 }, { 5, 3, 1 }, { 5, 0, 3 } + }; + + if (centered) + mesh = triangulateFaces(vertices, faces, 6); + else + mesh = new TriangleMesh(vertices, faces); + + name = translations.get("octahedron") + (centered ? " FC" : ""); + break; + + case 3 : //dodecahedron + vertices = new Vec3[20]; // 20 vertices + 12 faces + a = 1/Math.sqrt(3); + b = Math.sqrt( (3.0 - Math.sqrt(5)) /6.0); + c = Math.sqrt( (3.0 + Math.sqrt(5)) /6.0); + vertices[0] = new Vec3(a, a, a); + vertices[1] = new Vec3(a, a, -a); + vertices[2] = new Vec3(a, -a, a); + vertices[3] = new Vec3(a, -a, -a); + vertices[4] = new Vec3(-a, a, a); + vertices[5] = new Vec3(-a, a, -a); + vertices[6] = new Vec3(-a, -a, a); + vertices[7] = new Vec3(-a, -a, -a); + vertices[8] = new Vec3(b, c, 0); + vertices[9] = new Vec3(-b, c, 0); + vertices[10] = new Vec3(b, -c, 0); + vertices[11] = new Vec3(-b, -c, 0); + vertices[12] = new Vec3(c, 0, b); + vertices[13] = new Vec3(c, 0, -b); + vertices[14] = new Vec3(-c, 0, b); + vertices[15] = new Vec3(-c, 0, -b); + vertices[16] = new Vec3(0, b, c); + vertices[17] = new Vec3(0, -b, c); + vertices[18] = new Vec3(0, b, -c); + vertices[19] = new Vec3(0, -b, -c); + + int[][] faces = { //*quite* dull + { 0, 8, 9 }, { 0, 12, 13 }, { 0, 16, 17 }, { 8, 1, 18 }, + { 12, 2, 10 }, { 16, 4, 14 }, { 9, 5, 15 }, { 6, 11, 10 }, + { 3, 19, 18 }, { 7, 15, 5 }, { 7, 11, 6 }, { 7, 19, 3 }, + { 0, 9, 4 }, { 0, 13, 1 }, { 0, 17, 2 }, { 8, 18, 5 }, + { 12, 10, 3 }, { 16, 14, 6 }, { 9, 15, 14 }, { 6, 10, 2 }, + { 3, 18, 1 }, { 7, 5, 18 }, { 7, 6, 14 }, { 7, 3, 10 }, + { 0, 4, 16 }, { 0, 1, 8 }, { 0, 2, 12 }, { 8, 5, 9 }, + { 12, 3, 13 }, { 16, 6, 17 }, { 9, 14, 4 }, { 6, 2, 17 }, + { 3, 1, 13 }, { 7, 18, 19 }, { 7, 14, 15 }, { 7, 10, 11 } + }; + + int[][] dodeFaces = { + { 0, 8, 9, 4, 16}, { 0, 16, 17, 2, 12}, { 12, 2, 10, 3, 13}, + { 9, 5, 15, 14, 4}, { 3, 19, 18, 1, 13}, { 7, 11, 6, 14, 15}, + { 0, 12, 13, 1, 8}, { 8, 1, 18, 5, 9}, { 16, 4, 14, 6, 17}, + { 6, 11, 10, 2, 17}, { 7, 15, 5, 18, 19}, { 7, 19, 3, 10, 11} + }; + + if (centered) + mesh = triangulateFaces(vertices, dodeFaces, 20); + else + mesh = new TriangleMesh(vertices, faces); + + name = translations.get("dodecahedron") + (centered ? " FC" : ""); + break; + + case 4 : //icosahedron + vertices = new Vec3[12]; // 12 vertices + 20 faces + t = ( 1.0 + Math.sqrt(5) )/2.0; + vertices[0] = new Vec3(t, 1, 0); + vertices[1] = new Vec3(-t, 1, -0); + vertices[2] = new Vec3(t, -1, 0); + vertices[3] = new Vec3(-t, -1, 0); + vertices[4] = new Vec3(1, 0, t); + vertices[5] = new Vec3(1, 0, -t); + vertices[6] = new Vec3(-1, 0, t); + vertices[7] = new Vec3(-1, 0, -t); + vertices[8] = new Vec3(0, t, 1); + vertices[9] = new Vec3(0, -t, 1); + vertices[10] = new Vec3(0, t, -1); + vertices[11] = new Vec3(0, -t, -1); + for (i = 0 ; i < 12 ; ++i) + vertices[i].scale( 1.0 / Math.sqrt( 1.0 + t*t) ); + + int[][] faces = { + { 0, 8, 4 }, { 0, 5, 10 }, { 2, 4, 9 }, { 2, 11, 5 }, + { 1, 6, 8 }, { 1, 10, 7 }, { 3, 9, 6 }, { 3, 7, 11 }, + { 0, 10, 8 }, { 1, 8, 10 }, { 2, 9, 11 }, { 3, 11, 9 }, + { 4, 2, 0 }, { 5, 0, 2 }, { 6, 1, 3 }, { 7, 3, 1 }, + { 8, 6, 4 }, { 9, 4, 6 }, { 10, 5, 7 }, { 11, 7, 5 } + }; + + + if (centered) + mesh = triangulateFaces(vertices, faces, 12); + else + mesh = new TriangleMesh(vertices, faces); + + name = translations.get("icosahedron") + (centered ? " FC" : ""); + break; + default : + return; +} + +//set vertices smoothness and scale mesh in the process +meshVertices = mesh.getVertices(); +radius = radiusValueField.getValue(); +for ( i = 0 ; i < meshVertices.length ; ++i) +{ meshVertices[i].smoothness = (float) vertSmoothnessValueField.getValue(); + meshVertices[i].r.scale(radius); +} + +//set edges smoothness +meshEdges = mesh.getEdges(); +for ( i = 0 ; i < meshEdges.length ; ++i) + meshEdges[i].smoothness = (float) edgesSmoothnessValueField.getValue(); + +//set mesh smoothing method +mesh.setSmoothingMethod(smoothList.getSelectedIndex()); + +//set texture +//tex = scene.getTexture(textureList.getSelectedIndex()); +//mesh.setTexture(tex, tex.getDefaultMapping()); + +//set material +matIndex = materialList.getSelectedIndex(); +if ( matIndex > 0 ) +{ mat = scene.getMaterial(matIndex-1); + mesh.setMaterial(mat, mat.getDefaultMapping()); +} + +//we're done +window.addObject(mesh, new CoordinateSystem(), name, null); + + diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Truncated Teardrop Shaper.bsh b/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Truncated Teardrop Shaper.bsh new file mode 100644 index 0000000..766c3ac --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/Art of Illusion Scripts/Truncated Teardrop Shaper.bsh @@ -0,0 +1,119 @@ +/* + + +*/ + +do +{ + scene = window.getScene(); + + directionType = new RadioButtonGroup(); + up = new BRadioButton("Up", false, directionType); + down = new BRadioButton("Down", false, directionType); + left = new BRadioButton("Left", false, directionType); + right = new BRadioButton("Right", false, directionType); + + directional = new GridContainer(2, 2); + directional.setDefaultLayout(new LayoutInfo(LayoutInfo.WEST, LayoutInfo.NONE, new Insets(2, 2, 2, 2), null)); + directional.add(up, 0, 0); + directional.add(down, 0, 1); + directional.add(left, 1, 0); + directional.add(right, 1, 1); + + vertexValueField = new ValueField(45, ValueField.NONNEGATIVE); + radiusValueField = new ValueField(1.0, ValueField.NONNEGATIVE); + maxErrorValueField = new ValueField(0.05, ValueField.NONNEGATIVE); + directionValueField = new ValueField(0.0, ValueField.NONNEGATIVE); + truncationValueField = new ValueField(0.0, ValueField.NONNEGATIVE); + + dlg = new ComponentsDialog(window, "TearDrop Tool" , + new Widget [] { directional, directionValueField, vertexValueField, truncationValueField, radiusValueField, maxErrorValueField }, + new String [] { "Orientation about z-axis: ", "or specific rotation about z-axis (degrees): ", "Vertex angle (degrees): ", "Truncation: ", "Radius: ", "Max error: " } ); + + if (!dlg.clickedOk()) return; + + theta = vertexValueField.getValue() / 180 * Math.PI; + radius = radiusValueField.getValue(); + maxError = maxErrorValueField.getValue(); + directionAngle = directionValueField.getValue() / 180 * Math.PI; + + errorAngle = Math.acos((radius - maxError) / radius); // inverse cosine + +// numberSides = Math.ceil(((2 * Math.PI) - 2 * (Math.PI / 2 - theta)) / errorAngle); // rounded up + numberSides = Math.ceil(((2 * Math.PI) - 2 * (Math.PI / 2 - theta)) / errorAngle) + 1; // rounded up + + if (numberSides <= 10) + new MessageDialog(window, "Please decrease the value of the maximum error"); +} +while (numberSides < 10); + + +// Setting the orientation of the teardrop shape depending on the outcome of Radio Buttons +if (up.getState()) + directionAngle = Math.PI / 2; + +if (down.getState()) + directionAngle = (3 * Math.PI) / 2; + +if (left.getState()) + directionAngle = Math.PI; + +if (right.getState()) + directionAngle = 0; + + +Vec3[] v = new Vec3[numberSides]; +float[] smoothness = new float[numberSides]; + +//double phi = ((2.0 * Math.PI) - (2.0 * ((Math.PI / 2) - theta))) / (numberSides - 2); +double phi = ((2.0 * Math.PI) - (2.0 * ((Math.PI / 2) - theta))) / (numberSides - 3); + +double angle = ((Math.PI / 2.0) - theta); + +//for (int i = 0 ; i < (numberSides - 1) ; i++) +for (int i = 0 ; i < (numberSides - 2) ; i++) +{ + x1 = Math.cos(angle + phi * i); + y1 = Math.sin(angle + phi * i); + x2 = x1 * Math.cos(directionAngle) - y1 * Math.sin(directionAngle); // rotational vectors - x + y2 = y1 * Math.cos(directionAngle) + x1 * Math.sin(directionAngle); // rotational vectors - y + v[i] = new Vec3(x2, y2, 0); + v[i].scale(radius); + smoothness[i] = 0; +} + +// final vertex point at the sharp tip +//x3 = Math.cos(directionAngle) * (radius / Math.sin(theta)); +//y3 = Math.sin(directionAngle) * (radius / Math.sin(theta)); +//v[numberSides - 2.0] = new Vec3(x3, y3, 0); +truncationToVertex = truncationValueField.getValue() * (2 - Math.sqrt(2)); +xTip = Math.cos(directionAngle) * (radius / Math.sin(theta)); +yTip = Math.sin(directionAngle) * (radius / Math.sin(theta)); +vTip = new Vec3(xTip, yTip, 0); +vLast = v[numberSides - 3.0]; +vFromTipToLast = vLast.minus( vTip ); +vFromTipToLast.scale( truncationToVertex ); +v[numberSides - 2.0] = vTip.plus( vFromTipToLast ); +vStart = v[0]; +vFromTipToStart = vStart.minus( vTip ); +vFromTipToStart.scale( truncationToVertex ); +x4 = xTip + 0.2; +y4 = yTip + 0.3; +v[numberSides - 1.0] = vTip.plus( vFromTipToStart ); + +name = "Truncated Teardrop (" + numberSides + " sides)"; + +tolerance = 0.02; //surface accuracy + +curve = new Curve(v, smoothness, Mesh.APPROXIMATING, true); +window.addObject(curve, new CoordinateSystem(), name, null); + +// Finished \ No newline at end of file diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/RepRapArduinoSerialSender.py b/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/RepRapArduinoSerialSender.py new file mode 100644 index 0000000..d5ce0ba --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/RepRapArduinoSerialSender.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +Extrude requires pySerial installed for this module to work. If you are using Fedora it is available on yum +(run "sudo yum install pyserial"). To actually control the reprap requires write access to the serial device, +running as root is one way to get that access. + +Created by Brendan Erwin on 2008-05-21. +Copyright (c) 2008 Brendan Erwin. All rights reserved. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +""" + +try: + import serial # Import the pySerial modules. +except: + print('You do not have pySerial installed, which is needed to control the serial port.') + print('Information on pySerial is at:\nhttp://pyserial.wiki.sourceforge.net/pySerial') + +import os +import sys +import time + + +class RepRapArduinoSerialSender: + """ + A utility class for communication with the Arduino from python. + Intended for g-code only. Raises ValueException if the arduino + returns an unexpected response. Usually caused by sending invalid + g-code. + """ + + _verbose = False + block = "empty" + + def __init__(self, port, baud, verbose=False): + """ + Opens the serial port and prepares for writing. + port MUST be set, and values are operating system dependant. + """ + self._verbose = verbose + + if self._verbose: + print >> sys.stdout, "Opening serial port: " + port + + #Timeout value 10" max travel, 1RPM, 20 threads/in = 200 seconds + self.ser = serial.Serial(port, baud, timeout=200) + + if self._verbose: + print >> sys.stdout, "Serial Open?: " + str(self.ser.isOpen()) + print >> sys.stdout, "Baud Rate: " + str(self.ser.baudrate) + + def reset(self): + """ + Resets the arduino by droping DTR for 1 second + This will then wait for a response ("ready") and return. + """ + #Reboot the arduino, and wait for it's response + if self._verbose: + print "Resetting arduino..." + + self.ser.setDTR(0) + # There is presumably some latency required. + time.sleep(1) + self.ser.setDTR(1) + self.read("Start") + + def write(self, block): + """ + Writes one block of g-code out to arduino and waits for an "ok". + This version will wait for an "ok" before returning and prints any intermediate output received. + No error will be raised if non-ok response is received. Loop in read() is infinite if "ok" + does not come back! + This routine also removes all whitespace before sending it to the arduino, + which is handy for gcode, but will screw up if you try to do binary communications. + """ + if self._verbose: + print "> " + block + + # The arduino GCode interperter firmware doesn't like whitespace + # and if there's anything other than space and tab, we have other problems. + block=block.strip() + block=block.replace(' ','') + block=block.replace("\t",'') + #Skip blank blocks. + if len(block) == 0: + return + + self.ser.write(block + "\n") + self.read("OK") + + def read(self, expect=None): + """ + This routine should never be called directly. It's used by write() and reset() + to read a one-line response from the Arduino. + This version will wait for an "ok" before returning and prints any intermediate output received. + No error will be raised if non-ok response is received. Loop is infinite if "ok" + does not come back! + """ + #The g-code firmware returns exactly ONE line per block of gcode sent. + #Unless it is M104, M105 or other code that returns info!! + #It WILL return "ok" once the command has finished sending and completed. + while True: + response = self.ser.readline().strip() + if expect is None: + return + + if expect.lower() in response.lower(): + if self._verbose: + print "< " + response + return + else: + #Just print the response since it is useful data or an error message + print "< " + response + + + def close(): + """ + Closes the serial port, terminating communications with the arduino. + """ + if self._verbose: + print >> sys.stdout, "Closing serial port." + self.ser.close() + + if self._verbose: + print >> sys.stdout, "Serial Open?: " + str(self.ser.isOpen()) diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/__init__.py b/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/__init__.py new file mode 100644 index 0000000..036c122 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/__init__.py @@ -0,0 +1,11 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. + +import os +import sys + +numberOfLevelsDeepInPackageHierarchy = 2 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/demo.py b/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/demo.py new file mode 100644 index 0000000..c5cb182 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/demo.py @@ -0,0 +1,24 @@ +try: + import serial +except: + print('You do not have pySerial installed, which is needed to control the serial port.') + print('Information on pySerial is at:\nhttp://pyserial.wiki.sourceforge.net/pySerial') +import reprap, time # Import the reprap and pySerial modules. + +reprap.serial = serial.Serial(0, 19200, timeout = reprap.snap.messageTimeout) # Initialise serial port, here the first port (0) is used. +reprap.cartesian.x.active = True # These devices are present in network, will automatically scan in the future. +reprap.cartesian.y.active = True +reprap.cartesian.z.active = True +reprap.extruder.active = True +# The module is now ready to recieve commands # +moveSpeed = 220 +reprap.cartesian.homeReset( moveSpeed, True ) # Send all axies to home position. Wait until arrival. +reprap.cartesian.seek( (1000, 1000, 0), moveSpeed, True ) # Seek to (1000, 1000, 0). Wait until arrival. +time.sleep(2) # Pause. +reprap.cartesian.seek( (500, 1000, 0), moveSpeed, True ) # Seek to (500, 1000, 0). Wait until arrival. +time.sleep(2) +reprap.cartesian.seek( (1000, 500, 0), moveSpeed, True ) # Seek to (1000, 500, 0). Wait until arrival. +time.sleep(2) +reprap.cartesian.seek( (100, 100, 0), moveSpeed, True ) # Seek to (100, 100, 0). Wait until arrival. +reprap.cartesian.homeReset( moveSpeed, True ) # Send all axies to home position. Wait until arrival. +reprap.cartesian.free() # Shut off power to all motors. diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/example.py b/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/example.py new file mode 100644 index 0000000..640bf8d --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/example.py @@ -0,0 +1,144 @@ +#!/usr/bin/python +try: + import serial +except: + print('You do not have pySerial installed, which is needed to control the serial port.') + print('Information on pySerial is at:\nhttp://pyserial.wiki.sourceforge.net/pySerial') + +import reprap, time, sys + +#reprap.snap.printOutgoingPackets = True +#reprap.snap.printIncomingPackets = True +#reprap.snap.printFailedPackets = True +#reprap.printDebug = True + +#work surface approx x 2523, y 2743 + +#reprap.serial = serial.Serial(0, 19200, timeout = reprap.snap.messageTimeout) +reprap.serial = serial.Serial(0, 19200, timeout = 60) + +reprap.cartesian.x.active = True # these devices are present in network +reprap.cartesian.y.active = True +reprap.cartesian.z.active = True +reprap.extruder.active = True + +reprap.cartesian.x.setNotify() +reprap.cartesian.y.setNotify() +reprap.cartesian.z.setNotify() +reprap.cartesian.x.limit = 2523 +#reprap.cartesian.y.limit = 2743 +reprap.cartesian.y.limit = 2000 + +def printPos(): + x, y, z = reprap.cartesian.getPos() + print "Location [" + str(x) + ", " + str(y) + ", " + str(z) + "]" + + +print "================================================================" + + +########### control of cartesian frame as a whole ######### + +#stop all steppers +if sys.argv[1] == "stop": + reprap.cartesian.stop() + +#goto 0,0 +if sys.argv[1] == "reset": + reprap.cartesian.homeReset( 200, True ) + #time.sleep(2) + printPos() + +#print current positon +if sys.argv[1] == "pos": + printPos() + +#goto a specific location +if sys.argv[1] == "goto": + reprap.cartesian.seek( ( int(sys.argv[2]), int(sys.argv[3]), 0 ), 200, False) + printPos() + +#goto a specific location (use sync) +if sys.argv[1] == "gotos": + reprap.cartesian.syncSeek( ( int(sys.argv[2]), int(sys.argv[3]), 0 ), 200, False) + printPos() + +if sys.argv[1] == "power": + reprap.cartesian.setPower( int( sys.argv[2] ) ) # This is a value from 0 to 63 (6 bits) + + +#test routine +if sys.argv[1] == "go": #stepper test + reprap.cartesian.seek( (1000, 1000, 0), 200, True ) + time.sleep(2) + reprap.cartesian.seek( (500, 1000, 0), 200, True ) + time.sleep(2) + reprap.cartesian.seek( (500, 500, 0), 200, True ) + time.sleep(2) + reprap.cartesian.seek( (10, 10, 0), 200, True ) + +#free motors (switch off all coils) +if sys.argv[1] == "free": + reprap.axies.free(reprap.axisX) + reprap.axies.free(reprap.axisY) + +############## control of individual steppers ############# + +#spin stepper +if sys.argv[1] == "run": # run axis + if sys.argv[2] == "x": + reprap.cartesian.x.forward( int(sys.argv[3]) ) + elif sys.argv[2] == "y": + reprap.cartesian.y.forward( int(sys.argv[3]) ) + +#spin stepper in reverse +if sys.argv[1] == "runb": #runb axis + if sys.argv[2] == "x": + reprap.axies.backward( reprap.axisX, int(sys.argv[3]) ) + elif sys.argv[2] == "y": + reprap.axies.backward( reprap.axisY, int(sys.argv[3]) ) + +if sys.argv[1] == "step": + if sys.argv[2] == "x": + reprap.cartesian.x.forward1() + elif sys.argv[2] == "y": + reprap.cartesian.y.forward1() + +################# control of extruder ##################### + +#test extrder motor +elif sys.argv[1] == "motor": + nn = 0 + while 1: + if nn > 0: + nn = 0 + else: + nn = 150 + reprap.extruder.setMotor(reprap.CMD_REVERSE, nn) + time.sleep(1) + +elif sys.argv[1] == "getinfo": + mtype = reprap.extruder.getModuleType() + version = reprap.extruder.getVersion() + print "module", mtype, "version", version + +elif sys.argv[1] == "heat": + reprap.extruder.setHeat(255, 255, 255, 255) + +#setHeat(self, lowHeat, highHeat, tempTarget, tempMax + +elif sys.argv[1] == "temp": + print "Temp is ", reprap.extruder.getTemp() + +elif sys.argv[1] == "setref": + reprap.extruder.setVoltateReference( int(sys.argv[2]) ) + + + + +############### scan network for devices ################### + +#scan snap network +elif sys.argv[1] == "scan": + reprap.scanNetwork() + diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/extrude.py b/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/extrude.py new file mode 100644 index 0000000..564619b --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/extrude.py @@ -0,0 +1,390 @@ +""" +Extrude is a script to display and extrude a gcode file. + +It controls the extruder and movement. It can read linear and helical move commands. It saves a log file with the suffix _log. + +To run extrude, install python 2.x on your machine, which is avaliable from http://www.python.org/download/ + +Then in the folder which extrude is in, type 'python' in a shell to run the python interpreter. Finally type 'import extrude' to import +this program. Extrude requires pySerial installed for this module to work. If you are using Fedora it is available on yum +(run "sudo yum install pyserial"). To actually control the reprap requires write access to the serial device, running as root is +one way to get that access. + + +This example displays and extrudes a gcode file. This example is run in a terminal as root in the folder which contains +Hollow Square.gcode, and extrude.py. + +>>> import extrude +Extrude has been imported. +The gcode files in this directory that are not log files are the following: +['Hollow Square.gcode'] + + +>>> extrude.display() +File Hollow Square.gcode is being displayed. +reprap.serial = serial.Serial(0, 19200, timeout = 60) +reprap.cartesian.x.active = True +reprap.cartesian.y.active = True +reprap.cartesian.z.active = True +reprap.extruder.active = True +reprap.cartesian.x.setNotify() +reprap.cartesian.y.setNotify() +reprap.cartesian.z.setNotify() +reprap.cartesian.x.limit = 2523 +reprap.cartesian.y.limit = 2000 +reprap.cartesian.homeReset( 200, True ) +( GCode generated by March 29,2007 Skeinforge ) +( Extruder Initialization ) +M100 P210 +M103 +reprap.extruder.setMotor(reprap.CMD_REVERSE, 0) +.. +many lines of gcode and extruder commands +.. +reprap.cartesian.homeReset( 600, True ) +reprap.cartesian.free() +The gcode log file is saved as Hollow Square_log.gcode + + +>>> extrude.displayFile("Hollow Square.gcode") +File Hollow Square.gcode is being displayed. +.. +The gcode log file is saved as Hollow Square_log.gcode + + +>>> extrude.displayFiles(["Hollow Square.gcode"]) +File Hollow Square.gcode is being displayed. +.. +The gcode log file is saved as Hollow Square_log.gcode + + +>>> extrude.displayText(" +( GCode generated by March 29,2007 Skeinforge ) +( Extruder Initialization ) +.. +many lines of gcode +.. +") + +reprap.serial = serial.Serial(0, 19200, timeout = 60) +reprap.cartesian.x.active = True +reprap.cartesian.y.active = True +reprap.cartesian.z.active = True +reprap.extruder.active = True +reprap.cartesian.x.setNotify() +reprap.cartesian.y.setNotify() +reprap.cartesian.z.setNotify() +reprap.cartesian.x.limit = 2523 +reprap.cartesian.y.limit = 2000 +reprap.cartesian.homeReset( 200, True ) +( GCode generated by March 29,2007 Skeinforge ) +( Extruder Initialization ) +M100 P210 +M103 +reprap.extruder.setMotor(reprap.CMD_REVERSE, 0) +.. +many lines of gcode and extruder commands +.. +reprap.cartesian.homeReset( 600, True ) +reprap.cartesian.free() + + +Note: On my system the reprap is not connected, so I get can not connect messages, like: + +>>> extrude.extrude() +File Hollow Square.gcode is being extruded. +reprap.serial = serial.Serial(0, 19200, timeout = 60) +reprap.cartesian.x.active = True +reprap.cartesian.y.active = True +reprap.cartesian.z.active = True +reprap.extruder.active = True +reprap.cartesian.x.setNotify() +Error: Serial timeout +Error: ACK not recieved +.. + +On a system where a reprap is connected to the serial port, you should get the following: + +>>> extrude.extrude() +File Hollow Square.gcode is being extruded. +.. +The gcode log file is saved as Hollow Square_log.gcode + + +>>> extrude.extrudeFile("Hollow Square.gcode") +File Hollow Square.gcode is being extruded. +.. +The gcode log file is saved as Hollow Square_log.gcode + + +>>> extrude.extrudeFiles(["Hollow Square.gcode"]) +File Hollow Square.gcode is being extruded. +.. +The gcode log file is saved as Hollow Square_log.gcode + + +>>> extrude.extrudeText(" +( GCode generated by March 29,2007 Skeinforge ) +( Extruder Initialization ) +.. +many lines of gcode +.. +") + +reprap.serial = serial.Serial(0, 19200, timeout = 60) +reprap.cartesian.x.active = True +reprap.cartesian.y.active = True +reprap.cartesian.z.active = True +reprap.extruder.active = True +reprap.cartesian.x.setNotify() +reprap.cartesian.y.setNotify() +reprap.cartesian.z.setNotify() +reprap.cartesian.x.limit = 2523 +reprap.cartesian.y.limit = 2000 +reprap.cartesian.homeReset( 200, True ) +( GCode generated by March 29,2007 Skeinforge ) +( Extruder Initialization ) +M100 P210 +M103 +reprap.extruder.setMotor(reprap.CMD_REVERSE, 0) +.. +many lines of gcode and extruder commands +.. +reprap.cartesian.homeReset( 600, True ) +reprap.cartesian.free() +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +try: + import serial # Import the pySerial modules. +except: + print('You do not have pySerial installed, which is needed to control the serial port.') + print('Information on pySerial is at:\nhttp://pyserial.wiki.sourceforge.net/pySerial') + +from skeinforge_tools.skeinforge_utilities.vector3 import Vector3 +from skeinforge_tools.skeinforge_utilities import archive +from skeinforge_tools.skeinforge_utilities import euclidean +from skeinforge_tools.skeinforge_utilities import gcodec +import math +import os +import reprap # Import the reprap module. +import time + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'greenarrow ' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def display( filename = ''): + "Parse a gcode file and display the commands. If no filename is specified, parse all the gcode files which are not log files in this folder." + if filename == '': + displayFiles( getGCodeFilesWhichAreNotLogFiles() ) + return + displayFile( filename ) + +def displayFile( filename ): + "Parse a gcode file and display the commands." + print('File ' + filename + ' is being displayed.') + fileText = archive.getFileText( filename ) + gcodec.writeFileMessageSuffix( filename, displayText(fileText), 'The gcode log file is saved as ', '_log') + +def displayFiles( filenames ): + "Parse gcode files and display the commands." + for filename in filenames: + displayFile( filename ) + +def displayText(gcodeText): + "Parse a gcode text and display the commands." + skein = displaySkein() + skein.parseText(gcodeText) + return skein.output + +def extrude( filename = ''): + """Parse a gcode file and send the commands to the extruder. If no filename is specified, parse all the gcode files which are not log files in this folder. + This function requires write access to the serial device, running as root is one way to get that access.""" + if filename == '': + extrudeFiles( getGCodeFilesWhichAreNotLogFiles() ) + return + extrudeFile( filename ) + +def extrudeFile( filename ): + """Parse a gcode file and send the commands to the extruder. + This function requires write access to the serial device, running as root is one way to get that access.""" + print('File ' + filename + ' is being extruded.') + fileText = archive.getFileText( filename ) + gcodec.writeFileMessageSuffix( filename, extrudeText(fileText), 'The gcode log file is saved as ', '_log') + +def extrudeFiles( filenames ): + """Parse gcode files and send the commands to the extruder. + This function requires write access to the serial device, running as root is one way to get that access.""" + for filename in filenames: + extrudeFile( filename ) + +def extrudeText(gcodeText): + """Parse a gcode text and send the commands to the extruder. + This function requires write access to the serial device, running as root is one way to get that access.""" + skein = extrudeSkein() + skein.parseText(gcodeText) + return skein.output + +def getGCodeFilesWhichAreNotLogFiles(): + "Get gcode files which are not log files." + return archive.getFilesWithFileTypeWithoutWords('gcode', ['_log'] ) + +def getIntegerString(number): + "Get integer as string." + return str( int(number) ) + + +class displaySkein: + "A class to display a gcode skein of extrusions." + def __init__(self): + self.extruderActive = 0 + self.feedrateMinute = 200.0 + self.oldLocation = None + self.output = '' + + def addToOutput(self, line): + "Add line with a newline at the end to the output." + print(line) + self.output += line + '\n' + + def evaluateCommand( self, command ): + "Add an extruder command to the output." + self.addToOutput( command ) + + def helicalMove( self, isCounterclockwise, splitLine ): + "Parse a helical move gcode line and send the commands to the extruder." + if self.oldLocation == None: + return + location = Vector3( self.oldLocation ) + self.setFeedrate(splitLine) + setPointToSplitLine( location, splitLine ) + location = location + self.oldLocation + center = Vector3( self.oldLocation ) + indexOfR = getIndexOfStartingWithSecond( "R", splitLine ) + if indexOfR > 0: + radius = getDoubleAfterFirstLetter( splitLine[ indexOfR ] ) + halfLocationMinusOld = location - self.oldLocation + halfLocationMinusOld *= 0.5 + halfLocationMinusOldLength = halfLocationMinusOld.length() + centerMidpointDistance = math.sqrt( radius * radius - halfLocationMinusOldLength * halfLocationMinusOldLength ) + centerMinusMidpoint = getRotatedWiddershinsQuarterAroundZAxis( halfLocationMinusOld ) + centerMinusMidpoint.normalize() + centerMinusMidpoint *= centerMidpointDistance + if isCounterclockwise: + center.setToVec3( halfLocationMinusOld + centerMinusMidpoint ) + else: + center.setToVec3( halfLocationMinusOld - centerMinusMidpoint ) + else: + center.x = getDoubleForLetter( "I", splitLine ) + center.y = getDoubleForLetter( "J", splitLine ) + curveSection = 0.5 + center += self.oldLocation + afterCenterSegment = location - center + beforeCenterSegment = self.oldLocation - center + afterCenterDifferenceAngle = getAngleAroundZAxisDifference( afterCenterSegment, beforeCenterSegment ) + absoluteDifferenceAngle = abs( afterCenterDifferenceAngle ) + steps = int( math.ceil( max( absoluteDifferenceAngle * 2.4, absoluteDifferenceAngle * beforeCenterSegment.length() / curveSection ) ) ) + stepPlaneAngle = getPolar( afterCenterDifferenceAngle / steps, 1.0 ) + zIncrement = ( afterCenterSegment.z - beforeCenterSegment.z ) / float( steps ) + for step in range( 1, steps ): + beforeCenterSegment = getRoundZAxisByPlaneAngle( stepPlaneAngle, beforeCenterSegment ) + beforeCenterSegment.z += zIncrement + arcPoint = center + beforeCenterSegment + self.moveExtruder( arcPoint ) + self.moveExtruder( location ) + self.oldLocation = location + + def homeReset(self): + "Send all axies to home position. Wait until arrival." + homeCommandString = 'reprap.cartesian.homeReset(' + getIntegerString( self.feedrateMinute ) + ', True )' + self.evaluateCommand( homeCommandString ) + + def linearMove( self, splitLine ): + "Parse a linear move gcode line and send the commands to the extruder." + location = Vector3() + if self.oldLocation != None: + location = self.oldLocation + self.setFeedrate(splitLine) + setPointToSplitLine( location, splitLine ) + self.moveExtruder( location ) + self.oldLocation = location + + def moveExtruder(self, location): + "Seek to location. Wait until arrival." + moveSpeedString = getIntegerString( self.feedrateMinute ) + xMoveString = getIntegerString(location.x) + yMoveString = getIntegerString(location.y) + zMoveString = getIntegerString(location.z) + moveCommandString = 'reprap.cartesian.seek( (' + xMoveString + ', ' + yMoveString + ', ' + zMoveString + '), ' + moveSpeedString + ', True )' + self.evaluateCommand( moveCommandString ) + + def parseGCode(self, lines): + "Parse gcode and send the commands to the extruder." + self.evaluateCommand('reprap.serial = serial.Serial(0, 19200, timeout = 60)') # Initialise serial port, here the first port (0) is used. + self.evaluateCommand('reprap.cartesian.x.active = True') # These devices are present in network, will automatically scan in the future. + self.evaluateCommand('reprap.cartesian.y.active = True') + self.evaluateCommand('reprap.cartesian.z.active = True') + self.evaluateCommand('reprap.extruder.active = True') + self.evaluateCommand('reprap.cartesian.x.setNotify()') + self.evaluateCommand('reprap.cartesian.y.setNotify()') + self.evaluateCommand('reprap.cartesian.z.setNotify()') + self.evaluateCommand('reprap.cartesian.x.limit = 2523') + self.evaluateCommand('reprap.cartesian.y.limit = 2000') + self.homeReset() # The module is now ready to receive commands + for line in lines: + self.parseLine(line) + self.homeReset() + self.evaluateCommand('reprap.cartesian.free()') # Shut off power to all motors. + + def parseLine(self, line): + "Parse a gcode line and send the command to the extruder." + self.addToOutput(line) + splitLine = line.split(' ') + if len(splitLine) < 1: + return 0 + firstWord = splitLine[0] + if firstWord == 'G1': + self.linearMove(splitLine) + if firstWord == 'G2': + self.helicalMove( False, splitLine ) + if firstWord == 'G3': + self.helicalMove( True, splitLine ) + if firstWord == 'M101': + self.extruderActive = 1 + self.evaluateCommand('reprap.extruder.setMotor(reprap.CMD_REVERSE, 150)') + if firstWord == 'M103': + self.extruderActive = 0 + self.evaluateCommand('reprap.extruder.setMotor(reprap.CMD_REVERSE, 0)') + self.oldActiveLocation = None + + def parseText( self, text ): + "Parse a gcode text and evaluate the commands." + textLines = getTextLines(text) + self.parseGCode( textLines ) + + def setFeedrate( self, splitLine ): + "Set the feedrate to the gcode split line." + indexOfF = getIndexOfStartingWithSecond( "F", splitLine ) + if indexOfF > 0: + self.feedrateMinute = getDoubleAfterFirstLetter( splitLine[indexOfF] ) + + +class extrudeSkein( displaySkein ): + "A class to extrude a gcode skein of extrusions." + def evaluateCommand( self, command ): + """Add an extruder command to the output and evaluate the extruder command. + Display the entire command, but only evaluate the command after the first equal sign.""" + self.addToOutput( command ) + firstEqualIndex = command.find('=') + exec( command ) + + +print('Extrude has been imported.') +print('The gcode files in this directory that are not log files are the following:') +print(getGCodeFilesWhichAreNotLogFiles()) diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/frank_davies/bring_to_temp.py b/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/frank_davies/bring_to_temp.py new file mode 100644 index 0000000..4fe8ce5 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/frank_davies/bring_to_temp.py @@ -0,0 +1,47 @@ +# bring reprap to temperature +# Frank Davies +import serial +import time +import sys + +def out_rep(out_string): + ser.write(out_string) + print out_string + #print "waiting for OK" + start_time=time.clock() + while (ser.inWaiting()==0) and (time.clock()temp.gcode # make temporary file with extra at the beginning +ascii-xfr -sv temp.gcode >/dev/ttyUSB0 # transfer the file +#cp $1 /dev/ttyUSB0 # alternate transfer method commented out. +echo DONE diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/reprap.py b/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/reprap.py new file mode 100644 index 0000000..fdceb4b --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/reprap.py @@ -0,0 +1,467 @@ +""" + pyRepRap is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + pyRepRap is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pyRepRap. If not, see . +""" +""" + This is the main user imported module containing all end user functions +""" + +# add commands to switch to gcode mode to allow any script using this library to write gcode too. + +try: + import serial # Import the pySerial modules. +except: + print('You do not have pySerial installed, which is needed to control the serial port.') + print('Information on pySerial is at:\nhttp://pyserial.wiki.sourceforge.net/pySerial') + +import snap +import time + + +printDebug = False # print debug info + +# SNAP Control Commands - Taken from PIC code # + +# extruder commands # +CMD_VERSION = 0 +CMD_FORWARD = 1 +CMD_REVERSE = 2 +CMD_SETPOS = 3 +CMD_GETPOS = 4 +CMD_SEEK = 5 +CMD_FREE = 6 +CMD_NOTIFY = 7 +CMD_ISEMPTY = 8 +CMD_SETHEAT = 9 +CMD_GETTEMP = 10 +CMD_SETCOOLER = 11 +CMD_PWMPERIOD = 50 +CMD_PRESCALER = 51 +CMD_SETVREF = 52 +CMD_SETTEMPSCALER = 53 +CMD_GETDEBUGINFO = 54 +CMD_GETTEMPINFO = 55 + +# stepper commands # +CMD_VERSION = 0 +CMD_FORWARD = 1 +CMD_REVERSE = 2 +CMD_SETPOS = 3 +CMD_GETPOS = 4 +CMD_SEEK = 5 +CMD_FREE = 6 +CMD_NOTIFY = 7 +CMD_SYNC = 8 +CMD_CALIBRATE = 9 +CMD_GETRANGE = 10 +CMD_DDA = 11 +CMD_FORWARD1 = 12 +CMD_BACKWARD1 = 13 +CMD_SETPOWER = 14 +CMD_GETSENSOR = 15 +CMD_HOMERESET = 16 +CMD_GETMODULETYPE = 255 + +# sync modes # +sync_none = 0 # no sync (default) +sync_seek = 1 # synchronised seeking +sync_inc = 2 # inc motor on each pulse +sync_dec = 3 # dec motor on each pulse + +snap.localAddress = 0 # local address of host PC. This will always be 0. +#global serialPort +#serialPort = False + +def openSerial( port, rate, tout ): + global serialPort + try: + serialPort = serial.Serial( port, rate, timeout = tout ) + return True + except 13: + print "You do not have permissions to use the serial port, try running as root" + +def closeSerial(): + serialPort.close() + +# Convert two 8 bit bytes to one integer +def bytes2int(LSB, MSB): + return int( (0x100 * int(MSB) ) | int(LSB) ) + +# Convert integer to two 8 bit bytes +def int2bytes(val): + MSB = int( ( int(val) & 0xFF00) / 0x100 ) + LSB = int( int(val) & 0xFF ) + return LSB, MSB + +#def loopTest(): +# p = snap.SNAPPacket( serial, snap.localAddress, snap.localAddress, 0, 1, [] ) + +# Scan reprap network for devices (incomplete) - this will be used by autoconfig functions when complete +def scanNetwork(): + devices = [] + for remoteAddress in range(1, 10): # For every address in range. full range will be 255 + print "Trying address " + str(remoteAddress) + p = snap.SNAPPacket( serialPort, remoteAddress, snap.localAddress, 0, 1, [CMD_GETMODULETYPE] ) # Create snap packet requesting module type + #p = snap.SNAPPacket( serialPort, remoteAddress, snap.localAddress, 0, 1, [CMD_VERSION] ) + if p.send(): # Send snap packet, if sent ok then await reply + rep = p.getReply() + if rep: + #devices[ rep.dataBytes[1] ] = remoteAddress + devices.append( { 'address':remoteAddress, 'type':rep.dataBytes[1], 'subType':rep.dataBytes[2] } ) # If device replies then add to device list. + else: + "print na" + else: + print "scan no ack" + time.sleep(0.5) + for d in devices: + #now get versions + print "device", d + +def getNotification(serialPort): + return snap.getPacket(serialPort) + +class extruderClass: + def __init__(self): + self.address = 8 + self.active = False + + def getModuleType(self): #note: do pics not support this yet? I can't see it in code and get no reply from pic + if self.active: + p = snap.SNAPPacket( serialPort, self.address, snap.localAddress, 0, 1, [CMD_GETMODULETYPE] ) # Create SNAP packet requesting module type + if p.send(): + rep = p.getReply() + data = checkReplyPacket( rep, 2, CMD_GETMODULETYPE ) # If packet sent ok and was acknoledged then await reply, otherwise return False + if data: + return data[1] # If valid reply is recieved then return it, otherwise return False + return False + + def getVersion(self): + if self.active: + p = snap.SNAPPacket( serialPort, self.address, snap.localAddress, 0, 1, [CMD_VERSION] ) + if p.send(): + rep = p.getReply() + data = checkReplyPacket( rep, 3, CMD_VERSION ) + if data: + return data[1], data[2] + return False + + def setMotor(self, direction, speed): + if self.active: + p = snap.SNAPPacket( serialPort, self.address, snap.localAddress, 0, 1, [int(direction), int(speed)] ) ##no command being sent, whats going on? + if p.send(): + return True + return False + + def getTemp(self): + if self.active: + p = snap.SNAPPacket( serialPort, self.address, snap.localAddress, 0, 1, [CMD_GETTEMP] ) + if p.send(): + rep = p.getReply() + data = checkReplyPacket( rep, 2, CMD_GETTEMP ) + if data: + return data[1] + return False + + def setVoltateReference(self, val): + if self.active: + p = snap.SNAPPacket( serialPort, self.address, snap.localAddress, 0, 1, [CMD_SETVREF, int(val)] ) + if p.send(): + return True + return False + + def setHeat(self, lowHeat, highHeat, tempTarget, tempMax): + if self.active: + tempTargetMSB, tempTargetLSB = int2bytes( tempTarget ) + tempMaxMSB ,tempMaxLSB = int2bytes( tempMax ) + p = snap.SNAPPacket( serialPort, self.address, snap.localAddress, 0, 1, [CMD_SETHEAT, int(lowHeat), int(highHeat), tempTargetMSB, tempTargetLSB, tempMaxMSB, tempMaxLSB] ) # assumes MSB first (don't know this!) + if p.send(): + return True + return False + + def setCooler(self, speed): + if self.active: + p = snap.SNAPPacket( serialPort, self.address, snap.localAddress, 0, 1, [CMD_SETCOOLER, int(speed)] ) + if p.send(): + return True + return False + + def freeMotor(self): + if self.active: + p = snap.SNAPPacket( serialPort, self.address, snap.localAddress, 0, 1, [CMD_FREE] ) + if p.send(): + return True + return False + +extruder = extruderClass() + + +def checkReplyPacket (packet, numExpectedBytes, command): + if packet: + if len( packet.dataBytes ) == numExpectedBytes: # check correct number of data bytes have been recieved + if packet.dataBytes[0] == command: # check reply is a reply to sent command + return packet.dataBytes + return False + + +class axisClass: + def __init__(self, address): + self.address = address + self.active = False # when scanning network, set this, then in each func below, check alive before doing anything + self.limit = 100000 # limit effectively disabled unless set + #move axis one step forward + def forward1(self): + if self.active: + p = snap.SNAPPacket( serialPort, self.address, snap.localAddress, 0, 1, [CMD_FORWARD1] ) + if p.send(): + return True + return False + + #move axis one step backward + def backward1(self): + if self.active: + p = snap.SNAPPacket( serialPort, self.address, snap.localAddress, 0, 1, [CMD_BACKWARD1] ) + if p.send(): + return True + return False + + #spin axis forward at given speed + def forward(self, speed): + if self.active: + p = snap.SNAPPacket( serialPort, self.address, snap.localAddress, 0, 1, [CMD_FORWARD, int(speed)] ) + if p.send(): + return True + return False + + #spin axis backward at given speed + def backward(self, speed): + if self.active: + p = snap.SNAPPacket( serialPort, self.address, snap.localAddress, 0, 1, [CMD_REVERSE, int(speed)] ) + if p.send(): + return True + return False + + #debug only + def getSensors(self): + if self.active: + p = snap.SNAPPacket( serialPort, self.address, snap.localAddress, 0, 1, [CMD_GETSENSOR] ) + if p.send(): + rep = p.getReply() + data = checkReplyPacket( rep, 3, CMD_GETSENSOR ) # replace this with a proper object in SNAP module? + if data: + print data[1], data[2] + return False + + #get current axis position + def getPos(self): + if self.active: + p = snap.SNAPPacket( serialPort, self.address, snap.localAddress, 0, 1, [CMD_GETPOS] ) + if p.send(): + rep = p.getReply() + data = checkReplyPacket( rep, 3, CMD_GETPOS ) + if data: + pos = bytes2int( data[1], data[2] ) + return pos # return value + return False + + #set current position (set variable not robot position) + def setPos(self, pos): + if self.active: + posMSB ,posLSB = int2bytes( pos ) + p = snap.SNAPPacket( serialPort, self.address, snap.localAddress, 0, 1, [CMD_SETPOS, posMSB, posLSB] ) + if p.send(): + return True + return False + + #power off coils on stepper + def free(self): + if self.active: + p = snap.SNAPPacket( serialPort, self.address, snap.localAddress, 0, 1, [CMD_FREE] ) + if p.send(): + return True + return False + + #seek to axis location. When waitArrival is True, funtion does not return until seek is compete + def seek(self, pos, speed, waitArrival = True): + if self.active and pos <= self.limit: + posMSB ,posLSB = int2bytes( pos ) + p = snap.SNAPPacket( serialPort, self.address, snap.localAddress, 0, 1, [CMD_SEEK, int(speed), posMSB ,posLSB] ) + if p.send(): + if waitArrival: + if printDebug: print " wait notify" + notif = getNotification( serialPort ) + if notif.dataBytes[0] == CMD_SEEK: + if printDebug: print " valid notification for seek" + else: + return False + if printDebug: print " rec notif" + return True + return False + + #goto 0 position. When waitArrival is True, funtion does not return until reset is compete + def homeReset(self, speed, waitArrival = True): + if self.active: + p = snap.SNAPPacket( serialPort, self.address, snap.localAddress, 0, 1, [CMD_HOMERESET, int(speed)] ) + if p.send(): + if waitArrival: + if printDebug: print "reset wait" + notif = getNotification( serialPort ) + if notif.dataBytes[0] == CMD_HOMERESET: + if printDebug: print " valid notification for reset" + else: + return False + if printDebug: print "reset done" + return True + return False + + def setNotify(self): + #global serialPort + if self.active: + p = snap.SNAPPacket( serialPort, self.address, snap.localAddress, 0, 1, [CMD_NOTIFY, snap.localAddress] ) # set notifications to be sent to host + if p.send(): + return True + return False + + def setSync( self, syncMode ): + if self.active: + p = snap.SNAPPacket( serialPort, self.address, snap.localAddress, 0, 1, [CMD_SYNC, int(syncMode)] ) + if p.send(): + return True + return False + + def DDA( self, speed, seekTo, slaveDelta, waitArrival = True): + if self.active and seekTo <= self.limit: + masterPosMSB, masterPosLSB = int2bytes( seekTo ) + slaveDeltaMSB, slaveDeltaLSB = int2bytes( slaveDelta ) + p = snap.SNAPPacket( serialPort, self.address, snap.localAddress, 0, 1, [CMD_DDA, int(speed), masterPosMSB ,masterPosLSB, slaveDeltaMSB, slaveDeltaLSB] ) #start sync + if p.send(): + if waitArrival: + notif = getNotification( serialPort ) + if notif.dataBytes[0] == CMD_DDA: + if printDebug: print " valid notification for DDA" # todo: add actual enforement on wrong notification + else: + return False + return True + return False + + def setPower( self, power ): + if self.active: + p = snap.SNAPPacket( serialPort, self.address, snap.localAddress, 0, 1, [CMD_SETPOWER, int( power * 0.63 )] ) # This is a value from 0 to 63 (6 bits) + if p.send(): + return True + return False + +class syncAxis: + def __init__( self, axis, seekTo, delta, direction ): + self.axis = axis + self.seekTo = seekTo + self.delta = delta + self.direction = direction + + if self.direction > 0: + self.syncMode = sync_inc + else: + self.syncMode = sync_dec + + +class cartesianClass: + def __init__(self): + # initiate axies with addresses + self.x = axisClass(2) + self.y = axisClass(3) + self.z = axisClass(4) + + # goto home position (all axies) + def homeReset(self, speed, waitArrival = True): + if self.x.homeReset( speed, waitArrival ): #setting these to true breaks waitArrival convention. need to rework waitArrival and possibly have each axis storing it's arrival flag and pos as variables? + print "X Reset" + if self.y.homeReset( speed, waitArrival ): + print "Y Reset" + if self.z.homeReset( speed, waitArrival ): + print "Z Reset" + # add a way to collect all three notifications (in whatever order) then check they are all there. this will allow symultanious axis movement and use of waitArrival + + # seek to location (all axies). When waitArrival is True, funtion does not return until all seeks are compete + # seek will automatically use syncSeek when it is required. Always use the seek function + def seek(self, pos, speed, waitArrival = True): + curX, curY, curZ = self.x.getPos(), self.y.getPos(), self.z.getPos() + x, y, z = pos + if x <= self.x.limit and y <= self.y.limit and z <= self.z.limit: + if printDebug: print "seek from [", curX, curY, curZ, "] to [", x, y, z, "]" + if x == curX or y == curY: + if printDebug: print " standard seek" + self.x.seek( x, speed, True ) #setting these to true breaks waitArrival convention. need to rework waitArrival and possibly have each axis storing it's arrival flag and pos as variables? + self.y.seek( y, speed, True ) + else: + if printDebug: print " sync seek" + self.syncSeek( pos, speed, waitArrival ) + if z != curZ: + self.z.seek( z, speed, True ) + else: + print "Trying to print outside of limit, aborting seek" + + # perform syncronised x/y movement. This is called by seek when needed. + def syncSeek(self, pos, speed, waitArrival = True): + curX, curY = self.x.getPos(), self.y.getPos() + newX, newY, nullZ = pos + deltaX = abs( curX - newX ) # calc delta movements + deltaY = abs( curY - newY ) + directionX = ( curX - newX ) / -deltaX # gives direction -1 or 1 + directionY = ( curY - newY ) / -deltaY + if printDebug: print " dx", deltaX, "dy", deltaY, "dirX", directionX, "dirY", directionY + if printDebug: print " using x master" + + master = syncAxis( self.x, newX, deltaX, directionX ) # create two swapable data structures, set x as master, y as slave + slave = syncAxis( self.y, newY, deltaY, directionY ) + + if slave.delta > master.delta: # if y has the greater movement then make y master + slave, master = master, slave + if printDebug: print " switching to y master" + if printDebug: print " masterPos", master.seekTo, "slaveDelta", slave.delta + slave.axis.setSync( slave.syncMode ) + master.axis.DDA( speed, master.seekTo, slave.delta, True ) + time.sleep(0.1) + slave.axis.setSync( sync_none ) + if printDebug: print " sync seek complete" + + # get current position of all three axies + def getPos(self): + return self.x.getPos(), self.y.getPos(), self.z.getPos() + + # stop all motors + def stop(self): + self.x.forward( 0 ) + self.y.forward( 0 ) + self.z.forward( 0 ) + + # free all motors (no current on coils) + def free(self): + self.x.free() + self.y.free() + self.z.free() + def setPower(self, power): + self.x.setPower( power ) + self.y.setPower( power ) + self.z.setPower( power ) + #def lockout(): + #keep sending power down commands to all board every second + + +cartesian = cartesianClass() + +#wait on serial only when after somthing? or do pics send messages without pc request? + + + + + + diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/send.html b/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/send.html new file mode 100644 index 0000000..c150344 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/send.html @@ -0,0 +1,78 @@ +

+send.py is "glue" for sending a skeinforge-generated gcode file to your arduino-based reprap. +

+

+It is a command-line only utility, starting it from your GUI of choice will not be useful. +

+

+ +Syntax is simple: +

+send.py [options] <gcode or file> [<gcode or file>...]
+
+

+

+ +To send your extruder.gcode file to your reprap, type: +

+send.py extruder.gcode
+
+

+

+This will print the extruder.gcode shape, and print comments to the console. +Typically, comments are such things as +(>layerStart< 0.402 ) +which can be very useful to track the progress of your build, so these are "on" by default. +

+

+There are a simple options that may be useful for special circumstances. +These are --quiet, --noreset, --port, and --verbose. +These can also be writted -q, -n, -p, and -v for short. +If you are writing a script (for instance, the M100 scripts for use with EMC) then I'd recommend that you use the long options, so future maintainers don't have to look things up. +

+

+Quiet will suppress all but the most basic messages. It won't supress everything, however. Error messages will still be printed. But it will supress almost everything. +Since options are processed in order on the command line, so if you want to supress messages about processing options, you'll have to make Quiet the first option. +

+

+Normally, the arduino is reset by dropping the DTR line for 1 second. +Since the Arduino takes several seconds to reboot, you will want to disable this behavior when including send.py in scripts. +

+

+The verbose option will cause not just comments, but every command sent to and every response recieved from the arduino to be printed out. Useful for debugging, but it prints a great deal of text in ordinary usage. +

+

+The port option uses reasonable defaults for most operating systems - /dev/ttyUSB0 for posix systems, and COM3 for windows systems. +If you have some other port you'll have to set in manually as "send.py -p COM5 extruder.gcode" or +"send.py --port /dev/ttyUSB5 extruder.gcode" or something. +

+

+Future improvements: +

+

+

+I would like to add support for more g-code contructs on the python side of things. +Stuff like variables, subroutines, etc. +Stuff that the g-code firmware is unlikely to ever implement because of size restrictions. +The current version works, and skeinforge doesn't use these features. +It might be nice for a future "print several objects at once, automatically filling the bed area" interface. +And if I get my extruder built, I might even take the time to do that! +

+RepRapArduinoSerialSender.py +

+

+ +This, like send.py, was cribbed from Brenden Erwin's code for using EMC. It's been modified somewhat for more general use. +There are only five methods: +

+
+__init__
+reset
+write
+read
+close
+
+

+Of these, you should not directly use read(). It's used internally by write and reset to verify that the operation was completed successfully. +

+ diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/send.py b/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/send.py new file mode 100644 index 0000000..c7d7db8 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/send.py @@ -0,0 +1,168 @@ +#!/usr/bin/python2.5 +# encoding: utf-8 +""" +Created by Brendan Erwin on 2008-05-21. +Modified by John Gilmore 2008-08-23 +Copyright (c) 2008 Brendan Erwin. All rights reserved. +Copyright (c) 2008 John Gilmore. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import os +import sys +import getopt +import RepRapArduinoSerialSender + +help_message = ''' +Usage: send [options] [...] + --verbose : Verbose - print ALL communication, not just comments. + -v : prints responses from the arduino, and every command sent. + + --quiet : Quiet - don't print anything, whereas + -q : normally comments are printed. + + --noreset : skip the reset. + -n : causes the arduino to not be deliberately reset. + + --port : Set the port to write to + -p : default is "/dev/ttyUSB0" for posix, "COM3" for windows. + + --baud : Set the baud rate to use + -b : defaults to 19200 + +You may call this with either a single statement of g-code +to be sent to the arduino, or with the name of a g-code file. +------------------------------------------------------------------ +Copyright (C) 2008 Brendan Erwin +Copyright (C) 2008 John Gilmore + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +''' +#This was originally release by Brendan under GPLv2 or later + + + +class Usage(Exception): + def __init__(self, msg): + self.msg = msg + +def main(argv=None): + + # Set resonable defaults for port, verbosity, and reset. + verbose = 1 + baud = 19200 + reset = True + if os.name == "posix": + port = "/dev/ttyUSB0" + elif os.name == "nt": + port = "COM3" + else: + port = "/dev/ttyUSB0" + + if argv is None: + argv = sys.argv + + try: + try: + opts, argv = getopt.getopt(argv[1:], "vqnhb:p:", ["verbose","quiet","noreset","help","baud=","port="]) + except getopt.error, msg: + raise Usage(msg) + + # option processing + for option, value in opts: + if option in ( "-v" , "--verbose" ): + verbose = 2 + print "You have requested that verbosity be set to True" + print "All communication with the arduino will be printed" + elif option in ( "-q" , "--quiet" ): + verbose = 0 + #don't print "quiet mode on" + elif option in ( "-n" , "--noreset" ): + reset = False + if verbose: + print "Arduino will not be reset before sending gcode" + elif option in ( "-p" , "--port" ): + port = value + elif option in ("-h", "--help" ): + raise Usage(help_message) + elif option in ("-b", "--baud" ): + baud = int(value) + + if verbose: + print "Arduino port set to " + port + + except Usage, err: + #print >> sys.stderr, sys.argv[0].split("/")[-1] + ": " + str(err.msg) + print >> sys.stderr, str(err.msg) + print >> sys.stderr, "For help use --help" + return 2 + + + sender = RepRapArduinoSerialSender.RepRapArduinoSerialSender(port, baud, verbose>1) + if reset: + sender.reset() + + for filename in argv: + processfile(filename,sender,verbose) + +def processfile(filename,sender,verbose): + try: + datafile = open(filename) + except IOError: + #Ignore verbosity settings here, as if it's a typo we'll want to know. + line=filename + if line.lstrip().startswith(("G","X","Y","Z","M")): + if verbose: + print "Unable to open file \"" + line + "\", assuming it's one line of direct G-code..." + sender.write(line) + return 0 + else: + print "Unable to open file \"" + line + "\"" + sys.exit(-1) + + try: + for line in datafile: + line=line.rstrip() + # Ignore lines with comments (not technically correct, should ignore only the comment, + # but all gcode files that I've actually seen so far don't have code on comment lines. + if line.lstrip().startswith( ('(', '"' , '\\') ): + if verbose: + print line + continue + + # This is the place to insert G-Code interpretation. + # Subroutines, Variables, all sorts of fun stuff. + # probably by calling a "gcode interpreter" class intead + # of simply "sender". + + sender.write(line) + finally: + datafile.close() + + return 0 + +if __name__ == "__main__": + sys.exit(main()) diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/snap.py b/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/snap.py new file mode 100644 index 0000000..30845d6 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/fabricate/snap.py @@ -0,0 +1,280 @@ +""" + pyRepRap is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + pyRepRap is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with pyRepRap. If not, see . +""" + +try: + import serial # Import the pySerial modules. +except: + print('You do not have pySerial installed, which is needed to control the serial port.') + print('Information on pySerial is at:\nhttp://pyserial.wiki.sourceforge.net/pySerial') + + +offset_payload = 5 +offset_hdb1 = 2 + +#ackTimeout = 0.3 # unused +messageTimeout = 0.3 # used for ack also (possible to split?) +#messageTimeout = 2 # used for ack also (possible to split?) +retries = 3 # number of packet send retries allowed (for whatever failed reason + +printOutgoingPackets = False +printIncomingPackets = False +printFailedPackets = False + +#this is done again in full decode, but needed here so num bytes to expect is known. +def getPacketLen(buffer): + l = breakHDB1( buffer[offset_hdb1] ) + #l = buffer[offset_hdb1] & 0x0f; + #if (l & 8) != 0: + # return 8 << (l & 7) + return l + +#PCaddress = 0 + +#wait for a packet on serial - note : packets addressed to something other than 0 get recieved if you try sending to a non existant pcb (looped round). should we delete or pass on? (they cause errors right now in getpacket) +def getPacket(ser): + buffer = [] + while 1: + byte = ser.read() # read serial byte. + if len(byte) > 0: + buffer.append( ord (byte) ) # add serial byte to buffer. + else: + print "Error: Serial timeout" #clear buffer on timeout? + return False # timeout has occured. + #TODO - add check for sync on first byte + if len(buffer) > 4: # one packet length is recieved (HDB1?). + expectedLength = getPacketLen(buffer) + offset_payload + 1; # read num data bytes. + if len(buffer) >= expectedLength: # check we have enough data, otherwise continue reading from serial. + #print "############PR#############" + p = SNAPPacket( ser, 0, 0, 0, 0, [] ) # create empty packet + for b in buffer: + p.addByte(b) # add byte to packet + p.decode() + if printIncomingPackets: + print "###INCOMING PACKET##" + p.printPacket() + print "###END INCOMING PACKET##" + return p # return recieved packet + #need to check if packet is for pc (0), if not send on. + +#class for checksum calculator +class SNAPChecksum: + def __init__(self): + self.crc = 0 + def addData(self, data): + #byte i = (byte)(data ^ self.crc) + i = data ^ self.crc + self.crc = 0 + if((i & 1) != 0): + self.crc ^= 0x5e + if((i & 2) != 0): + self.crc ^= 0xbc + if((i & 4) != 0): + self.crc ^= 0x61 + if((i & 8) != 0): + self.crc ^= 0xc2 + if((i & 0x10) != 0): + self.crc ^= 0x9d + if((i & 0x20) != 0): + self.crc ^= 0x23 + if((i & 0x40) != 0): + self.crc ^= 0x46 + if((i & 0x80) != 0): + self.crc ^= 0x8c + return data + def getResult(self): + return self.crc + + +#class for snap packet +class SNAPPacket: + def __init__(self, serial, DAB, SAB, ACK, NAK, dataBytes): #specify serial here, not reason not to + self.SYNC = 0x54 + self.DAB = DAB + self.SAB = SAB + self.ACK = ACK + self.NAK = NAK + self.dataBytes = dataBytes + + self.bytes = [] + self.leftoverBytes = [] + self.encoded = False + self.decoded = False + self.valid = False + self.serial = serial + + #manually add a byte to packet (unused) + def addByte(self, byte): + self.bytes.append(byte) + + #convert individual packet properties into table self.bytes (raw data packet) + def encode(self): + self.NDB = len(self.dataBytes) + self.bytes = [] + self.bytes.insert( 0, 0xFF & self.SYNC ) #SYNC + self.bytes.insert( 1, 0xFF & makeHDB2(self.ACK, self.NAK) ) #HDB2 + self.bytes.insert( 2, 0xFF & makeHDB1(self.NDB) ) #HDB1 + self.bytes.insert( 3, 0xFF & self.DAB ) #DAB + self.bytes.insert( 4, 0xFF & self.SAB ) #SAB + + for d in self.dataBytes: + self.bytes.append( 0xFF & d ) #DATA + + checksum = SNAPChecksum() + for d in self.bytes[1:]: + checksum.addData(d) + self.CRC = checksum.getResult() + self.bytes.append( self.CRC ) #CRC + #print self.bytes + self.encoded = True + + #convert table self.bytes (raw data packet) into individual packet properties + def decode(self): + self.SYNC = self.bytes[0] + self.HDB2 = self.bytes[1] + self.HDB1 = self.bytes[2] + self.DAB = self.bytes[3] + self.SAB = self.bytes[4] + self.NDB = breakHDB1(self.HDB1) + + self.dataBytes = [] + for d in self.bytes[5:5 + self.NDB]: + self.dataBytes.append(d) + + #print self.bytes, self.NDB + self.CRC = self.bytes[5 + self.NDB::6 + self.NDB][0] + numLeftoverBytes = len(self.bytes) - 6 - self.NDB + self.leftoverBytes = self.bytes[6 + self.NDB:len(self.bytes)] + if numLeftoverBytes > 0: + print "leftover bytes", numLeftoverBytes, self.leftoverBytes + self.ACK, self.NAK = breakHDB2(self.HDB2) + self.bytes = self.bytes[:6 + self.NDB] + #print "newb", self.bytes + self.decoded = True + + #calculate checksum, compare to value in recieved packet + def check(self): + newChecksum = SNAPChecksum() + for d in self.bytes[1:-1]: + newChecksum.addData(d) + testCRC = newChecksum.getResult() + if testCRC == self.CRC: + self.valid = True + return True + else: + self.valid = False + return False, testCRC, self.CRC + + #actual sending of data packet (self.bytes) + def sendBytes(self): + if self.encoded == True: + for d in self.bytes: + #print "sending", d, chr(d) + self.serial.write(chr(d)) + else: + print "Error: packet not encoded" + + #user send function, sends packet and awaits and checks acknoledgement. + def send(self): + self.encode() + retriesLeft = retries + while retriesLeft > 0: # try sending define number of times only + self.sendBytes() # send data + if printOutgoingPackets: + print "###OUTGOING PACKET##" + self.decode() #remove need for this (tidy up) + self.printPacket() + print "###END OUTGOING PACKET##" + + ack = getPacket(self.serial) # await ack, returns false on timout + if ack: + ack.decode() + if ack.ACK == 1 and ack.SAB == self.DAB: # check that packet is an acknoledgement and that it is from the device we just messaged. + return True + #do some check on ack - TODO + if printFailedPackets: + print "###FAILED OUTGOING PACKET##" + self.decode() #remove need for this (tidy up) + self.printPacket() + print "###END FAILED OUTGOING PACKET##" + else: + print "Error: ACK not recieved" + if printFailedPackets: + print "###FAILED OUTGOING PACKET##" + self.decode() #remove need for this (tidy up) + self.printPacket() + print "###END FAILED OUTGOING PACKET##" + + retriesLeft = retriesLeft - 1 + print "Error: Packet send FAILED (or reply)" + return False + + # get a modules reply packet (not ack) + def getReply(self): + rep = getPacket(self.serial) + return rep + + #print packet info to console + def printPacket(self): + if self.decoded == True: + print self.bytes + print "SNAP Packet:" + if self.SYNC == 0x54: + print "...Sync OK" + else: + print "...Sync Error" + print "...Check: ", self.check() + print "...DATA", self.dataBytes + print "...CRC", self.CRC + print "...SAB", self.SAB + print "...DAB", self.DAB + print "...HDB1", self.HDB1, ":" + print "...........NDB", self.NDB + print "...HDB2", self.HDB2, ":" + print "...........ACK", self.ACK + print "...........NAK", self.NAK + print "END OF PACKET" + else: + print "Error: packet not decoded" + + + +#create HDB2 +def makeHDB2(ACK, NAK): + SAB = 1 # Length of the Source Address Bytes, in Binary. RepRap currently only accepts source addresses of 1 byte length + DAB = 1 # Length of the Destination Address Bytes, in Binary. RepRap currently only accepts destinations of 1 byte length + PFB = 0 # Length of Protocol Flag Bytes. RepRap does not accept any protocol flag bytes, so this must be set to 00 + HDB2val = ((DAB & 0x3) * pow(2,6)) | ((SAB & 0x3) * pow(2,4)) | ((PFB & 0x3) * pow(2,2)) | ((ACK & 0x1) * pow(2,1)) | (NAK & 0x1) + #print "HDB2 = '" + str(HDB2val) + "'" + return HDB2val + +def breakHDB2(HDB2): + ACK = (HDB2 & 0x2) / pow(2,1) + NAK = (HDB2 & 0x1) + return ACK, NAK + +#create HDB1 +def makeHDB1(NDB): + CMD = 0 # Command Mode Bit. Not implemented by RepRap and should be set to 0 + EMD = 0x3 # Currently RepRap only implements 8-bit self.crc. this should be set to 011 + HDB1val = ((CMD & 0x1) * pow(2,7)) | ((EMD & 0x7) * pow(2,4)) | (0xF & NDB) + #print "HDB1 = '" + str(HDB1val) + "'" + return HDB1val + +def breakHDB1(HDB1): + NDB = HDB1 & 0xF + return NDB + + + diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/nophead/enrique.py b/SkeinPyPy/fabmetheus_utilities/miscellaneous/nophead/enrique.py new file mode 100644 index 0000000..69a7a60 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/nophead/enrique.py @@ -0,0 +1,132 @@ +import Image, ImageDraw, ImageChops +from GifImagePlugin import getheader, getdata +from vector3 import Vector3 + +# Get the entire text of a file. +# @param fileName name of the file +# @return entire text of a file. +def getFileText(fileName): + file = open( fileName, 'r') + fileText = file.read() + file.close() + return fileText + +# Get the all the lines of text of a text. +# @param text text +# @return the lines of text of a text +def getTextLines(text): + return text.replace('\r', '\n').split('\n') + +# Get the double value of the word after the first letter. +# @param word string with value starting after the first letter +# @return double value of the word after the first letter +def getDoubleAfterFirstLetter(word): + return float( word[1 :] ) + +# Get the double value of the word after the first occurence of the letter in the split line. +def getDoubleForLetter(letter, splitLine): + return getDoubleAfterFirstLetter( splitLine[ 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 +def getIndexOfStartingWithSecond(letter, splitLine): + for wordIndex in xrange( 1, len(splitLine) ): + word = splitLine[ wordIndex ] + firstLetter = word[0] + if firstLetter == letter: + return wordIndex + return - 1 + +# straightforward delta encoding taken from gifmaker.py +def makedelta(fp, sequence): + """Convert list of image frames to a GIF animation file""" + previous = None + for im in sequence: + if not previous: + # global header + for s in getheader(im) + getdata(im): + fp.write(s) + else: + # delta frame + delta = ImageChops.subtract_modulo(im, previous) + bbox = delta.getbbox() + if not bbox: + bbox = (0,0, 1,1) + # compress difference + for s in getdata(im.crop(bbox), offset = bbox[:2]): + fp.write(s) + previous = im.copy() + fp.write(";") + + + +class g2gif: + def __init__(self,fileName, outfile): + self.last_pos = Vector3() + self.last_pos.z = 999 + self.do_move = 1 + fileText = getFileText(fileName) + textLines = getTextLines(fileText) + self.images = [] + self.image = None + for line in textLines: + self.parseLine(line) + self.images.append(self.image) + # write GIF animation + fp = open(outfile, "wb") + makedelta(fp, self.images) + fp.close() + + + def parseLine(self, line): + splitLine = line.split(' ') + if len(splitLine) < 1: + return 0 + firstWord = splitLine[0] + if firstWord == 'G1': + self.linearMove(splitLine) + if firstWord == 'M101': + self.do_move = 1 + + # Set the feedRate to the gcode split line. + def setFeedRate( self, splitLine ): + indexOfF = getIndexOfStartingWithSecond( "F", splitLine ) + if indexOfF > 0: + self.feedRateMinute = getDoubleAfterFirstLetter( splitLine[indexOfF] ) + + # Set a point to the gcode split line. + def setPointComponent( self, point, splitLine ): + point.x = getDoubleForLetter( "X", splitLine ) + point.y = getDoubleForLetter( "Y", splitLine ) + indexOfZ = getIndexOfStartingWithSecond( "Z", splitLine ) + if indexOfZ > 0: + point.z = getDoubleAfterFirstLetter( splitLine[indexOfZ] ) + + def scale( self, x, y ): + return x * 5 + 150, - y * 5 + 100 + + def linearMove( self, splitLine ): + location = Vector3() + self.setFeedRate(splitLine) + self.setPointComponent( location, splitLine ) + if location.z != self.last_pos.z: + if self.image: + for i in xrange(10): + self.images.append(self.image) + self.image = Image.new('P', (300, 200), 255) + palette = [] + for red in xrange(8): + for green in xrange(8): + for blue in xrange(4): + palette.extend((red * 255 / 7, green * 255 / 7, blue * 255 / 3)) + self.image.putpalette(palette) + self.segment = 0 + else: + if self.do_move: + draw = ImageDraw.Draw(self.image) + draw.line( ( self.scale( self.last_pos.x, self.last_pos.y ), self.scale( location.x, location.y ) ), fill = 192 ) + self.segment = self.segment + 1 + else: + draw = ImageDraw.Draw(self.image) + draw.line( ( self.scale( self.last_pos.x, self.last_pos.y ), self.scale(location.x, location.y ) ), fill = self.segment ) + self.last_pos = location + self.do_move = 0 diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/nophead/gRead.py b/SkeinPyPy/fabmetheus_utilities/miscellaneous/nophead/gRead.py new file mode 100644 index 0000000..abc4862 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/nophead/gRead.py @@ -0,0 +1,103 @@ +from vector3 import Vector3 + +# Get the entire text of a file. +# @param fileName name of the file +# @return entire text of a file. +def getFileText(fileName): + file = open( fileName, 'r') + fileText = file.read() + file.close() + return fileText + +# Get the all the lines of text of a text. +# @param text text +# @return the lines of text of a text +def getTextLines(text): + return text.replace('\r', '\n').split('\n') + +# Get the double value of the word after the first letter. +# @param word string with value starting after the first letter +# @return double value of the word after the first letter +def getDoubleAfterFirstLetter(word): + return float( word[1 :] ) + +# 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 +def getIndexOfStartingWithSecond(letter, splitLine): + for wordIndex in xrange( 1, len(splitLine) ): + word = splitLine[ wordIndex ] + firstLetter = word[0] + if firstLetter == letter: + return wordIndex + return - 1 + + +class gRead: + def __init__(self,fileName, layers,gcodeText = ''): + if gcodeText == '': + gcodeText = getFileText(fileName) + textLines = getTextLines(gcodeText) + self.last_pos = Vector3() + self.layers = layers + self.layer = None + self.thread = None + self.skeinforge = 0 + self.max_z = -9999999999 + for line in textLines: + self.parseLine(line) + self.newLayer() + + def parseLine(self, line): + if line.startswith( "(" ): + if line.startswith( "(" ): + self.newLayer() + return + splitLine = line.split() + if len(splitLine) < 1: + return 0 + firstWord = splitLine[0] + if firstWord == 'G1': + self.linearMove(splitLine) + if firstWord == 'M110': #filament height only sent by skeinforge at the moment + self.skeinforge = 1 + self.newThread() + if firstWord == 'M103': #extruder off + if self.skeinforge: + self.newThread() #end of thread if skeinforge + if firstWord == 'G92': #offset coordinate system + self.newThread() #for RepRap + + # Set a point to the gcode split line. + def setPointComponent( self, point, splitLine ): + indexOfX = getIndexOfStartingWithSecond( "X", splitLine ) + if indexOfX > 0: + point.x = getDoubleAfterFirstLetter( splitLine[indexOfX] ) + indexOfY = getIndexOfStartingWithSecond( "Y", splitLine ) + if indexOfY > 0: + point.y = getDoubleAfterFirstLetter( splitLine[indexOfY] ) + indexOfZ = getIndexOfStartingWithSecond( "Z", splitLine ) + if indexOfZ > 0: + point.z = getDoubleAfterFirstLetter( splitLine[indexOfZ] ) + + def newLayer(self): + self.newThread() + if self.layer: + self.layers.append(self.layer) + self.layer = [] + + def newThread(self): + if self.thread: + self.layer.append(self.thread) + self.thread = [] + + def linearMove( self, splitLine ): + if self.thread != None: + pos = self.last_pos.copy() + self.setPointComponent( pos, splitLine ) + if pos.z > self.max_z: +# self.newLayer() + self.max_z = pos.z + if pos.z < self.last_pos.z: + self.newThread() + if self.skeinforge or pos.z < self.max_z: + self.thread.append(pos) + self.last_pos = pos diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/nophead/hexgrid.py b/SkeinPyPy/fabmetheus_utilities/miscellaneous/nophead/hexgrid.py new file mode 100644 index 0000000..f3eb776 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/nophead/hexgrid.py @@ -0,0 +1,28 @@ +import math + +# root parameters +drillDiameter = 25.4 / 16.0 # 1/16 of an inch +separationMultiplier = 2.5 +safetyMultiplier = 1.0 +bottomLeft = complex( - 10.0, - 10.0 ) +topRight = complex( 10.0, 10.0 ) + +# derived parameters +separation = drillDiameter * separationMultiplier +horizontalSeparation = separation * math.cos( math.radians( 30.0 ) ) +oddRowOffset = separation * math.sin( math.radians( 30.0 ) ) +safetyMargin = complex( separation, separation ) * safetyMultiplier +safeBottomLeft = bottomLeft + safetyMargin +safeTopRight = topRight - safetyMargin + +# generate drill locations +drillLocation = safeBottomLeft * 1.0 +offset = 0.0 +while drillLocation.imag < safeTopRight.imag: + print('') + while drillLocation.real < safeTopRight.real: + print(drillLocation) + drillLocation = complex( drillLocation.real + separation, drillLocation.imag ) + offset = oddRowOffset - offset + drillLocation = complex( safeBottomLeft.real + offset, drillLocation.imag + horizontalSeparation ) +print('') diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/nophead/layers.py b/SkeinPyPy/fabmetheus_utilities/miscellaneous/nophead/layers.py new file mode 100644 index 0000000..c3051e9 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/nophead/layers.py @@ -0,0 +1,72 @@ +from vector3 import Vector3 +import Image, ImageDraw + +def bounding_cube(layers): + min_x = 999999 + min_y = 999999 + min_z = 999999 + max_x = -999999 + max_y = -999999 + max_z = -999999 + for layer in layers: + for thread in layer: + for point in thread: + if point.x > max_x: + max_x = point.x + if point.y > max_y: + max_y = point.y + if point.z > max_z: + max_z = point.z + if point.x < min_x: + min_x = point.x + if point.y < min_y: + min_y = point.y + if point.z < min_z: + min_z = point.z + return Vector3(min_x, min_y, min_z), Vector3(max_x, max_y, max_z) + +def make_images(layers): + palette = [] + for i in xrange(256): + #resistor colour codes + if i == 1: + palette.extend((134, 100, 57)) # brown + elif i == 2: + palette.extend((255, 0, 0)) # red + elif i == 3: + palette.extend((218, 90, 35)) # orange + elif i == 4: + palette.extend((255, 255, 0)) # yellow + elif i == 5: + palette.extend(( 0, 255, 0)) # green + elif i == 6: + palette.extend(( 0, 0, 255)) # blue + elif i == 7: + palette.extend((255, 0, 255)) # purple + else: + palette.extend((i, i, i)) # shades of grey + cube = bounding_cube(layers) + scale = 10 + x0 = int(cube[0].x) - 1 + y0 = int(cube[0].y) - 1 + width = int(round(cube[1].x - x0) + 1) * scale + height = int(round(cube[1].y - y0) + 1) * scale + last_pos = None + images = [] + for layer in layers: + image = Image.new('P', (width, height), 255) + image.putpalette(palette) + draw = ImageDraw.Draw(image) + segment = 0 + for thread in layer: + if last_pos != None: + draw.line(((( last_pos.x - x0) * scale, height - ( last_pos.y - y0) * scale), + ((thread[0].x - x0) * scale, height - (thread[0].y - y0) * scale)), fill = 128) + last_pos = thread[0].copy() + for point in thread[1:]: + draw.line((((last_pos.x - x0) * scale, height - (last_pos.y - y0) * scale), + ( (point.x - x0) * scale, height - (point.y - y0) * scale)), fill = segment % 8) + last_pos = point.copy() + segment = segment + 1 + images.append(image) + return images diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/nophead/preview.py b/SkeinPyPy/fabmetheus_utilities/miscellaneous/nophead/preview.py new file mode 100644 index 0000000..e48a6d4 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/nophead/preview.py @@ -0,0 +1,69 @@ +import sys +try: + import Tkinter +except: + print('You do not have Tkinter, which is needed for the graphical interface.') + print('Information on how to download Tkinter is at:\nwww.tcl.tk/software/tcltk/') +try: + from layers import * + from gRead import * + import ImageTk +except: + print('You do not have the Python Imaging Library, which is needed by preview and gifview to view the gcode.') + print('The Python Imaging Library can be downloaded from:\nwww.pythonware.com/products/pil/') + +class Preview: + def __init__(self, layers): + self.images = make_images(layers) + self.index = 0 + size = self.images[0].size + self.root = Tkinter.Tk() + self.root.title("Gifscene from HydraRaptor") + frame = Tkinter.Frame(self.root) + frame.pack() + self.canvas = Tkinter.Canvas(frame, width = size[0], height = size[1]) + self.canvas.pack() + self.canvas.config(scrollregion=self.canvas.bbox(Tkinter.ALL)) + self.exit_button = Tkinter.Button(frame, text = "Exit", fg = "red", command = frame.quit) + self.exit_button.pack(side=Tkinter.RIGHT) + self.down_button = Tkinter.Button(frame, text = "Down", command = self.down) + self.down_button.pack(side=Tkinter.LEFT) + self.up_button = Tkinter.Button(frame, text = "Up", command = self.up) + self.up_button.pack(side=Tkinter.LEFT) + self.update() + self.root.mainloop() + + def update(self): + # FIXME: Somehow this fails if this is launched using the Preferences, + # but works from the command-line. + self.image = ImageTk.PhotoImage(self.images[self.index]) + self.canvas.create_image(0,0, anchor= Tkinter.NW, image = self.image) + if self.index < len(self.images) - 1: + self.up_button.config(state = Tkinter.NORMAL) + else: + self.up_button.config(state = Tkinter.DISABLED) + if self.index > 0: + self.down_button.config(state = Tkinter.NORMAL) + else: + self.down_button.config(state = Tkinter.DISABLED) + + def up(self): + self.index += 1 + self.update() + + def down(self): + self.index -= 1 + self.update() + + +def viewGif( fileName, gcodeText = ''): + layers = [] + try: + gRead(fileName, layers, gcodeText) + Preview(layers) + except Exception, why: + print('Preview failed: ' + str( why ) ) + + +if __name__ == "__main__": + viewGif(' '.join(sys.argv[1 :])) diff --git a/SkeinPyPy/fabmetheus_utilities/miscellaneous/nophead/vector3.py b/SkeinPyPy/fabmetheus_utilities/miscellaneous/nophead/vector3.py new file mode 100644 index 0000000..9bac3ba --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/miscellaneous/nophead/vector3.py @@ -0,0 +1,489 @@ +""" +Vec3 is a three dimensional vector class. + +Below are examples of Vector3 use. + +>>> from vector3 import Vector3 +>>> origin = Vector3() +>>> origin +0.0, 0.0, 0.0 +>>> pythagoras = Vector3( 3, 4, 0 ) +>>> pythagoras +3.0, 4.0, 0.0 +>>> pythagoras.magnitude() +5.0 +>>> pythagoras.magnitudeSquared() +25 +>>> triplePythagoras = pythagoras * 3.0 +>>> triplePythagoras +9.0, 12.0, 0.0 +>>> plane = pythagoras.dropAxis() +>>> plane +(3+4j) +""" + +from __future__ import absolute_import +try: + import psyco + psyco.full() +except: + pass +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +import math +import operator + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Nophead \nArt of Illusion ' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +class Vector3: + "A three dimensional vector class." + __slots__ = ['x', 'y', 'z'] + + def __init__( self, x = 0.0, y = 0.0, z = 0.0 ): + self.x = x + self.y = y + self.z = z + + def __abs__(self): + "Get the magnitude of the Vector3." + return math.sqrt( self.x * self.x + self.y * self.y + self.z * self.z ) + + magnitude = __abs__ + + def __add__(self, other): + "Get the sum of this Vector3 and other one." + return Vector3( self.x + other.x, self.y + other.y, self.z + other.z ) + + def __copy__(self): + "Get the copy of this Vector3." + return Vector3( self.x, self.y, self.z ) + + __pos__ = __copy__ + + copy = __copy__ + + def __div__(self, other): + "Get a new Vector3 by dividing each component of this one." + return Vector3( self.x / other, self.y / other, self.z / other ) + + def __eq__(self, other): + "Determine whether this vector is identical to other one." + if other == None: + return False + return self.x == other.x and self.y == other.y and self.z == other.z + + def __floordiv__(self, other): + "Get a new Vector3 by floor dividing each component of this one." + return Vector3( self.x // other, self.y // other, self.z // other ) + + def __hash__(self): + "Determine whether this vector is identical to other one." + return self.__repr__().__hash__() + + def __iadd__(self, other): + "Add other Vector3 to this one." + self.x += other.x + self.y += other.y + self.z += other.z + return self + + def __idiv__(self, other): + "Divide each component of this Vector3." + self.x /= other + self.y /= other + self.z /= other + return self + + def __ifloordiv__(self, other): + "Floor divide each component of this Vector3." + self.x //= other + self.y //= other + self.z //= other + return self + + def __imul__(self, other): + "Multiply each component of this Vector3." + self.x *= other + self.y *= other + self.z *= other + return self + + def __isub__(self, other): + "Subtract other Vector3 from this one." + self.x -= other.x + self.y -= other.y + self.z -= other.z + return self + + def __itruediv__(self, other): + "True divide each component of this Vector3." + self.x = operator.truediv( self.x, other ) + self.y = operator.truediv( self.y, other ) + self.z = operator.truediv( self.z, other ) + return self + + def __mul__(self, other): + "Get a new Vector3 by multiplying each component of this one." + return Vector3( self.x * other, self.y * other, self.z * other ) + + def __ne__(self, other): + "Determine whether this vector is not identical to other one." + return not self.__eq__(other) + + def __neg__(self): + return Vector3( - self.x, - self.y, - self.z ) + + def __nonzero__(self): + return self.x != 0 or self.y != 0 or self.z != 0 + + def __repr__(self): + "Get the string representation of this Vector3." + return '%s, %s, %s' % ( self.x, self.y, self.z ) + + def __rdiv__(self, other): + "Get a new Vector3 by dividing each component of this one." + return Vector3( other / self.x, other / self.y, other / self.z ) + + def __rfloordiv__(self, other): + "Get a new Vector3 by floor dividing each component of this one." + return Vector3( other // self.x, other // self.y, other // self.z ) + + def __rmul__(self, other): + "Get a new Vector3 by multiplying each component of this one." + return Vector3( self.x * other, self.y * other, self.z * other ) + + def __rtruediv__(self, other): + "Get a new Vector3 by true dividing each component of this one." + return Vector3( operator.truediv( other , self.x ), operator.truediv( other, self.y ), operator.truediv( other, self.z ) ) + + def __sub__(self, other): + "Get the difference between the Vector3 and other one." + return Vector3( self.x - other.x, self.y - other.y, self.z - other.z ) + + def __truediv__(self, other): + "Get a new Vector3 by true dividing each component of this one." + return Vector3( operator.truediv( self.x, other ), operator.truediv( self.y, other ), operator.truediv( self.z, other ) ) + + def cross(self, other): + "Calculate the cross product of this vector with other one." + return Vector3( self.y * other.z - self.z * other.y, - self.x * other.z + self.z * other.x, self.x * other.y - self.y * other.x ) + + def distance(self, other): + "Get the Euclidean distance between this vector and other one." + return math.sqrt( self.distanceSquared(other) ) + + def distanceSquared(self, other): + "Get the square of the Euclidean distance between this vector and other one." + separationX = self.x - other.x + separationY = self.y - other.y + separationZ = self.z - other.z + return separationX * separationX + separationY * separationY + separationZ * separationZ + + def dot(self, other): + "Calculate the dot product of this vector with other one." + return self.x * other.x + self.y * other.y + self.z * other.z + + def dropAxis( self, which ): + """Get a complex by removing one axis of this one. + + Keyword arguments: + which -- the axis to drop (0=X, 1=Y, 2=Z)""" + if which == 0: + return complex( self.y, self.z ) + if which == 1: + return complex( self.x, self.z ) + if which == 2: + return complex( self.x, self.y ) + + def getNormalized(self, other): + "Get the normalized Vector3." + magnitude = abs(self) + if magnitude == 0.0: + return self.copy() + return self / magnitude + + def magnitudeSquared(self): + "Get the square of the magnitude of the Vector3." + return self.x * self.x + self.y * self.y + self.z * self.z + + def normalize(self): + "Scale each component of this Vector3 so that it has a magnitude of 1. If this Vector3 has a magnitude of 0, this method has no effect." + magnitude = abs(self) + if magnitude != 0.0: + self /= magnitude + + def reflect( self, normal ): + "Reflect the Vector3 across the normal, which is assumed to be normalized." + distance = 2 * ( self.x * normal.x + self.y * normal.y + self.z * normal.z ) + return Vector3( self.x - distance * normal.x, self.y - distance * normal.y, self.z - distance * normal.z ) + + def setToVec3(self, other): + "Set this Vector3 to be identical to other one." + self.x = other.x + self.y = other.y + self.z = other.z + + def setToXYZ( self, x, y, z ): + "Set the x, y, and z components of this Vector3." + self.x = x + self.y = y + self.z = z + +""" +class Vector3: + __slots__ = ['x', 'y', 'z'] + + def __init__(self, x, y, z): + self.x = x + self.y = y + self.z = z + + def __copy__(self): + return self.__class__(self.x, self.y, self.z) + + copy = __copy__ + + def __repr__(self): + return 'Vector3(%.2f, %.2f, %.2f)' % (self.x, + self.y, + self.z) + + def __eq__(self, other): + if isinstance(other, Vector3): + return self.x == other.x and \ + self.y == other.y and \ + self.z == other.z + else: + assert hasattr(other, '__len__') and len(other) == 3 + return self.x == other[0] and \ + self.y == other[1] and \ + self.z == other[2] + + def __ne__(self, other): + return not self.__eq__(other) + + def __nonzero__(self): + return self.x != 0 or self.y != 0 or self.z != 0 + + def __len__(self): + return 3 + + def __getitem__(self, key): + return (self.x, self.y, self.z)[key] + + def __setitem__(self, key, value): + l = [self.x, self.y, self.z] + l[key] = value + self.x, self.y, self.z = l + + def __iter__(self): + return iter((self.x, self.y, self.z)) + + def __getattr__(self, name): + try: + return tuple([(self.x, self.y, self.z)['xyz'.index(c)] \ + for c in name]) + except ValueError: + raise AttributeError, name + + if _enable_swizzle_set: + # This has detrimental performance on ordinary setattr as well + # if enabled + def __setattr__(self, name, value): + if len(name) == 1: + object.__setattr__(self, name, value) + else: + try: + l = [self.x, self.y, self.z] + for c, v in map(None, name, value): + l['xyz'.index(c)] = v + self.x, self.y, self.z = l + except ValueError: + raise AttributeError, name + + + def __add__(self, other): + if isinstance(other, Vector3): + # Vector + Vector -> Vector + # Vector + Point -> Point + # Point + Point -> Vector + if self.__class__ is other.__class__: + _class = Vector3 + else: + _class = Point3 + return _class(self.x + other.x, + self.y + other.y, + self.z + other.z) + else: + assert hasattr(other, '__len__') and len(other) == 3 + return Vector3(self.x + other[0], + self.y + other[1], + self.z + other[2]) + __radd__ = __add__ + + def __iadd__(self, other): + if isinstance(other, Vector3): + self.x += other.x + self.y += other.y + self.z += other.z + else: + self.x += other[0] + self.y += other[1] + self.z += other[2] + return self + + def __sub__(self, other): + if isinstance(other, Vector3): + # Vector - Vector -> Vector + # Vector - Point -> Point + # Point - Point -> Vector + if self.__class__ is other.__class__: + _class = Vector3 + else: + _class = Point3 + return Vector3(self.x - other.x, + self.y - other.y, + self.z - other.z) + else: + assert hasattr(other, '__len__') and len(other) == 3 + return Vector3(self.x - other[0], + self.y - other[1], + self.z - other[2]) + + + def __rsub__(self, other): + if isinstance(other, Vector3): + return Vector3(other.x - self.x, + other.y - self.y, + other.z - self.z) + else: + assert hasattr(other, '__len__') and len(other) == 3 + return Vector3(other.x - self[0], + other.y - self[1], + other.z - self[2]) + + def __mul__(self, other): + if isinstance(other, Vector3): + # TODO component-wise mul/div in-place and on Vector2; docs. + if self.__class__ is Point3 or other.__class__ is Point3: + _class = Point3 + else: + _class = Vector3 + return _class(self.x * other.x, + self.y * other.y, + self.z * other.z) + else: + assert type(other) in (int, long, float) + return Vector3(self.x * other, + self.y * other, + self.z * other) + + __rmul__ = __mul__ + + def __imul__(self, other): + assert type(other) in (int, long, float) + self.x *= other + self.y *= other + self.z *= other + return self + + def __div__(self, other): + assert type(other) in (int, long, float) + return Vector3(operator.div(self.x, other), + operator.div(self.y, other), + operator.div(self.z, other)) + + + def __rdiv__(self, other): + assert type(other) in (int, long, float) + return Vector3(operator.div(other, self.x), + operator.div(other, self.y), + operator.div(other, self.z)) + + def __floordiv__(self, other): + assert type(other) in (int, long, float) + return Vector3(operator.floordiv(self.x, other), + operator.floordiv(self.y, other), + operator.floordiv(self.z, other)) + + + def __rfloordiv__(self, other): + assert type(other) in (int, long, float) + return Vector3(operator.floordiv(other, self.x), + operator.floordiv(other, self.y), + operator.floordiv(other, self.z)) + + def __truediv__(self, other): + assert type(other) in (int, long, float) + return Vector3(operator.truediv(self.x, other), + operator.truediv(self.y, other), + operator.truediv(self.z, other)) + + + def __rtruediv__(self, other): + assert type(other) in (int, long, float) + return Vector3(operator.truediv(other, self.x), + operator.truediv(other, self.y), + operator.truediv(other, self.z)) + + def __neg__(self): + return Vector3(-self.x, + -self.y, + -self.z) + + __pos__ = __copy__ + + def __abs__(self): + return math.sqrt(self.x ** 2 + \ + self.y ** 2 + \ + self.z ** 2) + + magnitude = __abs__ + + def magnitude_squared(self): + return self.x ** 2 + \ + self.y ** 2 + \ + self.z ** 2 + + def normalize(self): + d = self.magnitude() + if d: + self.x /= d + self.y /= d + self.z /= d + return self + + def normalized(self): + d = self.magnitude() + if d: + return Vector3(self.x / d, + self.y / d, + self.z / d) + return self.copy() + + def dot(self, other): + assert isinstance(other, Vector3) + return self.x * other.x + \ + self.y * other.y + \ + self.z * other.z + + def cross(self, other): + assert isinstance(other, Vector3) + return Vector3(self.y * other.z - self.z * other.y, + -self.x * other.z + self.z * other.x, + self.x * other.y - self.y * other.x) + + def reflect(self, normal): + # assume normal is normalized + assert isinstance(normal, Vector3) + d = 2 * (self.x * normal.x + self.y * normal.y + self.z * normal.z) + return Vector3(self.x - d * normal.x, + self.y - d * normal.y, + self.z - d * normal.z) +""" diff --git a/SkeinPyPy/fabmetheus_utilities/settings.py b/SkeinPyPy/fabmetheus_utilities/settings.py new file mode 100644 index 0000000..0351270 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/settings.py @@ -0,0 +1,2046 @@ +""" +Settings is a collection of utilities to display, read & write the settings and position widgets. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +import cStringIO +import math +import os +import shutil +import sys +import traceback +import webbrowser +try: + import Tkinter +except: + print('You do not have Tkinter, which is needed for the graphical interface, you will only be able to use the command line.') + print('Information on how to download Tkinter is at:\nwww.tcl.tk/software/tcltk/') + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = "$Date: 2008/23/04 $" +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalRepositoryDialogListTable = {} +globalProfileSaveListenerListTable = {} +globalCloseListTables = [globalRepositoryDialogListTable, globalProfileSaveListenerListTable] +globalSettingReplacements = { + 'Perimeter Width over Thickness (ratio):' : 'Edge Width over Height (ratio):', + 'Layer Thickness (mm):' : 'Layer Height (mm):', + 'Location Arrival X (mm):' : 'Arrival X (mm):', + 'Location Arrival Y (mm):' : 'Arrival Y (mm):', + 'Location Arrival Z (mm):' : 'Arrival Z (mm):', + 'Location Departure X (mm):' : 'Departure X (mm):', + 'Location Departure Y (mm):' : 'Departure Y (mm):', + 'Location Departure Z (mm):' : 'Departure Z (mm):', + 'Location Wipe X (mm):' : 'Wipe X (mm):', + 'Location Wipe Y (mm):' : 'Wipe Y (mm):', + 'Location Wipe Z (mm):' : 'Wipe Z (mm):' + } +globalSpreadsheetSeparator = '\t' +globalTemporaryOverrides = {} + + +def addAcceleratorCommand( acceleratorBinding, commandFunction, master, menu, text ): + "Add accelerator command." + acceleratorText = acceleratorBinding[1 : -1] + lastIndexOfMinus = acceleratorText.rfind('-') + if lastIndexOfMinus > - 1: + acceleratorText = acceleratorText[ : lastIndexOfMinus + 1 ] + acceleratorText[ lastIndexOfMinus + 1 : ].capitalize() + acceleratorText = acceleratorText.replace('KeyPress-', '') + acceleratorText = acceleratorText.replace('-', '+') + acceleratorText = acceleratorText.replace('Control', 'Ctrl') + acceleratorBinding = acceleratorBinding.replace('KeyPress', '') + menu.add_command( accelerator = acceleratorText, label = text, underline = 0, command = commandFunction ) + master.bind( acceleratorBinding, commandFunction ) + +def addEmptyRow( gridPosition ): + "Add an empty row." + gridPosition.increment() + Tkinter.Label( gridPosition.master ).grid( row = gridPosition.row, column = gridPosition.column ) + +def addListsToRepository(fileNameHelp, repository): + 'Add the value to the lists.' + addListsToRepositoryByFunction(fileNameHelp, None, repository) + +def addListsToRepositoryByFunction(fileNameHelp, getProfileDirectory, repository): + 'Add the value to the lists.' + repository.displayEntities = [] + repository.executeTitle = None + repository.fileNameHelp = fileNameHelp + repository.fileNameInput = None + repository.lowerName = fileNameHelp.split('.')[-2] + repository.baseName = repository.lowerName + '.csv' + repository.baseNameSynonym = None + repository.baseNameSynonymDictionary = None + repository.capitalizedName = getEachWordCapitalized( repository.lowerName ) + repository.getProfileDirectory = getProfileDirectory + repository.openLocalHelpPage = HelpPage().getOpenFromDocumentationSubName( repository.fileNameHelp ) + repository.openWikiManualHelpPage = None + repository.preferences = [] + repository.repositoryDialog = None + repository.saveListenerTable = {} + repository.title = repository.capitalizedName + ' Settings' + repository.menuEntities = [] + repository.saveCloseTitle = 'Save and Close' + repository.windowPosition = WindowPosition().getFromValue( repository, '0+0') + for setting in repository.preferences: + setting.repository = repository + +def addMenuEntitiesToMenu( menu, menuEntities ): + "Add the menu entities to the menu." + for menuEntity in menuEntities: + menuEntity.addToMenu( menu ) + +def addMenuEntitiesToMenuFrameable( menu, menuEntities ): + "Add the menu entities to the menu." + for menuEntity in menuEntities: + menuEntity.addToMenuFrameable( menu ) + +def addPluginsParentToMenu( directoryPath, menu, parentPath, pluginFileNames ): + "Add plugins and the parent to the menu." + ToolDialog().addPluginToMenu( menu, parentPath[ : parentPath.rfind('.') ] ) + menu.add_separator() + addPluginsToMenu( directoryPath, menu, pluginFileNames ) + +def addPluginsToMenu( directoryPath, menu, pluginFileNames ): + "Add plugins to the menu." + for pluginFileName in pluginFileNames: + ToolDialog().addPluginToMenu( menu, os.path.join( directoryPath, pluginFileName ) ) + +def cancelRepository(repository): + "Read the repository then set all the entities to the read repository values." + getReadRepository(repository) + for setting in repository.displayEntities: + if setting in repository.preferences: + setting.setStateToValue() + +def deleteDirectory( directory, subfolderName ): + "Delete the directory if it exists." + subDirectory = os.path.join( directory, subfolderName ) + if os.path.isdir( subDirectory ): + shutil.rmtree( subDirectory ) + +def deleteMenuItems( menu ): + "Delete the menu items." + try: + lastMenuIndex = menu.index( Tkinter.END ) + if lastMenuIndex != None: + menu.delete( 0, lastMenuIndex ) + except: + print('this should never happen, the lastMenuIndex in deleteMenuItems in settings could not be determined.') + +def getAlongWayHexadecimalColor( beginBrightness, colorWidth, difference, endColorTuple, wayLength ): + "Get a color along the way from begin brightness to the end color." + alongWay = 1.0 + if wayLength != 0.0: + alongWay = 0.4 + 0.6 * min( 1.0, abs( float( difference ) / float( wayLength ) ) ) + hexadecimalColor = '#' + oneMinusAlongWay = 1.0 - alongWay + for primaryIndex in xrange(3): + hexadecimalColor += getAlongWayHexadecimalPrimary( beginBrightness, oneMinusAlongWay, colorWidth, endColorTuple[ primaryIndex ], alongWay ) + return hexadecimalColor + +def getAlongWayHexadecimalPrimary( beginBrightness, beginRatio, colorWidth, endBrightness, endRatio ): + "Get a primary color along the way from grey to the end color." + brightness = beginRatio * float( beginBrightness ) + endRatio * float( endBrightness ) + return getWidthHex( int( round( brightness ) ), colorWidth ) + +def getAlterationFile(fileName): + "Get the file from the fileName or the lowercase fileName in the alterations directories." + settingsAlterationsDirectory = archive.getSettingsPath('alterations') + archive.makeDirectory(settingsAlterationsDirectory) + fileInSettingsAlterationsDirectory = getFileInGivenDirectory(settingsAlterationsDirectory, fileName) + if fileInSettingsAlterationsDirectory != '': + return fileInSettingsAlterationsDirectory + alterationsDirectory = archive.getSkeinforgePath('alterations') + return getFileInGivenDirectory(alterationsDirectory, fileName) + +def getAlterationFileLine(fileName): + "Get the alteration file line from the fileName." + lines = getAlterationLines(fileName) + if len(lines) == 0: + return [] + return getAlterationFileLineBlindly(fileName) + +def getAlterationFileLineBlindly(fileName): + "Get the alteration file line from the fileName." + return '() %s ()' % fileName + +def getAlterationFileLines(fileName): + 'Get the alteration file line and the text lines from the fileName in the alterations directories.' + lines = getAlterationLines(fileName) + if len(lines) == 0: + return [] + return [getAlterationFileLineBlindly(fileName)] + lines + +def getAlterationLines(fileName): + "Get the text lines from the fileName in the alterations directories." + return archive.getTextLines(getAlterationFile(fileName)) + +def getDisplayedDialogFromConstructor(repository): + "Display the repository dialog." + try: + getReadRepository(repository) + return RepositoryDialog( repository, Tkinter.Tk() ) + except: + print('this should never happen, getDisplayedDialogFromConstructor in settings could not open') + print(repository) + traceback.print_exc(file=sys.stdout) + return None + +def getDisplayedDialogFromPath(path): + "Display the repository dialog." + pluginModule = archive.getModuleWithPath(path) + if pluginModule == None: + return None + return getDisplayedDialogFromConstructor( pluginModule.getNewRepository() ) + +def getDisplayToolButtonsRepository( directoryPath, importantFileNames, names, repository ): + "Get the display tool buttons." + displayToolButtons = [] + for name in names: + displayToolButton = DisplayToolButton().getFromPath( name in importantFileNames, name, os.path.join( directoryPath, name ), repository ) + displayToolButtons.append( displayToolButton ) + return displayToolButtons + +def getEachWordCapitalized( name ): + "Get the capitalized name." + withSpaces = name.lower().replace('_', ' ') + words = withSpaces.split(' ') + capitalizedStrings = [] + for word in words: + capitalizedStrings.append( word.capitalize() ) + return ' '.join( capitalizedStrings ) + +def getFileInGivenDirectory( directory, fileName ): + "Get the file from the fileName or the lowercase fileName in the given directory." + directoryListing = os.listdir(directory) + lowerFileName = fileName.lower() + for directoryFile in directoryListing: + if directoryFile.lower() == lowerFileName: + return getFileTextGivenDirectoryFileName( directory, directoryFile ) + return '' + +def getFileTextGivenDirectoryFileName( directory, fileName ): + "Get the entire text of a file with the given file name in the given directory." + absoluteFilePath = os.path.join( directory, fileName ) + return archive.getFileText( absoluteFilePath ) + +def getFolders(directory): + "Get the folder list in a directory." + archive.makeDirectory(directory) + directoryListing = [] + try: + directoryListing = os.listdir(directory) + except OSError: + print('Skeinforge can not list the directory:') + print(directory) + print('so give it read/write permission for that directory.') + folders = [] + for fileName in directoryListing: + if os.path.isdir( os.path.join( directory, fileName ) ): + folders.append(fileName) + return folders + +def getGlobalRepositoryDialogValues(): + "Get the global repository dialog values." + global globalRepositoryDialogListTable + return euclidean.getListTableElements(globalRepositoryDialogListTable) + +def getPathInFabmetheusFromFileNameHelp( fileNameHelp ): + "Get the directory path from file name help." + fabmetheusPath = archive.getFabmetheusPath() + splitFileNameHelps = fileNameHelp.split('.') + splitFileNameDirectoryNames = splitFileNameHelps[ : - 1 ] + for splitFileNameDirectoryName in splitFileNameDirectoryNames: + fabmetheusPath = os.path.join( fabmetheusPath, splitFileNameDirectoryName ) + return fabmetheusPath + +def getProfileBaseName(repository): + "Get the profile base file name." + return getProfileName(repository.baseName, repository) + +def getProfilesDirectoryInAboveDirectory(subName=''): + "Get the profiles directory path in the above directory." + aboveProfilesDirectory = archive.getSkeinforgePath('profiles') + if subName == '': + return aboveProfilesDirectory + return os.path.join( aboveProfilesDirectory, subName ) + +def getProfileName(name, repository): + "Get the name, joined with the profile directory if there is one." + if repository.getProfileDirectory == None: + return name + return os.path.join(repository.getProfileDirectory(), name) + +def getRadioPluginsAddPluginFrame( directoryPath, importantFileNames, names, repository ): + "Get the radio plugins and add the plugin frame." + repository.pluginFrame = PluginFrame() + radioPlugins = [] + for name in names: + radioPlugin = RadioPlugin().getFromRadio( name in importantFileNames, repository.pluginFrame.latentStringVar, name, repository, name == importantFileNames[0] ) + radioPlugin.updateFunction = repository.pluginFrame.update + radioPlugins.append( radioPlugin ) + defaultRadioButton = getSelectedRadioPlugin( importantFileNames + [ radioPlugins[0].name ], radioPlugins ) + repository.pluginFrame.getFromPath( defaultRadioButton, directoryPath, repository ) + return radioPlugins + +def getReadRepository(repository): + "Read and return settings from a file." + text = archive.getFileText(archive.getProfilesPath(getProfileBaseName(repository)), False) + if text == '': + if repository.baseNameSynonym != None: + text = archive.getFileText(archive.getProfilesPath(getProfileName(repository.baseNameSynonym, repository)), False) + if text == '': + print('The default %s will be written in the .skeinforge_pypy folder in the home directory.' % repository.title.lower() ) + text = archive.getFileText(getProfilesDirectoryInAboveDirectory(getProfileBaseName(repository)), False) + if text != '': + readSettingsFromText(repository, text) + writeSettings(repository) + temporaryApplyOverrides(repository) + return repository + readSettingsFromText(repository, text) + temporaryApplyOverrides(repository) + return repository + +def getRepositoryText(repository): + "Get the text representation of the repository." + repositoryWriter = getRepositoryWriter(repository.title.lower()) + for setting in repository.preferences: + setting.writeToRepositoryWriter(repositoryWriter) + return repositoryWriter.getvalue() + +def getRepositoryWriter(title): + "Get the repository writer for the title." + repositoryWriter = cStringIO.StringIO() + repositoryWriter.write('Format is tab separated %s.\n' % title) + repositoryWriter.write('_Name %sValue\n' % globalSpreadsheetSeparator) + return repositoryWriter + +def getSelectedPluginModuleFromPath(filePath, plugins): + "Get the selected plugin module." + for plugin in plugins: + if plugin.value: + return gcodec.getModuleFromPath(plugin.name, filePath) + return None + +def getSelectedPluginName( plugins ): + "Get the selected plugin name." + for plugin in plugins: + if plugin.value: + return plugin.name + return '' + +def getSelectedRadioPlugin( names, radioPlugins ): + "Get the selected radio button if it exists, None otherwise." + for radioPlugin in radioPlugins: + if radioPlugin.value: + return radioPlugin + for name in names: + for radioPlugin in radioPlugins: + if radioPlugin.name == name: + radioPlugin.value = True + return radioPlugin + print('this should never happen, no getSelectedRadioPlugin in settings') + print(names) + return radioPlugin[0] + +def getShortestUniqueSettingName(settingName, settings): + "Get the shortest unique name in the settings." + for length in xrange(3, len(settingName)): + numberOfEquals = 0 + shortName = settingName[: length] + for setting in settings: + if setting.name[: length] == shortName: + numberOfEquals += 1 + if numberOfEquals < 2: + return shortName.lower() + return settingName.lower() + +def getSubfolderWithBasename( basename, directory ): + "Get the subfolder in the directory with the basename." + archive.makeDirectory(directory) + directoryListing = os.listdir(directory) + for fileName in directoryListing: + joinedFileName = os.path.join( directory, fileName ) + if os.path.isdir(joinedFileName): + if basename == fileName: + return joinedFileName + return None + +def getTitleFromName( title ): + "Get the title of this setting." + if title[-1] == ':': + title = title[ : - 1 ] + spaceBracketIndex = title.find(' (') + if spaceBracketIndex > - 1: + return title[ : spaceBracketIndex ] + return title + +def getUntilFirstBracket(text): + 'Get the text until the first bracket, if any.' + dotIndex = text.find('(') + if dotIndex < 0: + return text + return text[: dotIndex] + +def getWidthHex( number, width ): + "Get the first width hexadecimal digits." + return ('0000%s' % hex(number)[ 2 : ] )[ - width : ] + +def liftRepositoryDialogs( repositoryDialogs ): + "Lift the repository dialogs." + for repositoryDialog in repositoryDialogs: + repositoryDialog.root.withdraw() # the withdraw & deiconify trick is here because lift does not work properly on my linux computer + repositoryDialog.root.lift() # probably not necessary, here in case the withdraw & deiconify trick does not work on some other computer + repositoryDialog.root.deiconify() + repositoryDialog.root.lift() # probably not necessary, here in case the withdraw & deiconify trick does not work on some other computer + repositoryDialog.root.update_idletasks() + +def openSVGPage( fileName, svgViewer ): + "Open svg page with an svg program." + if svgViewer == '': + return + if svgViewer == 'webbrowser': + openWebPage(fileName) + return + filePath = '"' + os.path.normpath(fileName) + '"' # " to send in file name with spaces + shellCommand = svgViewer + ' ' + filePath + commandResult = os.system(shellCommand) + if commandResult != 0: + print('It may be that the system could not find the %s program.' % svgViewer ) + print('If so, try installing the %s program or look for another svg viewer, like Netscape which can be found at:' % svgViewer ) + print('http://www.netscape.org/') + print('') + +def openWebPage( webPagePath ): + "Open a web page in a browser." + if webPagePath.find('#') != - 1: # to get around # encode bug + redirectionText = '\n\n\n' + redirectionText += '\n\n' % webPagePath + webPagePath = archive.getDocumentationPath('redirect.html') + archive.writeFileText( webPagePath, redirectionText ) + webPagePath = '"%s"' % webPagePath # " to get around space in url bug + try: # " to get around using gnome-open or internet explorer for webbrowser default + webbrowserController = webbrowser.get('firefox') + except: + webbrowserController = webbrowser.get() + webbrowserName = webbrowserController.name + if webbrowserName == '': + try: + os.startfile( webPagePath )#this is available on some python environments, but not all + return + except: + pass + print('Skeinforge was not able to open the file in a web browser. To see the documentation, open the following file in a web browser:') + print(webPagePath) + return + else: + os.system(webbrowserName + ' ' + webPagePath)#used this instead of webbrowser.open() to workaround webbrowser open() bug + +def printProgress(layerIndex, procedureName): + "Print layerIndex followed by a carriage return." + printProgressByString('%s layer count %s...' % (procedureName.capitalize(), layerIndex + 1)) + +def printProgressByNumber(layerIndex, numberOfLayers, procedureName): + "Print layerIndex and numberOfLayers followed by a carriage return." + printProgressByString('%s layer count %s of %s...' % (procedureName.capitalize(), layerIndex + 1, numberOfLayers)) + +def printProgressByString(progressString): + "Print progress string." + sys.stdout.write(progressString) + sys.stdout.write(chr(27) + '\r') + sys.stdout.flush() + +def quitWindow(root): + "Quit a window." + try: + root.destroy() + except: + pass + +def quitWindows( event=None ): + "Quit all windows." + global globalRepositoryDialogListTable + globalRepositoryDialogValues = euclidean.getListTableElements( globalRepositoryDialogListTable ) + for globalRepositoryDialogValue in globalRepositoryDialogValues: + quitWindow(globalRepositoryDialogValue.root) + +def readSettingsFromText(repository, text): + "Read settings from a text." + text = text.replace(('\nName %sValue\n' % globalSpreadsheetSeparator), ('\n_Name %sValue\n' % globalSpreadsheetSeparator)) + lines = archive.getTextLines(text) + shortDictionary = {} + for setting in repository.preferences: + shortDictionary[getShortestUniqueSettingName(setting.name, repository.preferences)] = setting + if repository.baseNameSynonymDictionary != None: + synonymDictionaryCopy = repository.baseNameSynonymDictionary.copy() + for line in lines: + splitLine = line.split(globalSpreadsheetSeparator) + if len(splitLine) > 1: + if splitLine[0] in synonymDictionaryCopy: + del synonymDictionaryCopy[splitLine[0]] + for synonymDictionaryCopyKey in synonymDictionaryCopy.keys(): + text = archive.getFileText(archive.getProfilesPath(getProfileName(synonymDictionaryCopy[synonymDictionaryCopyKey], repository)), False) + synonymLines = archive.getTextLines(text) + for synonymLine in synonymLines: + splitLine = synonymLine.split(globalSpreadsheetSeparator) + if len(splitLine) > 1: + if splitLine[0] == synonymDictionaryCopyKey: + lines.append(synonymLine) + for lineIndex in xrange(len(lines)): + setRepositoryToLine(lineIndex, lines, shortDictionary) + +def saveAll(): + "Save all the dialogs." + for globalRepositoryDialogValue in getGlobalRepositoryDialogValues(): + globalRepositoryDialogValue.save() + +def saveRepository(repository): + "Set the entities to the dialog then write them." + for setting in repository.preferences: + setting.setToDisplay() + writeSettingsPrintMessage(repository) + for saveListener in repository.saveListenerTable.values(): + saveListener() + +def setButtonFontWeightString( button, isBold ): + "Set button font weight given isBold." + try: + weightString = 'normal' + if isBold: + weightString = 'bold' + splitFont = button['font'].split() + button['font'] = ( splitFont[0], splitFont[1], weightString ) + except: + pass + +def setEntryText(entry, value): + "Set the entry text." + if entry == None: + return + entry.delete(0, Tkinter.END) + entry.insert(0, str(value)) + +def setIntegerValueToString( integerSetting, valueString ): + "Set the integer to the string." + dotIndex = valueString.find('.') + if dotIndex > - 1: + valueString = valueString[: dotIndex] + try: + integerSetting.value = int( valueString ) + return + except: + print('Warning, can not read integer ' + integerSetting.name + ' ' + valueString ) + print('Will try reading as a boolean, which might be a mistake.') + integerSetting.value = 0 + if valueString.lower() == 'true': + integerSetting.value = 1 + +def setRepositoryToLine(lineIndex, lines, shortDictionary): + "Set setting dictionary to a setting line.globalSettingReplacements" + line = lines[lineIndex] + splitLine = line.split(globalSpreadsheetSeparator) + if len(splitLine) < 2: + return + fileSettingName = splitLine[0] + if fileSettingName in globalSettingReplacements: + fileSettingName = globalSettingReplacements[fileSettingName] + shortDictionaryKeys = shortDictionary.keys() + shortDictionaryKeys.sort(key=len, reverse=True) # so that a short word like fill is not overidden by a longer word like fillet + for shortDictionaryKey in shortDictionaryKeys: + if fileSettingName[: len(shortDictionaryKey)].lower() == shortDictionaryKey: + shortDictionary[shortDictionaryKey].setValueToSplitLine(lineIndex, lines, splitLine) + return + +def setSpinColor( setting ): + "Set the spin box color to the value, yellow if it is lower than the default and blue if it is higher." + if setting.entry == None: + return + if setting.backgroundColor == None: + setting.backgroundColor = setting.entry['background'] + if setting.backgroundColor[0] != '#': + setting.backgroundColor = '#ffffff' + setting.colorWidth = len( setting.backgroundColor ) / 3 + setting.grey = int( setting.backgroundColor[ 1 : 1 + setting.colorWidth ], 16 ) + setting.white = int('f' * setting.colorWidth, 16 ) + if abs( setting.value - setting.defaultValue ) <= 0.75 * setting.increment: + setting.entry['background'] = setting.backgroundColor + return + difference = setting.value - setting.defaultValue + if difference > 0.0: + wayLength = setting.to - setting.defaultValue + setting.entry['background'] = getAlongWayHexadecimalColor( setting.grey, setting.colorWidth, difference, ( 0, setting.white, setting.white ), wayLength ) + return + wayLength = setting.from_ - setting.defaultValue + setting.entry['background'] = getAlongWayHexadecimalColor( setting.grey, setting.colorWidth, difference, ( setting.white, setting.white, 0 ), wayLength ) + +def startMainLoopFromConstructor(repository): + "Display the repository dialog and start the main loop." + try: + import Tkinter + except: + return + displayedDialogFromConstructor = getDisplayedDialogFromConstructor(repository) + if displayedDialogFromConstructor == None: + print('Warning, displayedDialogFromConstructor in settings is none, so the window will not be displayed.') + else: + displayedDialogFromConstructor.root.mainloop() + +def startMainLoopFromWindow(window): + 'Display the tableau window and start the main loop.' + if window == None: + return + if window.root == None: + print('Warning, window.root in startMainLoopFromWindow in settings is none, so the window will not be displayed.') + return + window.root.mainloop() + +def temporaryAddPreferenceOverride(module, name, value): + global globalTemporaryOverrides + if not module in globalTemporaryOverrides: + globalTemporaryOverrides[module] = {} + globalTemporaryOverrides[module][name] = value + print('OVERRIDE %s %s %s' % (module,name,value)) + print(globalTemporaryOverrides[module]) + +def temporaryApplyOverrides(repository): + 'Apply any overrides that have been set at the command line.' + # The override dictionary is a mapping of repository names to + # key-value mappings. + global globalTemporaryOverrides + if repository.baseName in globalTemporaryOverrides: + settingTable = {} + for setting in repository.preferences: + settingTable[ setting.name ] = setting + for (name, value) in overrides[repository.baseName].items(): + if name in settingTable: + settingTable[name].setValueToString(value) + else: + print('Override not applied for: %s, %s' % (name,value)) + +def writeSettings(repository): + "Write the settings to a file." + profilesDirectoryPath = archive.getProfilesPath(getProfileBaseName(repository)) + archive.makeDirectory(os.path.dirname(profilesDirectoryPath)) + archive.writeFileText(profilesDirectoryPath, getRepositoryText(repository)) + for setting in repository.preferences: + setting.updateSaveListeners() + +def writeSettingsPrintMessage(repository): + "Set the settings to the dialog then write them." + writeSettings(repository) + print(repository.title.lower().capitalize() + ' have been saved.') + +def writeValueListToRepositoryWriter( repositoryWriter, setting ): + "Write tab separated name and list to the repository writer." + repositoryWriter.write( setting.name ) + for item in setting.value: + if item != '[]': + repositoryWriter.write(globalSpreadsheetSeparator) + repositoryWriter.write( item ) + repositoryWriter.write('\n') + + +class StringSetting: + "A class to display, read & write a string." + def __init__(self): + "Set the update function to none." + self.entry = None + self.updateFunction = None + + def __repr__(self): + "Get the string representation of this StringSetting." + return str(self.__dict__) + + def addToDialog( self, gridPosition ): + "Add this to the dialog." + gridPosition.increment() + self.label = Tkinter.Label( gridPosition.master, text = self.name ) + self.label.grid( row = gridPosition.row, column = 0, columnspan = 3, sticky = Tkinter.W ) + self.createEntry( gridPosition.master ) + self.setStateToValue() + self.entry.grid( row = gridPosition.row, column = 3, columnspan = 2, sticky = Tkinter.W ) + self.bindEntry() + LabelHelp( self.repository.fileNameHelp, gridPosition.master, self.name, self.label ) + + def addToMenu( self, repositoryMenu ): + "Do nothing because this should only be added to a frameable repository menu." + pass + + def addToMenuFrameable( self, repositoryMenu ): + "Add this to the frameable repository menu." + titleFromName = getTitleFromName( self.name ) + helpWindowMenu = Tkinter.Menu( repositoryMenu, tearoff = 0 ) + repositoryMenu.add_cascade( label = titleFromName, menu = helpWindowMenu, underline = 0 ) + if self.name in self.repository.frameList.value: + helpWindowMenu.add_command( label = 'Remove from Window', command = self.removeFromWindow ) + else: + helpWindowMenu.add_command( label = 'Add to Window', command = self.addToWindow ) + helpWindowMenu.add_separator() + helpWindowMenu.add_command( label = 'Help', command = HelpPage().getOpenFromDocumentationSubName( self.repository.fileNameHelp + '#' + titleFromName ) ) + + def addToWindow(self): + "Add this to the repository frame list." + self.repository.frameList.addToList( self.name ) + + def bindEntry(self): + "Bind the entry to the update function." + if self.updateFunction != None: + self.entry.bind('', self.updateFunction ) + + def createEntry( self, root ): + "Create the entry." + self.entry = Tkinter.Entry( root ) + + def getFromValue( self, name, repository, value ): + "Initialize." + return self.getFromValueOnlyAddToRepository( name, repository, value ) + + def getFromValueOnly( self, name, repository, value ): + "Initialize." + self.defaultValue = value + self.name = name + self.repository = repository + self.value = value + return self + + def getFromValueOnlyAddToRepository( self, name, repository, value ): + "Initialize." + repository.displayEntities.append(self) + repository.menuEntities.append(self) + repository.preferences.append(self) + return self.getFromValueOnly( name, repository, value ) + + def removeFromWindow(self): + "Remove this from the repository frame list." + self.repository.frameList.removeFromList( self.name ) + + def setStateToValue(self): + "Set the entry to the value." + setEntryText(self.entry, self.value) + + def setToDisplay(self): + "Set the string to the entry field." + try: + valueString = self.entry.get() + self.setValueToString( valueString ) + except: + pass + + def setUpdateFunction( self, updateFunction ): + "Set the update function." + self.updateFunction = updateFunction + + def setValueToSplitLine( self, lineIndex, lines, splitLine ): + "Set the value to the second word of a split line." + self.setValueToString(splitLine[1]) + + def setValueToString( self, valueString ): + "Set the value to the value string." + self.value = valueString + + def updateSaveListeners(self): + "Update save listeners if any." + pass + + def writeToRepositoryWriter( self, repositoryWriter ): + "Write tab separated name and value to the repository writer." + repositoryWriter.write('%s%s%s\n' % ( self.name, globalSpreadsheetSeparator, self.value ) ) + + +class BooleanSetting( StringSetting ): + "A class to display, read & write a boolean." + def addToDialog( self, gridPosition ): + "Add this to the dialog." + gridPosition.increment() + self.checkbutton = Tkinter.Checkbutton( gridPosition.master, command = self.toggleCheckbutton, text = self.name ) +#toggleCheckbutton is being used instead of a Tkinter IntVar because there is a weird bug where it doesn't work properly if this setting is not on the first window. + self.checkbutton.grid( row = gridPosition.row, columnspan = 5, sticky = Tkinter.W ) + self.setStateToValue() + LabelHelp( self.repository.fileNameHelp, gridPosition.master, self.name, self.checkbutton ) + + def addToMenu( self, repositoryMenu ): + "Add this to the repository menu." + self.activateToggleMenuCheckbutton = False +#activateToggleMenuCheckbutton is being used instead of setting command after because add_checkbutton does not return a checkbutton. + repositoryMenu.add_checkbutton( label = getTitleFromName( self.name ), command = self.toggleMenuCheckbutton ) + if self.value: + repositoryMenu.invoke( repositoryMenu.index( Tkinter.END ) ) + self.activateToggleMenuCheckbutton = True + + def addToMenuFrameable( self, repositoryMenu ): + "Add this to the frameable repository menu." + titleFromName = getTitleFromName( self.name ) + helpWindowMenu = Tkinter.Menu( repositoryMenu, tearoff = 0 ) + repositoryMenu.add_cascade( label = titleFromName, menu = helpWindowMenu, underline = 0 ) + self.addToMenu( helpWindowMenu ) + helpWindowMenu.add_separator() + helpWindowMenu.add_command( label = 'Help', command = HelpPage().getOpenFromDocumentationSubName( self.repository.fileNameHelp + '#' + titleFromName ) ) + + def setStateToValue(self): + "Set the checkbutton to the boolean." + try: + if self.value: + self.checkbutton.select() + else: + self.checkbutton.deselect() + except: + pass + + def setToDisplay(self): + "Do nothing because toggleCheckbutton is handling the value." + pass + + def setValueToString( self, valueString ): + "Set the boolean to the string." + self.value = ( valueString.lower() == 'true') + + def toggleCheckbutton(self): + "Workaround for Tkinter bug, toggle the value." + self.value = not self.value + self.setStateToValue() + if self.updateFunction != None: + self.updateFunction() + + def toggleMenuCheckbutton(self): + "Workaround for Tkinter bug, toggle the value." + if self.activateToggleMenuCheckbutton: + self.value = not self.value + if self.updateFunction != None: + self.updateFunction() + + +class CloseListener: + "A class to listen to link a window to the global repository dialog list table." + def __init__( self, window, closeFunction = None ): + "Add the window to the global repository dialog list table." + self.closeFunction = closeFunction + self.window = window + self.shouldWasClosedBeBound = True + global globalRepositoryDialogListTable + euclidean.addElementToListDictionaryIfNotThere( window, window, globalRepositoryDialogListTable ) + + def listenToWidget( self, widget ): + "Listen to the destroy message of the widget." + if self.shouldWasClosedBeBound: + self.shouldWasClosedBeBound = False + widget.bind('', self.wasClosed ) + + def wasClosed(self, event): + "The dialog was closed." + global globalCloseListTables + for globalCloseListTable in globalCloseListTables: + if self.window in globalCloseListTable: + del globalCloseListTable[ self.window ] + if self.closeFunction != None: + self.closeFunction() + + +class DisplayToolButton: + "A class to display the tool dialog button, in a two column wide table." + def addToDialog( self, gridPosition ): + "Add this to the dialog." + self.displayButton = Tkinter.Button( gridPosition.master, activebackground = 'black', activeforeground = 'white', text = getEachWordCapitalized( self.name ), command = self.displayDialog ) + setButtonFontWeightString( self.displayButton, self.important ) + gridPosition.incrementGivenNumberOfColumns(2) + self.displayButton.grid( row = gridPosition.row, column = gridPosition.column, columnspan = 2 ) + + def displayDialog(self): + "Display function." + ToolDialog().getFromPath( self.path ).display() + + def getFromPath( self, important, name, path, repository ): + "Initialize." + self.important = important + self.name = name + self.path = path + self.repository = repository + repository.displayEntities.append(self) + return self + + +class FileHelpMenuBar: + def __init__( self, root ): + "Create a menu bar with a file and help menu." + self.underlineLetters = [] + self.menuBar = Tkinter.Menu( root ) + self.root = root + root.config( menu = self.menuBar ) + self.fileMenu = Tkinter.Menu( self.menuBar, tearoff = 0 ) + self.menuBar.add_cascade( label = "File", menu = self.fileMenu, underline = 0 ) + self.underlineLetters.append('f') + + def addMenuToMenuBar( self, labelText, menu ): + "Add a menu to the menu bar." + lowerLabelText = labelText.lower() + for underlineLetterIndex in xrange( len( lowerLabelText ) ): + underlineLetter = lowerLabelText[ underlineLetterIndex ] + if underlineLetter not in self.underlineLetters: + self.underlineLetters.append( underlineLetter ) + self.menuBar.add_cascade( label = labelText, menu = menu, underline = underlineLetterIndex ) + return + self.menuBar.add_cascade( label = labelText, menu = menu ) + + def addPluginToMenuBar( self, modulePath, repository, window ): + "Add a menu to the menu bar from a tool." + pluginModule = archive.getModuleWithPath( modulePath ) + if pluginModule == None: + print('this should never happen, pluginModule in addMenuToMenuBar in settings is None.') + return None + repositoryMenu = Tkinter.Menu( self.menuBar, tearoff = 0 ) + labelText = getEachWordCapitalized( os.path.basename( modulePath ) ) + self.addMenuToMenuBar( labelText, repositoryMenu ) + pluginModule.addToMenu( self.root, repositoryMenu, repository, window ) + + def completeMenu(self, closeFunction, repository, saveFunction, window): + "Complete the menu." + self.closeFunction = closeFunction + self.saveFunction = saveFunction + addAcceleratorCommand('', saveFunction, self.root, self.fileMenu, 'Save') + self.fileMenu.add_command(label = "Save and Close", command = self.saveClose) + addAcceleratorCommand('', closeFunction, self.root, self.fileMenu, 'Close') + self.fileMenu.add_separator() + addAcceleratorCommand('', quitWindows, self.root, self.fileMenu, 'Quit') + skeinforgePluginsPath = archive.getSkeinforgePath('skeinforge_plugins') + pluginFileNames = archive.getPluginFileNamesFromDirectoryPath(skeinforgePluginsPath) + for pluginFileName in pluginFileNames: + self.addPluginToMenuBar(os.path.join(skeinforgePluginsPath, pluginFileName), repository, window) + + def saveClose(self): + "Call the save function then the close function." + self.saveFunction() + self.closeFunction() + + +class FileNameInput( StringSetting ): + "A class to display, read & write a fileName." + def addToDialog( self, gridPosition ): + "Add this to the dialog." + self.gridPosition = gridPosition + gridPosition.executables.append(self) + + def execute(self): + "Open the file picker." + self.wasCancelled = False + parent = self.gridPosition.master + try: + import tkFileDialog + summarized = archive.getSummarizedFileName(self.value) + initialDirectory = os.path.dirname( summarized ) + if len( initialDirectory ) > 0: + initialDirectory += os.sep + else: + initialDirectory = "." + fileName = tkFileDialog.askopenfilename( filetypes = self.getFileNameFirstTypes(), initialdir = initialDirectory, initialfile = os.path.basename( summarized ), parent = parent, title = self.name ) + self.setCancelledValue(fileName) + return + except: + print('Could not get the old directory in settings, so the file picker will be opened in the default directory.') + try: + fileName = tkFileDialog.askopenfilename( filetypes = self.getFileNameFirstTypes(), initialdir = '.', initialfile = '', parent = parent, title = self.name ) + self.setCancelledValue(fileName) + except: + print('Error in execute in FileName in settings, ' + self.name ) + + def getFileNameFirstTypes(self): + "Get the file types with the file type of the fileName moved to the front of the list." + allFiles = [ ('All', '*.*') ] + try: + basename = os.path.basename(self.value) + splitFile = basename.split('.') + allReadables = [] + if len( self.fileTypes ) > 1: + for fileType in self.fileTypes: + allReadable = ( ('All Readable', fileType[1] ) ) + allReadables.append( allReadable ) + if len( splitFile ) < 1: + return allReadables + allFiles + self.fileTypes + baseExtension = splitFile[-1] + for fileType in self.fileTypes: + fileExtension = fileType[1].split('.')[-1] + if fileExtension == baseExtension: + fileNameFirstTypes = self.fileTypes[:] + fileNameFirstTypes.remove( fileType ) + return [ fileType ] + allReadables + allFiles + fileNameFirstTypes + return allReadables + allFiles + self.fileTypes + except: + return allFiles + + def getFromFileName( self, fileTypes, name, repository, value ): + "Initialize." + self.getFromValueOnly( name, repository, value ) + self.fileTypes = fileTypes + self.wasCancelled = False + repository.displayEntities.append(self) + repository.preferences.append(self) + return self + + def setCancelledValue( self, fileName ): + "Set the value to the file name and wasCancelled true if a file was not picked." + if ( str(fileName) == '()' or str(fileName) == ''): + self.wasCancelled = True + else: + self.value = fileName + + def setToDisplay(self): + "Do nothing because the file dialog is handling the value." + pass + + +class FloatSetting( StringSetting ): + "A class to display, read & write a float." + def setValueToString( self, valueString ): + "Set the float to the string." + try: + self.value = float( valueString ) + except: + print('Oops, can not read float' + self.name + ' ' + valueString ) + + +class FloatSpin( FloatSetting ): + "A class to display, read & write an float in a spin box." + def addToMenuFrameable( self, repositoryMenu ): + "Add this to the frameable repository menu." + titleFromName = getTitleFromName( self.name ) + helpWindowMenu = Tkinter.Menu( repositoryMenu, tearoff = 0 ) + repositoryMenu.add_cascade( label = titleFromName, menu = helpWindowMenu, underline = 0 ) + if self.name in self.repository.frameList.value: + helpWindowMenu.add_command( label = 'Remove from Window', command = self.removeFromWindow ) + else: + helpWindowMenu.add_command( label = 'Add to Window', command = self.addToWindow ) + helpWindowMenu.add_separator() + changeString = ' by %s' % self.increment + helpWindowMenu.add_command( label = 'Increase' + changeString, command = self.increase ) + helpWindowMenu.add_command( label = 'Decrease' + changeString, command = self.decrease ) + helpWindowMenu.add_separator() + helpWindowMenu.add_command( label = 'Help', command = HelpPage().getOpenFromDocumentationSubName( self.repository.fileNameHelp + '#' + titleFromName ) ) + + def bindEntry(self): + "Bind the entry to the update function." + self.entry.bind('', self.entryUpdated ) + self.setColor() + + def createEntry( self, root ): + "Create the entry." + self.entry = Tkinter.Spinbox( root, command = self.setColorToDisplay, from_ = self.from_, increment = self.increment, to = self.to ) + + def decrease(self): + "Decrease the value then set the state and color to the value." + self.value -= self.increment + self.setStateUpdateColor() + + def entryUpdated(self, event=None): + "Create the entry." + self.setColorToDisplay() + if self.updateFunction != None: + self.updateFunction(event) + + def getFromValue(self, from_, name, repository, to, value): + "Initialize." + self.backgroundColor = None + self.from_ = from_ + self.minimumWidth = min(value - from_, to - value) + rank = euclidean.getRank(0.05 * (to - from_)) + self.increment = euclidean.getIncrementFromRank(rank) + self.to = to + return self.getFromValueOnlyAddToRepository(name, repository, value) + + def increase(self): + "Increase the value then set the state and color to the value." + self.value += self.increment + self.setStateUpdateColor() + + def setColor(self, event=None): + "Set the color to the value, yellow if it is lower than the default and blue if it is higher." + setSpinColor(self) + + def setColorToDisplay(self, event=None): + "Set the color to the value, yellow if it is lower than the default and blue if it is higher." + self.setToDisplay() + self.setColor() + + def setStateToValue(self): + "Set the entry to the value." + setEntryText( self.entry, self.value ) + self.setColor() + + def setStateUpdateColor(self): + "Set the state to the value, call the update function, then set the color." + self.setStateToValue() + if self.updateFunction != None: + self.updateFunction() + + +class FloatSpinNotOnMenu( FloatSpin ): + "A class to display, read & write an float in a spin box, which is not to be added to a menu." + def getFromValueOnlyAddToRepository( self, name, repository, value ): + "Initialize." + repository.displayEntities.append(self) + repository.preferences.append(self) + return self.getFromValueOnly( name, repository, value ) + + +class FloatSpinUpdate( FloatSpin ): + "A class to display, read, update & write an float in a spin box." + def createEntry( self, root ): + "Create the entry." + self.entry = Tkinter.Spinbox( root, command = self.entryUpdated, from_ = self.from_, increment = self.increment, to = self.to ) + + +class FrameList: + "A class to list the frames." + def addToList(self, word): + "Add the word to the sorted list." + self.value.append(word) + self.value.sort() + self.repository.window.redisplayWindowUpdate() + + def getFromValue( self, name, repository, value ): + "Initialize." + repository.preferences.append(self) + self.name = name + self.repository = repository + self.value = value + return self + + def removeFromList(self, word): + "Remove the word from the sorted list." + self.value.remove(word) + self.value.sort() + self.repository.window.redisplayWindowUpdate() + + def setToDisplay(self): + "Do nothing because frame list does not have a display." + pass + + def setValueToSplitLine( self, lineIndex, lines, splitLine ): + "Set the value to the second and later words of a split line." + self.value = splitLine[1 :] + + def updateSaveListeners(self): + "Update save listeners if any." + pass + + def writeToRepositoryWriter( self, repositoryWriter ): + "Write tab separated name and list to the repository writer." + writeValueListToRepositoryWriter( repositoryWriter, self ) + + +class GridHorizontal: + "A class to place elements horizontally on a grid." + def __init__( self, column, row ): + "Initialize the column and row." + self.column = column + self.columnStart = column + self.row = row + + def getCopy(self): + "Get a copy." + copy = GridHorizontal( self.column, self.row ) + copy.columnStart = self.columnStart + return copy + + def increment(self): + "Increment the position horizontally." + self.column += 1 + + +class GridVertical: + "A class to place elements vertically on a grid." + def __init__( self, column, row ): + "Initialize the column and row." + self.column = column + self.columnOffset = column + self.columnStart = column + self.row = row + self.rowStart = row + + def execute(self): + "The execute button was clicked." + for executable in self.executables: + executable.execute() + saveAll() + self.repository.execute() + + def getCopy(self): + "Get a copy." + copy = GridVertical( self.column, self.row ) + copy.columnOffset = self.columnOffset + copy.columnStart = self.columnStart + copy.rowStart = self.rowStart + return copy + + def increment(self): + "Increment the position vertically." + self.column = self.columnStart + self.columnOffset = self.columnStart + self.row += 1 + + def incrementGivenNumberOfColumns( self, numberOfColumns ): + "Increment the position vertically and offset it horizontally by the given number of columns." + self.column = self.columnOffset + if self.columnOffset == self.columnStart: + self.columnOffset = self.columnStart + 1 + self.row += 1 + return + if self.columnOffset < self.columnStart + numberOfColumns - 1: + self.columnOffset += 1 + return + self.columnOffset = self.columnStart + + def setExecutablesRepository( self, repository ): + "Set the executables to an empty list and set the repository." + self.executables = [] + self.repository = repository + + +class HelpPage: + "A class to open a help page." + def __init__(self): + "Initialize column." + self.column = 3 + + def addToDialog( self, gridPosition ): + "Add this to the dialog." + capitalizedName = getEachWordCapitalized( self.name ) + self.displayButton = Tkinter.Button( gridPosition.master, activebackground = 'black', activeforeground = 'white', command = self.openPage, text = capitalizedName ) + if len( capitalizedName ) < 12: + self.displayButton['width'] = 10 + self.displayButton.grid( row = gridPosition.row, column = self.column, columnspan = 2 ) + + def addToMenu( self, repositoryMenu ): + "Add this to the repository menu." + repositoryMenu.add_command( label = getTitleFromName( self.name ), command = self.openPage ) + + def addToMenuFrameable( self, repositoryMenu ): + "Add this to the frameable repository menu." + self.addToMenu( repositoryMenu ) + + def getFromNameAfterHTTP( self, afterHTTP, name, repository ): + "Initialize." + self.setToNameRepository( name, repository ) + self.hypertextAddress = 'http://' + afterHTTP + return self + + def getFromNameAfterWWW( self, afterWWW, name, repository ): + "Initialize." + self.setToNameRepository( name, repository ) + self.hypertextAddress = 'http://www.' + afterWWW + return self + + def getFromNameSubName( self, name, repository, subName=''): + "Initialize." + self.setToNameRepository( name, repository ) + self.hypertextAddress = archive.getDocumentationPath( subName ) + return self + + def getOpenFromAbsolute( self, hypertextAddress ): + "Get the open help page function from the hypertext address." + self.hypertextAddress = hypertextAddress + return self.openPage + + def getOpenFromAfterHTTP( self, afterHTTP ): + "Get the open help page function from the part of the address after the HTTP." + self.hypertextAddress = 'http://' + afterHTTP + return self.openPage + + def getOpenFromAfterWWW( self, afterWWW ): + "Get the open help page function from the afterWWW of the address after the www." + self.hypertextAddress = 'http://www.' + afterWWW + return self.openPage + + def getOpenFromDocumentationSubName( self, subName=''): + "Get the open help page function from the afterWWW of the address after the www." + self.hypertextAddress = archive.getDocumentationPath( subName ) + return self.openPage + + def openPage(self, event=None): + "Open the browser to the hypertext address." + openWebPage( self.hypertextAddress ) + + def setToNameRepository( self, name, repository ): + "Set to the name and repository." + self.name = name + self.repository = repository + repository.displayEntities.append(self) + repository.menuEntities.append(self) + + +class HelpPageRepository: + "A class to open a repository help page." + def __init__( self, repository ): + "Add this to the dialog." + self.repository = repository + + def openPage(self, event=None): + "Open the browser to the repository help page." + if self.repository.openWikiManualHelpPage == None: + self.repository.openLocalHelpPage() + return + from skeinforge_application.skeinforge_utilities import skeinforge_help + helpRepository = getReadRepository( skeinforge_help.HelpRepository() ) + if helpRepository.wikiManualPrimary.value: + self.repository.openWikiManualHelpPage() + return + self.repository.openLocalHelpPage() + + +class IntSetting( FloatSetting ): + "A class to display, read & write an int." + def setValueToString( self, valueString ): + "Set the integer to the string." + setIntegerValueToString( self, valueString ) + + +class IntSpin(FloatSpin): + "A class to display, read & write an int in a spin box." + def getFromValue(self, from_, name, repository, to, value): + "Initialize." + self.backgroundColor = None + self.from_ = from_ + rank = euclidean.getRank(0.05 * (to - from_)) + self.increment = max(1, int(euclidean.getIncrementFromRank(rank))) + self.minimumWidth = min(value - from_, to - value) + self.to = to + return self.getFromValueOnlyAddToRepository(name, repository, value) + + def getSingleIncrementFromValue( self, from_, name, repository, to, value ): + "Initialize." + self.backgroundColor = None + self.from_ = from_ + self.increment = 1 + self.minimumWidth = min(value - from_, to - value) + self.to = to + return self.getFromValueOnlyAddToRepository( name, repository, value ) + + def setValueToString( self, valueString ): + "Set the integer to the string." + setIntegerValueToString( self, valueString ) + + + +class IntSpinNotOnMenu( IntSpin ): + "A class to display, read & write an integer in a spin box, which is not to be added to a menu." + def getFromValueOnlyAddToRepository( self, name, repository, value ): + "Initialize." + repository.displayEntities.append(self) + repository.preferences.append(self) + return self.getFromValueOnly( name, repository, value ) + + +class IntSpinUpdate( IntSpin ): + "A class to display, read, update & write an int in a spin box." + def createEntry( self, root ): + "Create the entry." + self.entry = Tkinter.Spinbox( root, command = self.entryUpdated, from_ = self.from_, increment = self.increment, to = self.to ) + + +class LabelDisplay: + "A class to add a label." + def addToDialog( self, gridPosition ): + "Add this to the dialog." + gridPosition.increment() + self.label = Tkinter.Label( gridPosition.master, text = self.name ) + self.label.grid( row = gridPosition.row, column = 0, columnspan = self.columnspan, sticky = Tkinter.W ) + LabelHelp( self.repository.fileNameHelp, gridPosition.master, self.name, self.label ) + + def getFromName( self, name, repository ): + "Initialize." + self.columnspan = 3 + self.name = name + self.repository = repository + repository.displayEntities.append(self) + return self + + +class LabelHelp: + "A class to add help to a widget." + def __init__( self, fileNameHelp, master, name, widget ): + "Add menu to the widget." + if len( name ) < 1: + return + self.popupMenu = Tkinter.Menu( master, tearoff = 0 ) + titleFromName = getTitleFromName( name.replace('- ', '').replace(' -', '') ) + self.popupMenu.add_command( label = 'Help', command = HelpPage().getOpenFromDocumentationSubName( fileNameHelp + '#' + titleFromName ) ) + widget.bind('', self.unpostPopupMenu ) + widget.bind('', self.unpostPopupMenu ) + widget.bind('', self.displayPopupMenu ) + + def displayPopupMenu(self, event=None): + 'Display the popup menu when the button is right clicked.' + try: + self.popupMenu.tk_popup( event.x_root + 30, event.y_root, 0 ) + finally: + self.popupMenu.grab_release() + + def unpostPopupMenu(self, event=None): + 'Unpost the popup menu.' + self.popupMenu.unpost() + + +class LabelSeparator: + "A class to add a label and menu separator." + def addToDialog( self, gridPosition ): + "Add this to the dialog." + gridPosition.increment() + self.label = Tkinter.Label( gridPosition.master, text='') + self.label.grid( row = gridPosition.row, column = 0, columnspan = 3, sticky = Tkinter.W ) + + def addToMenu( self, repositoryMenu ): + "Add this to the repository menu." + repositoryMenu.add_separator() + + def addToMenuFrameable( self, repositoryMenu ): + "Add this to the frameable repository menu." + self.addToMenu( repositoryMenu ) + + def getFromRepository( self, repository ): + "Initialize." + self.name = '' + self.repository = repository + repository.displayEntities.append(self) + repository.menuEntities.append(self) + return self + + +class LatentStringVar: + "A class to provide a StringVar when needed." + def __init__(self): + "Set the string var." + self.stringVar = None + + def getString(self): + "Get the string." + return self.getVar().get() + + def getVar(self): + "Get the string var." + if self.stringVar == None: + self.stringVar = Tkinter.StringVar() + return self.stringVar + + def setString(self, word): + "Set the string." + self.getVar().set(word) + + +class LayerCount: + 'A class to handle the layerIndex.' + def __init__(self): + 'Initialize.' + self.layerIndex = -1 + + def __repr__(self): + 'Get the string representation of this LayerCount.' + return str(self.layerIndex) + + def printProgressIncrement(self, procedureName): + 'Print progress then increment layerIndex.' + self.layerIndex += 1 + printProgress(self.layerIndex, procedureName) + + +class MenuButtonDisplay: + "A class to add a menu button." + def addRadiosToDialog( self, gridPosition ): + "Add the menu radios to the dialog." + for menuRadio in self.menuRadios: + menuRadio.addToDialog( gridPosition ) + + def addToMenu( self, repositoryMenu ): + "Add this to the repository menu." + if len( self.menuRadios ) < 1: + print('The MenuButtonDisplay in settings should have menu items.') + print(self.name) + return + self.menu = Tkinter.Menu( repositoryMenu, tearoff = 0 ) + repositoryMenu.add_cascade( label = getTitleFromName( self.name ), menu = self.menu ) + self.setRadioVarToName( self.menuRadios[0].name ) + + def addToMenuFrameable( self, repositoryMenu ): + "Add this to the frameable repository menu." + titleFromName = getTitleFromName( self.name ) + self.addToMenu( repositoryMenu ) + self.menu.add_command( label = 'Help', command = HelpPage().getOpenFromDocumentationSubName( self.repository.fileNameHelp + '#' + titleFromName ) ) + self.menu.add_separator() + + def getFromName( self, name, repository ): + "Initialize." + self.columnspan = 2 + self.menuRadios = [] + self.name = name + self.radioVar = None + self.repository = repository + repository.menuEntities.append(self) + return self + + def removeMenus(self): + "Remove all menus." + deleteMenuItems( self.menu ) + self.menuRadios = [] + + def setRadioVarToName(self, name): + "Get the menu button." + self.optionList = [name] + self.radioVar = Tkinter.StringVar() + self.radioVar.set( self.optionList[0] ) + + def setToNameAddToDialog( self, name, gridPosition ): + "Get the menu button." + if self.radioVar != None: + return + gridPosition.increment() + self.setRadioVarToName( name ) + self.label = Tkinter.Label( gridPosition.master, text = self.name ) + self.label.grid( row = gridPosition.row, column = 0, columnspan = 3, sticky = Tkinter.W ) + self.menuButton = Tkinter.OptionMenu( gridPosition.master, self.radioVar, self.optionList ) + self.menuButton.grid( row = gridPosition.row, column = 3, columnspan = self.columnspan, sticky = Tkinter.W ) + self.menuButton.menu = Tkinter.Menu( self.menuButton, tearoff = 0 ) + self.menu = self.menuButton.menu + self.menuButton['menu'] = self.menu + LabelHelp( self.repository.fileNameHelp, gridPosition.master, self.name, self.label ) + + +class MenuRadio( BooleanSetting ): + "A class to display, read & write a boolean with associated menu radio button." + def addToDialog( self, gridPosition ): + "Add this to the dialog." + self.menuButtonDisplay.setToNameAddToDialog( self.name, gridPosition ) + self.addToSubmenu() + + def addToMenu( self, repositoryMenu ): + "Add this to the submenu set by MenuButtonDisplay, the repository menu is ignored" + self.addToSubmenu() + + def addToMenuFrameable( self, repositoryMenu ): + "Add this to the frameable repository menu." + self.addToMenu( repositoryMenu ) + + def addToSubmenu(self): + "Add this to the submenu." + self.activate = False + menu = self.menuButtonDisplay.menu + menu.add_radiobutton( label = self.name, command = self.clickRadio, value = self.name, variable = self.menuButtonDisplay.radioVar ) + self.menuLength = menu.index( Tkinter.END ) + if self.value: + self.menuButtonDisplay.radioVar.set( self.name ) + self.invoke() + self.activate = True + + def clickRadio(self): + "Workaround for Tkinter bug, invoke and set the value when clicked." + if not self.activate: + return + self.menuButtonDisplay.radioVar.set( self.name ) + if self.updateFunction != None: + self.updateFunction() + + def getFromMenuButtonDisplay( self, menuButtonDisplay, name, repository, value ): + "Initialize." + self.getFromValueOnlyAddToRepository( name, repository, value ) + self.menuButtonDisplay = menuButtonDisplay + self.menuButtonDisplay.menuRadios.append(self) + return self + + def invoke(self): + "Workaround for Tkinter bug, invoke to set the value when changed." + self.menuButtonDisplay.menu.invoke( self.menuLength ) + + def setStateToValue(self): + "Set the checkbutton to the boolean." + try: + if self.value: + self.invoke() + except: + pass + + def setToDisplay(self): + "Set the boolean to the checkbutton." + if self.menuButtonDisplay.radioVar != None: + self.value = ( self.menuButtonDisplay.radioVar.get() == self.name ) + + +class PluginFrame: + "A class to display the plugins in a frame." + def __init__(self): + "Initialize." + self.gridTable = {} + self.latentStringVar = LatentStringVar() + self.oldLatentString = '' + + def addToDialog( self, gridPosition ): + "Add this to the dialog." + gridPosition.increment() + self.gridPosition = gridPosition.getCopy() + self.gridPosition.master = gridPosition.master + self.createFrame( gridPosition ) + + def createFrame( self, gridPosition ): + "Create the frame." + gridVertical = GridVertical( 0, 0 ) + gridVertical.master = Tkinter.LabelFrame( gridPosition.master, borderwidth = 3, relief = 'raised') + gridVertical.master.grid( row = gridPosition.row, column = gridPosition.column, columnspan = 12, sticky = Tkinter.E + Tkinter.W + Tkinter.N + Tkinter.S ) + gridPosition.master.grid_rowconfigure( gridPosition.row, weight = 1 ) + gridPosition.master.grid_columnconfigure( gridPosition.column + 11, weight = 1 ) + if self.latentStringVar.getString() == '': + self.defaultRadioButton.setSelect() + self.gridTable[ self.latentStringVar.getString() ] = gridVertical + path = os.path.join( self.directoryPath, self.latentStringVar.getString() ) + pluginModule = archive.getModuleWithPath(path) + if pluginModule == None: + print('this should never happen, pluginModule in addToDialog in PluginFrame in settings is None') + print(path) + return + gridVertical.repository = getReadRepository( pluginModule.getNewRepository() ) + gridVertical.frameGridVertical = GridVertical( 0, 0 ) + gridVertical.frameGridVertical.setExecutablesRepository( gridVertical.repository ) + executeTitle = gridVertical.repository.executeTitle + if executeTitle != None: + executeButton = Tkinter.Button( gridVertical.master, activebackground = 'black', activeforeground = 'blue', text = executeTitle, command = gridVertical.frameGridVertical.execute ) + executeButton.grid( row = gridVertical.row, column = gridVertical.column, sticky = Tkinter.W ) + gridVertical.column += 1 + self.helpButton = Tkinter.Button( gridVertical.master, activebackground = 'black', activeforeground = 'white', text = "?", command = HelpPageRepository( gridVertical.repository ).openPage ) + self.helpButton.grid( row = gridVertical.row, column = gridVertical.column, sticky = Tkinter.W ) + addEmptyRow( gridVertical ) + gridVertical.increment() + from fabmetheus_utilities.hidden_scrollbar import HiddenScrollbar + gridVertical.xScrollbar = HiddenScrollbar( gridVertical.master, orient = Tkinter.HORIZONTAL ) + gridVertical.xScrollbar.grid( row = gridVertical.row + 1, column = gridVertical.column, columnspan = 11, sticky = Tkinter.E + Tkinter.W ) + gridVertical.yScrollbar = HiddenScrollbar( gridVertical.master ) + gridVertical.yScrollbar.grid( row = gridVertical.row, column = gridVertical.column + 12, sticky = Tkinter.N + Tkinter.S ) + canvasHeight = min( 1000, gridPosition.master.winfo_screenheight() - 540 ) - 6 - int( gridVertical.xScrollbar['width'] ) + canvasWidth = min( 650, gridPosition.master.winfo_screenwidth() - 100 ) - 6 - int( gridVertical.yScrollbar['width'] ) + gridVertical.canvas = Tkinter.Canvas( gridVertical.master, height = canvasHeight, highlightthickness = 0, width = canvasWidth ) + gridVertical.frameGridVertical.master = Tkinter.Frame( gridVertical.canvas ) + for setting in gridVertical.repository.displayEntities: + setting.addToDialog( gridVertical.frameGridVertical ) + addEmptyRow( gridVertical.frameGridVertical ) + gridVertical.frameGridVertical.master.update_idletasks() + gridVertical.xScrollbar.config( command = gridVertical.canvas.xview ) + gridVertical.canvas['xscrollcommand'] = gridVertical.xScrollbar.set + gridVertical.yScrollbar.config( command = gridVertical.canvas.yview ) + gridVertical.canvas['yscrollcommand'] = gridVertical.yScrollbar.set + gridVertical.canvas.create_window( 0, 0, anchor = Tkinter.NW, window = gridVertical.frameGridVertical.master ) + gridVertical.canvas['scrollregion'] = gridVertical.frameGridVertical.master.grid_bbox() + gridVertical.canvas.grid( row = gridVertical.row, column = gridVertical.column, columnspan = 12, sticky = Tkinter.E + Tkinter.W + Tkinter.N + Tkinter.S ) + gridVertical.master.grid_rowconfigure( gridVertical.row, weight = 1 ) + gridVertical.master.grid_columnconfigure( gridVertical.column + 11, weight = 1 ) + gridVertical.frameGridVertical.master.lift() + self.oldLatentString = self.latentStringVar.getString() + + def focusSetMaster( self, gridPosition ): + "Set the focus to the plugin master." + gridPosition.frameGridVertical.master.focus_set() + + def getFromPath( self, defaultRadioButton, directoryPath, repository ): + "Initialize." + self.defaultRadioButton = defaultRadioButton + self.directoryPath = directoryPath + self.name = 'PluginFrame' + self.repository = repository + repository.displayEntities.append(self) + repository.preferences.append(self) + return self + + def setStateToValue(self): + "Set the state of all the plugins to the value." + for gridTableValue in self.gridTable.values(): + cancelRepository( gridTableValue.repository ) + + def setToDisplay(self): + "Set the plugins to the display." + pass + + def update(self): + "Update the frame." + if len(self.gridTable) < 1: + return + if self.oldLatentString == self.latentStringVar.getString(): + return + self.oldLatentString = self.latentStringVar.getString() + self.repository.preferences.remove(self) + for setting in self.repository.preferences: + setting.setToDisplay() + writeSettingsPrintMessage(self.repository) + self.repository.preferences.append(self) + if self.latentStringVar.getString() in self.gridTable: + gridPosition = self.gridTable[self.latentStringVar.getString()] + gridPosition.master.lift() + self.focusSetMaster(gridPosition) + return + self.createFrame(self.gridPosition) + + def updateSaveListeners(self): + "Update save listeners if any." + gridTableKeys = self.gridTable.keys() + gridTableKeys.sort() + for gridTableKey in gridTableKeys: + saveRepository( self.gridTable[ gridTableKey ].repository ) + + def writeToRepositoryWriter( self, repositoryWriter ): + "Write tab separated name and value to the repository writer." + pass + + +class PluginGroupFrame( PluginFrame ): + "A class to display the plugin groups in a frame." + def createFrame( self, gridPosition ): + "Create the frame." + gridVertical = GridVertical( 0, 0 ) + gridVertical.master = Tkinter.LabelFrame( gridPosition.master, borderwidth = 3, relief = 'raised') + gridVertical.master.grid( row = gridPosition.row, column = gridPosition.column, columnspan = 11, sticky = Tkinter.E + Tkinter.W + Tkinter.N + Tkinter.S ) + gridPosition.master.grid_rowconfigure( gridPosition.row, weight = 1 ) + gridPosition.master.grid_columnconfigure( gridPosition.column + 10, weight = 1 ) + if self.latentStringVar.getString() == '': + self.defaultRadioButton.setSelect() + self.gridTable[ self.latentStringVar.getString() ] = gridVertical + path = os.path.join( self.directoryPath, self.latentStringVar.getString() ) + pluginModule = archive.getModuleWithPath(path) + if pluginModule == None: + print('this should never happen, pluginModule in addToDialog in PluginFrame in settings is None') + print(path) + return + gridVertical.repository = getReadRepository( pluginModule.getNewRepository() ) + gridVertical.setExecutablesRepository( gridVertical.repository ) + executeTitle = gridVertical.repository.executeTitle + if executeTitle != None: + executeButton = Tkinter.Button( gridVertical.master, activebackground = 'black', activeforeground = 'blue', text = executeTitle, command = gridVertical.execute ) + executeButton.grid( row = gridVertical.row, column = gridVertical.column, sticky = Tkinter.W ) + gridVertical.column += 1 + self.helpButton = Tkinter.Button( gridVertical.master, activebackground = 'black', activeforeground = 'white', text = "?", command = HelpPageRepository( gridVertical.repository ).openPage ) + self.helpButton.grid( row = gridVertical.row, column = gridVertical.column, sticky = Tkinter.W ) + addEmptyRow( gridVertical ) + gridVertical.increment() + for setting in gridVertical.repository.displayEntities: + setting.addToDialog( gridVertical ) + gridVertical.master.update_idletasks() + gridVertical.master.lift() + self.oldLatentString = self.latentStringVar.getString() + + def focusSetMaster( self, gridPosition ): + "Set the focus to the plugin master." + gridPosition.master.focus_set() + + +class Radio( BooleanSetting ): + "A class to display, read & write a boolean with associated radio button." + def addToDialog( self, gridPosition ): + "Add this to the dialog." + gridPosition.increment() + self.createRadioButton( gridPosition ) + self.radiobutton.grid( row = gridPosition.row, column = 0, columnspan = 3, sticky = Tkinter.W ) + self.setStateToValue() + + def clickRadio(self): + "Workaround for Tkinter bug, set the value." + self.latentStringVar.setString( self.radiobutton['value'] ) + if self.updateFunction != None: + self.updateFunction() + + def createRadioButton( self, gridPosition ): + "Create the radio button." + self.radiobutton = Tkinter.Radiobutton( gridPosition.master, command = self.clickRadio, text = self.name, value = self.name, variable = self.latentStringVar.getVar() ) + LabelHelp( self.repository.fileNameHelp, gridPosition.master, self.name, self.radiobutton ) + + def getFromRadio( self, latentStringVar, name, repository, value ): + "Initialize." + self.getFromValueOnly( name, repository, value ) + self.latentStringVar = latentStringVar + repository.displayEntities.append(self) + repository.preferences.append(self) +#when addToMenu is added to this entity, the line below should be uncommented +# repository.menuEntities.append(self) + return self + + def setSelect(self): + "Set the int var and select the radio button." + oldLatentStringValue = self.latentStringVar.getString() + self.latentStringVar.setString( self.radiobutton['value'] ) + self.radiobutton.select() + if oldLatentStringValue == '': + return False + return oldLatentStringValue != self.latentStringVar.getString() + + def setStateToValue(self): + "Set the checkbutton to the boolean." + if self.value: + if self.setSelect(): + if self.updateFunction != None: + self.updateFunction() + + def setToDisplay(self): + "Set the boolean to the checkbutton." + self.value = ( self.latentStringVar.getString() == self.radiobutton['value'] ) + + +class RadioCapitalized( Radio ): + "A class to display, read & write a boolean with associated radio button." + def createRadioButton( self, gridPosition ): + "Create the radio button." + capitalizedName = getEachWordCapitalized( self.name ) + self.radiobutton = Tkinter.Radiobutton( gridPosition.master, command = self.clickRadio, text = capitalizedName, value = self.name, variable = self.latentStringVar.getVar() ) + + +class RadioCapitalizedButton( Radio ): + "A class to display, read & write a boolean with associated radio button." + def createRadioButton( self, gridPosition ): + "Create the radio button." + capitalizedName = getEachWordCapitalized( self.name ) + self.radiobutton = Tkinter.Radiobutton( gridPosition.master, command = self.clickRadio, text = capitalizedName, value = self.name, variable = self.latentStringVar.getVar() ) + self.displayButton = Tkinter.Button( gridPosition.master, activebackground = 'black', activeforeground = 'white', text = capitalizedName, command = self.displayDialog ) + self.displayButton.grid( row = gridPosition.row, column = 3, columnspan = 2 ) + + def displayDialog(self): + "Display function." + ToolDialog().getFromPath( self.path ).display() + self.setSelect() + + def getFromPath( self, latentStringVar, name, path, repository, value ): + "Initialize." + self.getFromRadio( latentStringVar, name, repository, value ) + self.path = path + return self + + +class RadioPlugin( RadioCapitalized ): + "A class to display, read & write a boolean with associated radio button." + def addToDialog( self, gridPosition ): + "Add this to the dialog." + self.createRadioButton( gridPosition ) + self.radiobutton['activeforeground'] = 'magenta' + self.radiobutton['selectcolor'] = 'white' + self.radiobutton['borderwidth'] = 3 + self.radiobutton['indicatoron'] = 0 + setButtonFontWeightString( self.radiobutton, self.important ) + self.incrementGridPosition( gridPosition ) + self.setStateToValue() + + def getFromRadio( self, important, latentStringVar, name, repository, value ): + "Initialize." + self.important = important + return RadioCapitalized.getFromRadio( self, latentStringVar, name, repository, value ) + + def incrementGridPosition( self, gridPosition ): + "Increment the grid position." + gridPosition.incrementGivenNumberOfColumns( 10 ) + self.radiobutton.grid( row = gridPosition.row, column = gridPosition.column, sticky = Tkinter.W ) + + +class TextSetting( StringSetting ): + "A class to display, read & write a text." + def __init__(self): + "Set the update function to none." + self.tokenConversions = [ + TokenConversion(), + TokenConversion('carriageReturn', '\r'), + TokenConversion('doubleQuote', '"'), + TokenConversion('newline', '\n'), + TokenConversion('semicolon', ';'), + TokenConversion('singleQuote', "'" ), + TokenConversion('tab', '\t') ] + self.updateFunction = None + + def addToDialog( self, gridPosition ): + "Add this to the dialog." + gridPosition.increment() + self.label = Tkinter.Label( gridPosition.master, text = self.name ) + self.label.grid( row = gridPosition.row, column = 0, columnspan = 3, sticky = Tkinter.W ) + gridPosition.increment() + self.entry = Tkinter.Text( gridPosition.master ) + self.setStateToValue() + self.entry.grid( row = gridPosition.row, column = 0, columnspan = 5, sticky = Tkinter.W ) + LabelHelp( self.repository.fileNameHelp, gridPosition.master, self.name, self.label ) + + def getFromValue( self, name, repository, value ): + "Initialize." + self.getFromValueOnly( name, repository, value ) + repository.displayEntities.append(self) + repository.preferences.append(self) + return self + + def setStateToValue(self): + "Set the entry to the value." + try: + self.entry.delete( 1.0, Tkinter.END ) + self.entry.insert( Tkinter.INSERT, self.value ) + except: + pass + + def setToDisplay(self): + "Set the string to the entry field." + valueString = self.entry.get( 1.0, Tkinter.END ) + self.setValueToString( valueString ) + + def setValueToSplitLine( self, lineIndex, lines, splitLine ): + "Set the value to the second word of a split line." + replacedValue = splitLine[1] + for tokenConversion in reversed( self.tokenConversions ): + replacedValue = tokenConversion.getTokenizedString( replacedValue ) + self.setValueToString( replacedValue ) + + def writeToRepositoryWriter( self, repositoryWriter ): + "Write tab separated name and value to the repository writer." + replacedValue = self.value + for tokenConversion in self.tokenConversions: + replacedValue = tokenConversion.getNamedString( replacedValue ) + repositoryWriter.write('%s%s%s\n' % ( self.name, globalSpreadsheetSeparator, replacedValue ) ) + + +class TokenConversion: + "A class to convert tokens in a string." + def __init__( self, name = 'replaceToken', token = '___replaced___'): + "Set the name and token." + self.replacedName = '___replaced___' + name + self.token = token + + def getNamedString( self, text ): + "Get a string with the tokens changed to names." + return text.replace( self.token, self.replacedName ) + + def getTokenizedString( self, text ): + "Get a string with the names changed to tokens." + return text.replace( self.replacedName, self.token ) + + +class ToolDialog: + "A class to display the tool repository dialog." + def addPluginToMenu( self, menu, path ): + "Add the display command to the menu." + name = os.path.basename(path) + self.path = path + menu.add_command( label = getEachWordCapitalized( name ) + '...', command = self.display ) + + def display(self): + "Display the tool repository dialog." + global globalRepositoryDialogListTable + for repositoryDialog in globalRepositoryDialogListTable: + if getPathInFabmetheusFromFileNameHelp( repositoryDialog.repository.fileNameHelp ) == self.path: + liftRepositoryDialogs( globalRepositoryDialogListTable[ repositoryDialog ] ) + return + self.repositoryDialog = getDisplayedDialogFromPath( self.path ) + + def getFromPath( self, path ): + "Initialize and return display function." + self.path = path + return self + + +class WindowPosition( StringSetting ): + "A class to display, read & write a window position." + def addToDialog( self, gridPosition ): + "Set the root to later get the geometry." + self.root = gridPosition.master + self.setToDisplay() + + def getFromValue( self, repository, value ): + "Initialize." + self.getFromValueOnly('WindowPosition', repository, value ) + repository.displayEntities.append(self) + repository.preferences.append(self) + return self + + def setToDisplay(self): + "Set the string to the window position." + try: + geometryString = self.root.geometry() + except: + return + if geometryString == '1x1+0+0': + return + firstPlusIndexPlusOne = geometryString.find('+') + 1 + self.value = geometryString[ firstPlusIndexPlusOne : ] + + def setWindowPosition(self): + "Set the window position." + movedGeometryString = '%sx%s+%s' % ( self.root.winfo_reqwidth(), self.root.winfo_reqheight(), self.value ) + self.root.geometry( movedGeometryString ) + + +class RepositoryDialog: + def __init__( self, repository, root ): + "Add entities to the dialog." + self.isFirst = ( len( globalRepositoryDialogListTable.keys() ) == 0 ) + self.closeListener = CloseListener(self) + self.repository = repository + self.gridPosition = GridVertical( 0, - 1 ) + self.gridPosition.setExecutablesRepository(repository) + self.gridPosition.master = root + self.root = root + self.openDialogListeners = [] + repository.repositoryDialog = self + root.withdraw() + title = repository.title + if repository.fileNameInput != None: + title = os.path.basename( repository.fileNameInput.value ) + ' - ' + title + root.title( title ) + fileHelpMenuBar = FileHelpMenuBar( root ) + fileHelpMenuBar.completeMenu( self.close, repository, self.save, self ) + for setting in repository.displayEntities: + setting.addToDialog( self.gridPosition ) + if self.gridPosition.row < 20: + addEmptyRow( self.gridPosition ) + self.addButtons( repository, root ) + root.update_idletasks() + self.setWindowPositionDeiconify() + root.deiconify() + for openDialogListener in self.openDialogListeners: + openDialogListener.openDialog() + + def __repr__(self): + "Get the string representation of this RepositoryDialog." + return self.repository.title + + def addButtons( self, repository, root ): + "Add buttons to the dialog." + columnIndex = 0 + self.gridPosition.increment() + saveCommand = self.save + saveText = 'Save' + if self.isFirst: + saveCommand = saveAll + saveText = 'Save All' + if repository.executeTitle != None: + executeButton = Tkinter.Button( root, activebackground = 'black', activeforeground = 'blue', text = repository.executeTitle, command = self.gridPosition.execute ) + executeButton.grid( row = self.gridPosition.row, column = columnIndex, columnspan = 2, sticky = Tkinter.W ) + columnIndex += 2 + self.helpButton = Tkinter.Button( root, activebackground = 'black', activeforeground = 'white', text = "?", command = HelpPageRepository(self.repository).openPage ) + self.helpButton.grid( row = self.gridPosition.row, column = columnIndex, sticky = Tkinter.W ) + self.closeListener.listenToWidget( self.helpButton ) + columnIndex += 6 + cancelButton = Tkinter.Button( root, activebackground = 'black', activeforeground = 'orange', command = self.cancel, fg = 'orange', text = 'Cancel') + cancelButton.grid( row = self.gridPosition.row, column = columnIndex ) + columnIndex += 1 + self.saveButton = Tkinter.Button( root, activebackground = 'black', activeforeground = 'darkgreen', command = saveCommand, fg = 'darkgreen', text = saveText ) + self.saveButton.grid( row = self.gridPosition.row, column = columnIndex ) + + def cancel(self, event=None): + "Set all entities to their saved state." + cancelRepository(self.repository) + + def close(self, event=None): + "The dialog was closed." + try: + self.root.destroy() + except: + pass + + def save(self, event=None): + "Set the entities to the dialog then write them." + saveRepository(self.repository) + + def setWindowPositionDeiconify(self): + "Set the window position if that setting exists." + for setting in self.repository.preferences: + if setting.name == 'WindowPosition': + setting.setWindowPosition() + return diff --git a/SkeinPyPy/fabmetheus_utilities/svg_reader.py b/SkeinPyPy/fabmetheus_utilities/svg_reader.py new file mode 100644 index 0000000..9633852 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/svg_reader.py @@ -0,0 +1,955 @@ +""" +Svg reader. + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.xml_simple_reader import DocumentNode +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from fabmetheus_utilities import svg_writer +import math +import os +import sys +import traceback + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Nophead \nArt of Illusion ' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalNumberOfCornerPoints = 11 +globalNumberOfBezierPoints = globalNumberOfCornerPoints + globalNumberOfCornerPoints +globalNumberOfCirclePoints = 4 * globalNumberOfCornerPoints + + +def addFunctionsToDictionary( dictionary, functions, prefix ): + "Add functions to dictionary." + for function in functions: + dictionary[ function.__name__[ len( prefix ) : ] ] = function + +def getArcComplexes(begin, end, largeArcFlag, radius, sweepFlag, xAxisRotation): + 'Get the arc complexes, procedure at http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes' + if begin == end: + print('Warning, begin equals end in getArcComplexes in svgReader') + print(begin) + print(end) + return [] + if radius.imag < 0.0: + print('Warning, radius.imag is less than zero in getArcComplexes in svgReader') + print(radius) + radius = complex(radius.real, abs(radius.imag)) + if radius.real < 0.0: + print('Warning, radius.real is less than zero in getArcComplexes in svgReader') + print(radius) + radius = complex(abs(radius.real), radius.imag) + if radius.imag <= 0.0: + print('Warning, radius.imag is too small for getArcComplexes in svgReader') + print(radius) + return [end] + if radius.real <= 0.0: + print('Warning, radius.real is too small for getArcComplexes in svgReader') + print(radius) + return [end] + xAxisRotationComplex = euclidean.getWiddershinsUnitPolar(xAxisRotation) + reverseXAxisRotationComplex = complex(xAxisRotationComplex.real, -xAxisRotationComplex.imag) + beginRotated = begin * reverseXAxisRotationComplex + endRotated = end * reverseXAxisRotationComplex + beginTransformed = complex(beginRotated.real / radius.real, beginRotated.imag / radius.imag) + endTransformed = complex(endRotated.real / radius.real, endRotated.imag / radius.imag) + midpointTransformed = 0.5 * (beginTransformed + endTransformed) + midMinusBeginTransformed = midpointTransformed - beginTransformed + midMinusBeginTransformedLength = abs(midMinusBeginTransformed) + if midMinusBeginTransformedLength > 1.0: + print('The ellipse radius is too small for getArcComplexes in svgReader.') + print('So the ellipse will be scaled to fit, according to the formulas in "Step 3: Ensure radii are large enough" of:') + print('http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii') + print('') + radius *= midMinusBeginTransformedLength + beginTransformed /= midMinusBeginTransformedLength + endTransformed /= midMinusBeginTransformedLength + midpointTransformed /= midMinusBeginTransformedLength + midMinusBeginTransformed /= midMinusBeginTransformedLength + midMinusBeginTransformedLength = 1.0 + midWiddershinsTransformed = complex(-midMinusBeginTransformed.imag, midMinusBeginTransformed.real) + midWiddershinsLengthSquared = 1.0 - midMinusBeginTransformedLength * midMinusBeginTransformedLength + if midWiddershinsLengthSquared < 0.0: + midWiddershinsLengthSquared = 0.0 + midWiddershinsLength = math.sqrt(midWiddershinsLengthSquared) + midWiddershinsTransformed *= midWiddershinsLength / abs(midWiddershinsTransformed) + centerTransformed = midpointTransformed + if largeArcFlag == sweepFlag: + centerTransformed -= midWiddershinsTransformed + else: + centerTransformed += midWiddershinsTransformed + beginMinusCenterTransformed = beginTransformed - centerTransformed + beginMinusCenterTransformedLength = abs(beginMinusCenterTransformed) + if beginMinusCenterTransformedLength <= 0.0: + return end + beginAngle = math.atan2(beginMinusCenterTransformed.imag, beginMinusCenterTransformed.real) + endMinusCenterTransformed = endTransformed - centerTransformed + angleDifference = euclidean.getAngleDifferenceByComplex(endMinusCenterTransformed, beginMinusCenterTransformed) + if sweepFlag: + if angleDifference < 0.0: + angleDifference += 2.0 * math.pi + else: + if angleDifference > 0.0: + angleDifference -= 2.0 * math.pi + global globalSideAngle + sides = int(math.ceil(abs(angleDifference) / globalSideAngle)) + sideAngle = angleDifference / float(sides) + arcComplexes = [] + center = complex(centerTransformed.real * radius.real, centerTransformed.imag * radius.imag) * xAxisRotationComplex + for side in xrange(1, sides): + unitPolar = euclidean.getWiddershinsUnitPolar(beginAngle + float(side) * sideAngle) + circumferential = complex(unitPolar.real * radius.real, unitPolar.imag * radius.imag) * beginMinusCenterTransformedLength + point = center + circumferential * xAxisRotationComplex + arcComplexes.append(point) + arcComplexes.append(end) + return arcComplexes + +def getChainMatrixSVG(elementNode, matrixSVG): + "Get chain matrixSVG by svgElement." + matrixSVG = matrixSVG.getOtherTimesSelf(getMatrixSVG(elementNode).tricomplex) + if elementNode.parentNode != None: + matrixSVG = getChainMatrixSVG(elementNode.parentNode, matrixSVG) + return matrixSVG + +def getChainMatrixSVGIfNecessary(elementNode, yAxisPointingUpward): + "Get chain matrixSVG by svgElement and yAxisPointingUpward." + matrixSVG = MatrixSVG() + if yAxisPointingUpward: + return matrixSVG + return getChainMatrixSVG(elementNode, matrixSVG) + +def getCubicPoint( along, begin, controlPoints, end ): + 'Get the cubic point.' + segmentBegin = getQuadraticPoint( along, begin, controlPoints[0], controlPoints[1] ) + segmentEnd = getQuadraticPoint( along, controlPoints[0], controlPoints[1], end ) + return ( 1.0 - along ) * segmentBegin + along * segmentEnd + +def getCubicPoints( begin, controlPoints, end, numberOfBezierPoints=globalNumberOfBezierPoints): + 'Get the cubic points.' + bezierPortion = 1.0 / float(numberOfBezierPoints) + cubicPoints = [] + for bezierIndex in xrange( 1, numberOfBezierPoints + 1 ): + cubicPoints.append(getCubicPoint(bezierPortion * bezierIndex, begin, controlPoints, end)) + return cubicPoints + +def getFontReader(fontFamily): + 'Get the font reader for the fontFamily.' + fontLower = fontFamily.lower().replace(' ', '_') + global globalFontReaderDictionary + if fontLower in globalFontReaderDictionary: + return globalFontReaderDictionary[fontLower] + global globalFontFileNames + if globalFontFileNames == None: + globalFontFileNames = archive.getFileNamesByFilePaths(archive.getFilePathsByDirectory(getFontsDirectoryPath())) + if fontLower not in globalFontFileNames: + print('Warning, the %s font was not found in the fabmetheus_utilities/fonts folder, so Gentium Basic Regular will be substituted.' % fontFamily) + print('The available fonts are:') + globalFontFileNames.sort() + print(globalFontFileNames) + print('') + fontLower = 'gentium_basic_regular' + fontReader = FontReader(fontLower) + globalFontReaderDictionary[fontLower] = fontReader + return fontReader + +def getFontsDirectoryPath(): + "Get the fonts directory path." + return archive.getFabmetheusUtilitiesPath('fonts') + +def getLabelString(dictionary): + "Get the label string for the dictionary." + for key in dictionary: + labelIndex = key.find('label') + if labelIndex >= 0: + return dictionary[key] + return '' + +def getMatrixSVG(elementNode): + "Get matrixSVG by svgElement." + matrixSVG = MatrixSVG() + if 'transform' not in elementNode.attributes: + return matrixSVG + transformWords = [] + for transformWord in elementNode.attributes['transform'].replace(')', '(').split('('): + transformWordStrip = transformWord.strip() + if transformWordStrip != '': # workaround for split(character) bug which leaves an extra empty element + transformWords.append(transformWordStrip) + global globalGetTricomplexDictionary + getTricomplexDictionaryKeys = globalGetTricomplexDictionary.keys() + for transformWordIndex, transformWord in enumerate(transformWords): + if transformWord in getTricomplexDictionaryKeys: + transformString = transformWords[transformWordIndex + 1].replace(',', ' ') + matrixSVG = matrixSVG.getSelfTimesOther(globalGetTricomplexDictionary[ transformWord ](transformString.split())) + return matrixSVG + +def getQuadraticPoint( along, begin, controlPoint, end ): + 'Get the quadratic point.' + oneMinusAlong = 1.0 - along + segmentBegin = oneMinusAlong * begin + along * controlPoint + segmentEnd = oneMinusAlong * controlPoint + along * end + return oneMinusAlong * segmentBegin + along * segmentEnd + +def getQuadraticPoints(begin, controlPoint, end, numberOfBezierPoints=globalNumberOfBezierPoints): + 'Get the quadratic points.' + bezierPortion = 1.0 / float(numberOfBezierPoints) + quadraticPoints = [] + for bezierIndex in xrange(1, numberOfBezierPoints + 1): + quadraticPoints.append(getQuadraticPoint(bezierPortion * bezierIndex, begin, controlPoint, end)) + return quadraticPoints + +def getRightStripAlphabetPercent(word): + "Get word with alphabet characters and the percent sign stripped from the right." + word = word.strip() + for characterIndex in xrange(len(word) - 1, -1, -1): + character = word[characterIndex] + if not character.isalpha() and not character == '%': + return float(word[: characterIndex + 1]) + return None + +def getRightStripMinusSplit(lineString): + "Get string with spaces after the minus sign stripped." + oldLineStringLength = -1 + while oldLineStringLength < len(lineString): + oldLineStringLength = len(lineString) + lineString = lineString.replace('- ', '-') + return lineString.split() + +def getStrokeRadius(elementNode): + "Get the stroke radius." + return 0.5 * getRightStripAlphabetPercent(getStyleValue('1.0', elementNode, 'stroke-width')) + +def getStyleValue(defaultValue, elementNode, key): + "Get the stroke value string." + if 'style' in elementNode.attributes: + line = elementNode.attributes['style'] + strokeIndex = line.find(key) + if strokeIndex > -1: + words = line[strokeIndex :].replace(':', ' ').replace(';', ' ').split() + if len(words) > 1: + return words[1] + if key in elementNode.attributes: + return elementNode.attributes[key] + if elementNode.parentNode == None: + return defaultValue + return getStyleValue(defaultValue, elementNode.parentNode, key) + +def getTextComplexLoops(fontFamily, fontSize, text, yAxisPointingUpward=True): + "Get text as complex loops." + textComplexLoops = [] + fontReader = getFontReader(fontFamily) + horizontalAdvanceX = 0.0 + for character in text: + glyph = fontReader.getGlyph(character, yAxisPointingUpward) + textComplexLoops += glyph.getSizedAdvancedLoops(fontSize, horizontalAdvanceX, yAxisPointingUpward) + horizontalAdvanceX += glyph.horizontalAdvanceX + return textComplexLoops + +def getTransformedFillOutline(elementNode, loop, yAxisPointingUpward): + "Get the loops if fill is on, otherwise get the outlines." + fillOutlineLoops = None + if getStyleValue('none', elementNode, 'fill').lower() == 'none': + fillOutlineLoops = intercircle.getAroundsFromLoop(loop, getStrokeRadius(elementNode)) + else: + fillOutlineLoops = [loop] + return getChainMatrixSVGIfNecessary(elementNode, yAxisPointingUpward).getTransformedPaths(fillOutlineLoops) + +def getTransformedOutlineByPath(elementNode, path, yAxisPointingUpward): + "Get the outline from the path." + aroundsFromPath = intercircle.getAroundsFromPath(path, getStrokeRadius(elementNode)) + return getChainMatrixSVGIfNecessary(elementNode, yAxisPointingUpward).getTransformedPaths(aroundsFromPath) + +def getTransformedOutlineByPaths(elementNode, paths, yAxisPointingUpward): + "Get the outline from the paths." + aroundsFromPaths = intercircle.getAroundsFromPaths(paths, getStrokeRadius(elementNode)) + return getChainMatrixSVGIfNecessary(elementNode, yAxisPointingUpward).getTransformedPaths(aroundsFromPaths) + +def getTricomplexmatrix(transformWords): + "Get matrixSVG by transformWords." + tricomplex = [euclidean.getComplexByWords(transformWords)] + tricomplex.append(euclidean.getComplexByWords(transformWords, 2)) + tricomplex.append(euclidean.getComplexByWords(transformWords, 4)) + return tricomplex + +def getTricomplexrotate(transformWords): + "Get matrixSVG by transformWords." + rotate = euclidean.getWiddershinsUnitPolar(math.radians(float(transformWords[0]))) + return [rotate, complex(-rotate.imag,rotate.real), complex()] + +def getTricomplexscale(transformWords): + "Get matrixSVG by transformWords." + scale = euclidean.getComplexByWords(transformWords) + return [complex(scale.real,0.0), complex(0.0,scale.imag), complex()] + +def getTricomplexskewX(transformWords): + "Get matrixSVG by transformWords." + skewX = math.tan(math.radians(float(transformWords[0]))) + return [complex(1.0, 0.0), complex(skewX, 1.0), complex()] + +def getTricomplexskewY(transformWords): + "Get matrixSVG by transformWords." + skewY = math.tan(math.radians(float(transformWords[0]))) + return [complex(1.0, skewY), complex(0.0, 1.0), complex()] + +def getTricomplexTimesColumn(firstTricomplex, otherColumn): + "Get this matrix multiplied by the otherColumn." + dotProductX = firstTricomplex[0].real * otherColumn.real + firstTricomplex[1].real * otherColumn.imag + dotProductY = firstTricomplex[0].imag * otherColumn.real + firstTricomplex[1].imag * otherColumn.imag + return complex(dotProductX, dotProductY) + +def getTricomplexTimesOther(firstTricomplex, otherTricomplex): + "Get the first tricomplex multiplied by the other tricomplex." + #A down, B right from http://en.wikipedia.org/wiki/Matrix_multiplication + tricomplexTimesOther = [getTricomplexTimesColumn(firstTricomplex, otherTricomplex[0])] + tricomplexTimesOther.append(getTricomplexTimesColumn(firstTricomplex, otherTricomplex[1])) + tricomplexTimesOther.append(getTricomplexTimesColumn(firstTricomplex, otherTricomplex[2]) + firstTricomplex[2]) + return tricomplexTimesOther + +def getTricomplextranslate(transformWords): + "Get matrixSVG by transformWords." + translate = euclidean.getComplexByWords(transformWords) + return [complex(1.0, 0.0), complex(0.0, 1.0), translate] + +def processSVGElementcircle( elementNode, svgReader ): + "Process elementNode by svgReader." + attributes = elementNode.attributes + center = euclidean.getComplexDefaultByDictionaryKeys( complex(), attributes, 'cx', 'cy') + radius = euclidean.getFloatDefaultByDictionary( 0.0, attributes, 'r') + if radius == 0.0: + print('Warning, in processSVGElementcircle in svgReader radius is zero in:') + print(attributes) + return + global globalNumberOfCirclePoints + global globalSideAngle + loop = [] + loopLayer = svgReader.getLoopLayer() + for side in xrange( globalNumberOfCirclePoints ): + unitPolar = euclidean.getWiddershinsUnitPolar( float(side) * globalSideAngle ) + loop.append( center + radius * unitPolar ) + loopLayer.loops += getTransformedFillOutline(elementNode, loop, svgReader.yAxisPointingUpward) + +def processSVGElementellipse( elementNode, svgReader ): + "Process elementNode by svgReader." + attributes = elementNode.attributes + center = euclidean.getComplexDefaultByDictionaryKeys( complex(), attributes, 'cx', 'cy') + radius = euclidean.getComplexDefaultByDictionaryKeys( complex(), attributes, 'rx', 'ry') + if radius.real == 0.0 or radius.imag == 0.0: + print('Warning, in processSVGElementellipse in svgReader radius is zero in:') + print(attributes) + return + global globalNumberOfCirclePoints + global globalSideAngle + loop = [] + loopLayer = svgReader.getLoopLayer() + for side in xrange( globalNumberOfCirclePoints ): + unitPolar = euclidean.getWiddershinsUnitPolar( float(side) * globalSideAngle ) + loop.append( center + complex( unitPolar.real * radius.real, unitPolar.imag * radius.imag ) ) + loopLayer.loops += getTransformedFillOutline(elementNode, loop, svgReader.yAxisPointingUpward) + +def processSVGElementg(elementNode, svgReader): + 'Process elementNode by svgReader.' + if 'id' not in elementNode.attributes: + return + idString = elementNode.attributes['id'] + if 'beginningOfControlSection' in elementNode.attributes: + if elementNode.attributes['beginningOfControlSection'].lower()[: 1] == 't': + svgReader.stopProcessing = True + return + idStringLower = idString.lower() + zIndex = idStringLower.find('z:') + if zIndex < 0: + idStringLower = getLabelString(elementNode.attributes) + zIndex = idStringLower.find('z:') + if zIndex < 0: + return + floatFromValue = euclidean.getFloatFromValue(idStringLower[zIndex + len('z:') :].strip()) + if floatFromValue != None: + svgReader.z = floatFromValue + +def processSVGElementline(elementNode, svgReader): + "Process elementNode by svgReader." + begin = euclidean.getComplexDefaultByDictionaryKeys(complex(), elementNode.attributes, 'x1', 'y1') + end = euclidean.getComplexDefaultByDictionaryKeys(complex(), elementNode.attributes, 'x2', 'y2') + loopLayer = svgReader.getLoopLayer() + loopLayer.loops += getTransformedOutlineByPath(elementNode, [begin, end], svgReader.yAxisPointingUpward) + +def processSVGElementpath( elementNode, svgReader ): + "Process elementNode by svgReader." + if 'd' not in elementNode.attributes: + print('Warning, in processSVGElementpath in svgReader can not get a value for d in:') + print(elementNode.attributes) + return + loopLayer = svgReader.getLoopLayer() + PathReader(elementNode, loopLayer.loops, svgReader.yAxisPointingUpward) + +def processSVGElementpolygon( elementNode, svgReader ): + "Process elementNode by svgReader." + if 'points' not in elementNode.attributes: + print('Warning, in processSVGElementpolygon in svgReader can not get a value for d in:') + print(elementNode.attributes) + return + loopLayer = svgReader.getLoopLayer() + words = getRightStripMinusSplit(elementNode.attributes['points'].replace(',', ' ')) + loop = [] + for wordIndex in xrange( 0, len(words), 2 ): + loop.append(euclidean.getComplexByWords(words[wordIndex :])) + loopLayer.loops += getTransformedFillOutline(elementNode, loop, svgReader.yAxisPointingUpward) + +def processSVGElementpolyline(elementNode, svgReader): + "Process elementNode by svgReader." + if 'points' not in elementNode.attributes: + print('Warning, in processSVGElementpolyline in svgReader can not get a value for d in:') + print(elementNode.attributes) + return + loopLayer = svgReader.getLoopLayer() + words = getRightStripMinusSplit(elementNode.attributes['points'].replace(',', ' ')) + path = [] + for wordIndex in xrange(0, len(words), 2): + path.append(euclidean.getComplexByWords(words[wordIndex :])) + loopLayer.loops += getTransformedOutlineByPath(elementNode, path, svgReader.yAxisPointingUpward) + +def processSVGElementrect( elementNode, svgReader ): + "Process elementNode by svgReader." + attributes = elementNode.attributes + height = euclidean.getFloatDefaultByDictionary( 0.0, attributes, 'height') + if height == 0.0: + print('Warning, in processSVGElementrect in svgReader height is zero in:') + print(attributes) + return + width = euclidean.getFloatDefaultByDictionary( 0.0, attributes, 'width') + if width == 0.0: + print('Warning, in processSVGElementrect in svgReader width is zero in:') + print(attributes) + return + center = euclidean.getComplexDefaultByDictionaryKeys(complex(), attributes, 'x', 'y') + inradius = 0.5 * complex( width, height ) + cornerRadius = euclidean.getComplexDefaultByDictionaryKeys( complex(), attributes, 'rx', 'ry') + loopLayer = svgReader.getLoopLayer() + if cornerRadius.real == 0.0 and cornerRadius.imag == 0.0: + inradiusMinusX = complex( - inradius.real, inradius.imag ) + loop = [center + inradius, center + inradiusMinusX, center - inradius, center - inradiusMinusX] + loopLayer.loops += getTransformedFillOutline(elementNode, loop, svgReader.yAxisPointingUpward) + return + if cornerRadius.real == 0.0: + cornerRadius = complex( cornerRadius.imag, cornerRadius.imag ) + elif cornerRadius.imag == 0.0: + cornerRadius = complex( cornerRadius.real, cornerRadius.real ) + cornerRadius = complex( min( cornerRadius.real, inradius.real ), min( cornerRadius.imag, inradius.imag ) ) + ellipsePath = [ complex( cornerRadius.real, 0.0 ) ] + inradiusMinusCorner = inradius - cornerRadius + loop = [] + global globalNumberOfCornerPoints + global globalSideAngle + for side in xrange( 1, globalNumberOfCornerPoints ): + unitPolar = euclidean.getWiddershinsUnitPolar( float(side) * globalSideAngle ) + ellipsePath.append( complex( unitPolar.real * cornerRadius.real, unitPolar.imag * cornerRadius.imag ) ) + ellipsePath.append( complex( 0.0, cornerRadius.imag ) ) + cornerPoints = [] + for point in ellipsePath: + cornerPoints.append( point + inradiusMinusCorner ) + cornerPointsReversed = cornerPoints[: : -1] + for cornerPoint in cornerPoints: + loop.append( center + cornerPoint ) + for cornerPoint in cornerPointsReversed: + loop.append( center + complex( - cornerPoint.real, cornerPoint.imag ) ) + for cornerPoint in cornerPoints: + loop.append( center - cornerPoint ) + for cornerPoint in cornerPointsReversed: + loop.append( center + complex( cornerPoint.real, - cornerPoint.imag ) ) + loop = euclidean.getLoopWithoutCloseSequentialPoints( 0.0001 * abs(inradius), loop ) + loopLayer.loops += getTransformedFillOutline(elementNode, loop, svgReader.yAxisPointingUpward) + +def processSVGElementtext(elementNode, svgReader): + "Process elementNode by svgReader." + if svgReader.yAxisPointingUpward: + return + fontFamily = getStyleValue('Gentium Basic Regular', elementNode, 'font-family') + fontSize = getRightStripAlphabetPercent(getStyleValue('12.0', elementNode, 'font-size')) + matrixSVG = getChainMatrixSVGIfNecessary(elementNode, svgReader.yAxisPointingUpward) + loopLayer = svgReader.getLoopLayer() + translate = euclidean.getComplexDefaultByDictionaryKeys(complex(), elementNode.attributes, 'x', 'y') + for textComplexLoop in getTextComplexLoops(fontFamily, fontSize, elementNode.getTextContent(), svgReader.yAxisPointingUpward): + translatedLoop = [] + for textComplexPoint in textComplexLoop: + translatedLoop.append(textComplexPoint + translate ) + loopLayer.loops.append(matrixSVG.getTransformedPath(translatedLoop)) + + +class FontReader: + "Class to read a font in the fonts folder." + def __init__(self, fontFamily): + "Initialize." + self.fontFamily = fontFamily + self.glyphDictionary = {} + self.glyphElementNodeDictionary = {} + self.missingGlyph = None + fileName = os.path.join(getFontsDirectoryPath(), fontFamily + '.svg') + documentElement = DocumentNode(fileName, archive.getFileText(fileName)).getDocumentElement() + self.fontElementNode = documentElement.getFirstChildByLocalName('defs').getFirstChildByLocalName('font') + self.fontFaceElementNode = self.fontElementNode.getFirstChildByLocalName('font-face') + self.unitsPerEM = float(self.fontFaceElementNode.attributes['units-per-em']) + glyphElementNodes = self.fontElementNode.getChildElementsByLocalName('glyph') + for glyphElementNode in glyphElementNodes: + self.glyphElementNodeDictionary[glyphElementNode.attributes['unicode']] = glyphElementNode + + def getGlyph(self, character, yAxisPointingUpward): + "Get the glyph for the character." + if character not in self.glyphElementNodeDictionary: + if self.missingGlyph == None: + missingGlyphElementNode = self.fontElementNode.getFirstChildByLocalName('missing-glyph') + self.missingGlyph = Glyph(missingGlyphElementNode, self.unitsPerEM, yAxisPointingUpward) + return self.missingGlyph + if character not in self.glyphDictionary: + self.glyphDictionary[character] = Glyph(self.glyphElementNodeDictionary[character], self.unitsPerEM, yAxisPointingUpward) + return self.glyphDictionary[character] + + +class Glyph: + "Class to handle a glyph." + def __init__(self, elementNode, unitsPerEM, yAxisPointingUpward): + "Initialize." + self.horizontalAdvanceX = float(elementNode.attributes['horiz-adv-x']) + self.loops = [] + self.unitsPerEM = unitsPerEM + elementNode.attributes['fill'] = '' + if 'd' not in elementNode.attributes: + return + PathReader(elementNode, self.loops, yAxisPointingUpward) + + def getSizedAdvancedLoops(self, fontSize, horizontalAdvanceX, yAxisPointingUpward=True): + "Get loops for font size, advanced horizontally." + multiplierX = fontSize / self.unitsPerEM + multiplierY = multiplierX + if not yAxisPointingUpward: + multiplierY = -multiplierY + sizedLoops = [] + for loop in self.loops: + sizedLoop = [] + sizedLoops.append(sizedLoop) + for point in loop: + sizedLoop.append( complex(multiplierX * (point.real + horizontalAdvanceX), multiplierY * point.imag)) + return sizedLoops + + +class MatrixSVG: + "Two by three svg matrix." + def __init__(self, tricomplex=None): + "Initialize." + self.tricomplex = tricomplex + + def __repr__(self): + "Get the string representation of this two by three svg matrix." + return str(self.tricomplex) + + def getOtherTimesSelf(self, otherTricomplex): + "Get the other matrix multiplied by this matrix." + if otherTricomplex == None: + return MatrixSVG(self.tricomplex) + if self.tricomplex == None: + return MatrixSVG(otherTricomplex) + return MatrixSVG(getTricomplexTimesOther(otherTricomplex, self.tricomplex)) + + def getSelfTimesOther(self, otherTricomplex): + "Get this matrix multiplied by the other matrix." + if otherTricomplex == None: + return MatrixSVG(self.tricomplex) + if self.tricomplex == None: + return MatrixSVG(otherTricomplex) + return MatrixSVG(getTricomplexTimesOther(self.tricomplex, otherTricomplex)) + + def getTransformedPath(self, path): + "Get transformed path." + if self.tricomplex == None: + return path + complexX = self.tricomplex[0] + complexY = self.tricomplex[1] + complexTranslation = self.tricomplex[2] + transformedPath = [] + for point in path: + x = complexX.real * point.real + complexY.real * point.imag + y = complexX.imag * point.real + complexY.imag * point.imag + transformedPath.append(complex(x, y) + complexTranslation) + return transformedPath + + def getTransformedPaths(self, paths): + "Get transformed paths." + if self.tricomplex == None: + return paths + transformedPaths = [] + for path in paths: + transformedPaths.append(self.getTransformedPath(path)) + return transformedPaths + + +class PathReader: + "Class to read svg path." + def __init__(self, elementNode, loops, yAxisPointingUpward): + "Add to path string to loops." + self.controlPoints = None + self.elementNode = elementNode + self.loops = loops + self.oldPoint = None + self.outlinePaths = [] + self.path = [] + self.yAxisPointingUpward = yAxisPointingUpward + pathString = elementNode.attributes['d'].replace(',', ' ') + global globalProcessPathWordDictionary + processPathWordDictionaryKeys = globalProcessPathWordDictionary.keys() + for processPathWordDictionaryKey in processPathWordDictionaryKeys: + pathString = pathString.replace( processPathWordDictionaryKey, ' %s ' % processPathWordDictionaryKey ) + self.words = getRightStripMinusSplit(pathString) + for self.wordIndex in xrange( len( self.words ) ): + word = self.words[ self.wordIndex ] + if word in processPathWordDictionaryKeys: + globalProcessPathWordDictionary[word](self) + if len(self.path) > 0: + self.outlinePaths.append(self.path) + self.loops += getTransformedOutlineByPaths(elementNode, self.outlinePaths, yAxisPointingUpward) + + def addPathArc( self, end ): + "Add an arc to the path." + begin = self.getOldPoint() + self.controlPoints = None + radius = self.getComplexByExtraIndex(1) + xAxisRotation = math.radians(float(self.words[self.wordIndex + 3])) + largeArcFlag = euclidean.getBooleanFromValue(self.words[ self.wordIndex + 4 ]) + sweepFlag = euclidean.getBooleanFromValue(self.words[ self.wordIndex + 5 ]) + self.path += getArcComplexes(begin, end, largeArcFlag, radius, sweepFlag, xAxisRotation) + self.wordIndex += 8 + + def addPathCubic( self, controlPoints, end ): + "Add a cubic curve to the path." + begin = self.getOldPoint() + self.controlPoints = controlPoints + self.path += getCubicPoints( begin, controlPoints, end ) + self.wordIndex += 7 + + def addPathCubicReflected( self, controlPoint, end ): + "Add a cubic curve to the path from a reflected control point." + begin = self.getOldPoint() + controlPointBegin = begin + if self.controlPoints != None: + if len(self.controlPoints) == 2: + controlPointBegin = begin + begin - self.controlPoints[-1] + self.controlPoints = [controlPointBegin, controlPoint] + self.path += getCubicPoints(begin, self.controlPoints, end) + self.wordIndex += 5 + + def addPathLine(self, lineFunction, point): + "Add a line to the path." + self.controlPoints = None + self.path.append(point) + self.wordIndex += 3 + self.addPathLineByFunction(lineFunction) + + def addPathLineAxis(self, point): + "Add an axis line to the path." + self.controlPoints = None + self.path.append(point) + self.wordIndex += 2 + + def addPathLineByFunction( self, lineFunction ): + "Add a line to the path by line function." + while 1: + if self.getFloatByExtraIndex() == None: + return + self.path.append(lineFunction()) + self.wordIndex += 2 + + def addPathMove( self, lineFunction, point ): + "Add an axis line to the path." + self.controlPoints = None + if len(self.path) > 0: + self.outlinePaths.append(self.path) + self.oldPoint = self.path[-1] + self.path = [point] + self.wordIndex += 3 + self.addPathLineByFunction(lineFunction) + + def addPathQuadratic( self, controlPoint, end ): + "Add a quadratic curve to the path." + begin = self.getOldPoint() + self.controlPoints = [controlPoint] + self.path += getQuadraticPoints(begin, controlPoint, end) + self.wordIndex += 5 + + def addPathQuadraticReflected( self, end ): + "Add a quadratic curve to the path from a reflected control point." + begin = self.getOldPoint() + controlPoint = begin + if self.controlPoints != None: + if len( self.controlPoints ) == 1: + controlPoint = begin + begin - self.controlPoints[-1] + self.controlPoints = [ controlPoint ] + self.path += getQuadraticPoints(begin, controlPoint, end) + self.wordIndex += 3 + + def getComplexByExtraIndex( self, extraIndex=0 ): + 'Get complex from the extraIndex.' + return euclidean.getComplexByWords(self.words, self.wordIndex + extraIndex) + + def getComplexRelative(self): + "Get relative complex." + return self.getComplexByExtraIndex() + self.getOldPoint() + + def getFloatByExtraIndex( self, extraIndex=0 ): + 'Get float from the extraIndex.' + totalIndex = self.wordIndex + extraIndex + if totalIndex >= len(self.words): + return None + word = self.words[totalIndex] + if word[: 1].isalpha(): + return None + return euclidean.getFloatFromValue(word) + + def getOldPoint(self): + 'Get the old point.' + if len(self.path) > 0: + return self.path[-1] + return self.oldPoint + + def processPathWordA(self): + 'Process path word A.' + self.addPathArc( self.getComplexByExtraIndex( 6 ) ) + + def processPathWorda(self): + 'Process path word a.' + self.addPathArc(self.getComplexByExtraIndex(6) + self.getOldPoint()) + + def processPathWordC(self): + 'Process path word C.' + end = self.getComplexByExtraIndex( 5 ) + self.addPathCubic( [ self.getComplexByExtraIndex( 1 ), self.getComplexByExtraIndex(3) ], end ) + + def processPathWordc(self): + 'Process path word C.' + begin = self.getOldPoint() + end = self.getComplexByExtraIndex( 5 ) + self.addPathCubic( [ self.getComplexByExtraIndex( 1 ) + begin, self.getComplexByExtraIndex(3) + begin ], end + begin ) + + def processPathWordH(self): + "Process path word H." + beginY = self.getOldPoint().imag + self.addPathLineAxis(complex(float(self.words[self.wordIndex + 1]), beginY)) + while 1: + floatByExtraIndex = self.getFloatByExtraIndex() + if floatByExtraIndex == None: + return + self.path.append(complex(floatByExtraIndex, beginY)) + self.wordIndex += 1 + + def processPathWordh(self): + "Process path word h." + begin = self.getOldPoint() + self.addPathLineAxis(complex(float(self.words[self.wordIndex + 1]) + begin.real, begin.imag)) + while 1: + floatByExtraIndex = self.getFloatByExtraIndex() + if floatByExtraIndex == None: + return + self.path.append(complex(floatByExtraIndex + self.getOldPoint().real, begin.imag)) + self.wordIndex += 1 + + def processPathWordL(self): + "Process path word L." + self.addPathLine(self.getComplexByExtraIndex, self.getComplexByExtraIndex( 1 )) + + def processPathWordl(self): + "Process path word l." + self.addPathLine(self.getComplexRelative, self.getComplexByExtraIndex(1) + self.getOldPoint()) + + def processPathWordM(self): + "Process path word M." + self.addPathMove(self.getComplexByExtraIndex, self.getComplexByExtraIndex(1)) + + def processPathWordm(self): + "Process path word m." + self.addPathMove(self.getComplexRelative, self.getComplexByExtraIndex(1) + self.getOldPoint()) + + def processPathWordQ(self): + 'Process path word Q.' + self.addPathQuadratic( self.getComplexByExtraIndex( 1 ), self.getComplexByExtraIndex(3) ) + + def processPathWordq(self): + 'Process path word q.' + begin = self.getOldPoint() + self.addPathQuadratic(self.getComplexByExtraIndex(1) + begin, self.getComplexByExtraIndex(3) + begin) + + def processPathWordS(self): + 'Process path word S.' + self.addPathCubicReflected( self.getComplexByExtraIndex( 1 ), self.getComplexByExtraIndex(3) ) + + def processPathWords(self): + 'Process path word s.' + begin = self.getOldPoint() + self.addPathCubicReflected(self.getComplexByExtraIndex(1) + begin, self.getComplexByExtraIndex(3) + begin) + + def processPathWordT(self): + 'Process path word T.' + self.addPathQuadraticReflected( self.getComplexByExtraIndex( 1 ) ) + + def processPathWordt(self): + 'Process path word t.' + self.addPathQuadraticReflected(self.getComplexByExtraIndex(1) + self.getOldPoint()) + + def processPathWordV(self): + "Process path word V." + beginX = self.getOldPoint().real + self.addPathLineAxis(complex(beginX, float(self.words[self.wordIndex + 1]))) + while 1: + floatByExtraIndex = self.getFloatByExtraIndex() + if floatByExtraIndex == None: + return + self.path.append(complex(beginX, floatByExtraIndex)) + self.wordIndex += 1 + + def processPathWordv(self): + "Process path word v." + begin = self.getOldPoint() + self.addPathLineAxis(complex(begin.real, float(self.words[self.wordIndex + 1]) + begin.imag)) + while 1: + floatByExtraIndex = self.getFloatByExtraIndex() + if floatByExtraIndex == None: + return + self.path.append(complex(begin.real, floatByExtraIndex + self.getOldPoint().imag)) + self.wordIndex += 1 + + def processPathWordZ(self): + "Process path word Z." + self.controlPoints = None + if len(self.path) < 1: + return + self.loops.append(getChainMatrixSVGIfNecessary(self.elementNode, self.yAxisPointingUpward).getTransformedPath(self.path)) + self.oldPoint = self.path[0] + self.path = [] + + def processPathWordz(self): + "Process path word z." + self.processPathWordZ() + + +class SVGReader: + "An svg carving." + def __init__(self): + "Add empty lists." + self.loopLayers = [] + self.sliceDictionary = None + self.stopProcessing = False + self.z = 0.0 + + def flipDirectLayer(self, loopLayer): + "Flip the y coordinate of the layer and direct the loops." + for loop in loopLayer.loops: + for pointIndex, point in enumerate(loop): + loop[pointIndex] = complex(point.real, -point.imag) + triangle_mesh.sortLoopsInOrderOfArea(True, loopLayer.loops) + for loopIndex, loop in enumerate(loopLayer.loops): + isInsideLoops = euclidean.getIsInFilledRegion(loopLayer.loops[: loopIndex], euclidean.getLeftPoint(loop)) + intercircle.directLoop((not isInsideLoops), loop) + + def getLoopLayer(self): + "Return the rotated loop layer." + if self.z != None: + loopLayer = euclidean.LoopLayer(self.z) + self.loopLayers.append(loopLayer) + self.z = None + return self.loopLayers[-1] + + def parseSVG(self, fileName, svgText): + "Parse SVG text and store the layers." + self.fileName = fileName + xmlParser = DocumentNode(fileName, svgText) + self.documentElement = xmlParser.getDocumentElement() + if self.documentElement == None: + print('Warning, documentElement was None in parseSVG in SVGReader, so nothing will be done for:') + print(fileName) + return + self.parseSVGByElementNode(self.documentElement) + + def parseSVGByElementNode(self, elementNode): + "Parse SVG by elementNode." + self.sliceDictionary = svg_writer.getSliceDictionary(elementNode) + self.yAxisPointingUpward = euclidean.getBooleanFromDictionary(False, self.sliceDictionary, 'yAxisPointingUpward') + self.processElementNode(elementNode) + if not self.yAxisPointingUpward: + for loopLayer in self.loopLayers: + self.flipDirectLayer(loopLayer) + + def processElementNode(self, elementNode): + 'Process the xml element.' + if self.stopProcessing: + return + lowerLocalName = elementNode.getNodeName().lower() + global globalProcessSVGElementDictionary + if lowerLocalName in globalProcessSVGElementDictionary: + try: + globalProcessSVGElementDictionary[lowerLocalName](elementNode, self) + except: + print('Warning, in processElementNode in svg_reader, could not process:') + print(elementNode) + traceback.print_exc(file=sys.stdout) + for childNode in elementNode.childNodes: + self.processElementNode(childNode) + + +globalFontFileNames = None +globalFontReaderDictionary = {} +globalGetTricomplexDictionary = {} +globalGetTricomplexFunctions = [ + getTricomplexmatrix, + getTricomplexrotate, + getTricomplexscale, + getTricomplexskewX, + getTricomplexskewY, + getTricomplextranslate ] +globalProcessPathWordFunctions = [ + PathReader.processPathWordA, + PathReader.processPathWorda, + PathReader.processPathWordC, + PathReader.processPathWordc, + PathReader.processPathWordH, + PathReader.processPathWordh, + PathReader.processPathWordL, + PathReader.processPathWordl, + PathReader.processPathWordM, + PathReader.processPathWordm, + PathReader.processPathWordQ, + PathReader.processPathWordq, + PathReader.processPathWordS, + PathReader.processPathWords, + PathReader.processPathWordT, + PathReader.processPathWordt, + PathReader.processPathWordV, + PathReader.processPathWordv, + PathReader.processPathWordZ, + PathReader.processPathWordz ] +globalProcessPathWordDictionary = {} +globalProcessSVGElementDictionary = {} +globalProcessSVGElementFunctions = [ + processSVGElementcircle, + processSVGElementellipse, + processSVGElementg, + processSVGElementline, + processSVGElementpath, + processSVGElementpolygon, + processSVGElementpolyline, + processSVGElementrect, + processSVGElementtext ] +globalSideAngle = 0.5 * math.pi / float( globalNumberOfCornerPoints ) + + +addFunctionsToDictionary( globalGetTricomplexDictionary, globalGetTricomplexFunctions, 'getTricomplex') +addFunctionsToDictionary( globalProcessPathWordDictionary, globalProcessPathWordFunctions, 'processPathWord') +addFunctionsToDictionary( globalProcessSVGElementDictionary, globalProcessSVGElementFunctions, 'processSVGElement') diff --git a/SkeinPyPy/fabmetheus_utilities/svg_writer.py b/SkeinPyPy/fabmetheus_utilities/svg_writer.py new file mode 100644 index 0000000..ae836df --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/svg_writer.py @@ -0,0 +1,281 @@ +""" +Svg_writer is a class and collection of utilities to read from and write to an svg file. + +Svg_writer uses the layer_template.svg file in the templates folder in the same folder as svg_writer, to output an svg file. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities.xml_simple_reader import DocumentNode +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import xml_simple_reader +from fabmetheus_utilities import xml_simple_writer +import cStringIO +import math +import os + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalOriginalTextString = '' in lineStripped: + isComment = False + xml_simple_reader.CommentNode(self.svgElement, '%s%s-->\n' % (globalOriginalTextString, commentNodeOutput.getvalue())).appendSelfToParent() + + def getReplacedSVGTemplate(self, fileName, loopLayers, procedureName, elementNode=None): + 'Get the lines of text from the layer_template.svg file.' + self.extent = self.cornerMaximum - self.cornerMinimum + svgTemplateText = archive.getFileText(archive.getTemplatesPath('layer_template.svg')) + documentNode = DocumentNode(fileName, svgTemplateText) + self.svgElement = documentNode.getDocumentElement() + svgElementDictionary = self.svgElement.attributes + self.sliceDictionary = getSliceDictionary(self.svgElement) + self.controlBoxHeight = float(self.sliceDictionary['controlBoxHeight']) + self.controlBoxWidth = float(self.sliceDictionary['controlBoxWidth']) + self.margin = float(self.sliceDictionary['margin']) + self.marginTop = float(self.sliceDictionary['marginTop']) + self.textHeight = float(self.sliceDictionary['textHeight']) + self.unitScale = float(self.sliceDictionary['unitScale']) + svgMinWidth = float(self.sliceDictionary['svgMinWidth']) + self.controlBoxHeightMargin = self.controlBoxHeight + self.marginTop + if not self.addLayerTemplateToSVG: + self.svgElement.getElementNodeByID('layerTextTemplate').removeFromIDNameParent() + del self.svgElement.getElementNodeByID('sliceElementTemplate').attributes['transform'] + self.graphicsElementNode = self.svgElement.getElementNodeByID('sliceElementTemplate') + self.graphicsElementNode.attributes['id'] = 'z:' + self.addLoopLayersToOutput(loopLayers) + self.setMetadataNoscriptElement('layerHeight', 'Layer Height: ', self.layerHeight) + self.setMetadataNoscriptElement('maxX', 'X: ', self.cornerMaximum.x) + self.setMetadataNoscriptElement('minX', 'X: ', self.cornerMinimum.x) + self.setMetadataNoscriptElement('maxY', 'Y: ', self.cornerMaximum.y) + self.setMetadataNoscriptElement('minY', 'Y: ', self.cornerMinimum.y) + self.setMetadataNoscriptElement('maxZ', 'Z: ', self.cornerMaximum.z) + self.setMetadataNoscriptElement('minZ', 'Z: ', self.cornerMinimum.z) + self.textHeight = float( self.sliceDictionary['textHeight'] ) + controlTop = len(loopLayers) * (self.margin + self.extent.y * self.unitScale + self.textHeight) + self.marginTop + self.textHeight + self.svgElement.getFirstChildByLocalName('title').setTextContent(os.path.basename(fileName) + ' - Slice Layers') + svgElementDictionary['height'] = '%spx' % self.getRounded(max(controlTop, self.controlBoxHeightMargin)) + width = max(self.extent.x * self.unitScale, svgMinWidth) + svgElementDictionary['width'] = '%spx' % self.getRounded( width ) + self.sliceDictionary['decimalPlacesCarried'] = str( self.decimalPlacesCarried ) + if self.edgeWidth != None: + self.sliceDictionary['edgeWidth'] = self.getRounded( self.edgeWidth ) + self.sliceDictionary['yAxisPointingUpward'] = 'true' + self.sliceDictionary['procedureName'] = procedureName + self.setDimensionTexts('dimX', 'X: ' + self.getRounded(self.extent.x)) + self.setDimensionTexts('dimY', 'Y: ' + self.getRounded(self.extent.y)) + self.setDimensionTexts('dimZ', 'Z: ' + self.getRounded(self.extent.z)) + self.setTexts('numberOfLayers', 'Number of Layers: %s' % len(loopLayers)) + volume = 0.0 + for loopLayer in loopLayers: + volume += euclidean.getAreaLoops(loopLayer.loops) + volume *= 0.001 * self.layerHeight + self.setTexts('volume', 'Volume: %s cm3' % self.getRounded(volume)) + if not self.addLayerTemplateToSVG: + self.svgElement.getFirstChildByLocalName('script').removeFromIDNameParent() + self.svgElement.getElementNodeByID('isoControlBox').removeFromIDNameParent() + self.svgElement.getElementNodeByID('layerControlBox').removeFromIDNameParent() + self.svgElement.getElementNodeByID('scrollControlBox').removeFromIDNameParent() + self.graphicsElementNode.removeFromIDNameParent() + self.addOriginalAsComment(elementNode) + return documentNode.__repr__() + + def getRounded(self, number): + 'Get number rounded to the number of carried decimal places as a string.' + return euclidean.getRoundedToPlacesString(self.decimalPlacesCarried, number) + + def getRoundedComplexString(self, point): + 'Get the rounded complex string.' + return self.getRounded( point.real ) + ' ' + self.getRounded( point.imag ) + + def getSVGStringForLoop( self, loop ): + 'Get the svg loop string.' + if len(loop) < 1: + return '' + return self.getSVGStringForPath(loop) + ' z' + + def getSVGStringForLoops( self, loops ): + 'Get the svg loops string.' + loopString = '' + if len(loops) > 0: + loopString += self.getSVGStringForLoop( loops[0] ) + for loop in loops[1 :]: + loopString += ' ' + self.getSVGStringForLoop(loop) + return loopString + + def getSVGStringForPath( self, path ): + 'Get the svg path string.' + svgLoopString = '' + for point in path: + stringBeginning = 'M ' + if len( svgLoopString ) > 0: + stringBeginning = ' L ' + svgLoopString += stringBeginning + self.getRoundedComplexString(point) + return svgLoopString + + def getTransformString(self): + 'Get the svg transform string.' + cornerMinimumXString = self.getRounded(-self.cornerMinimum.x) + cornerMinimumYString = self.getRounded(-self.cornerMinimum.y) + return 'scale(%s, %s) translate(%s, %s)' % (self.unitScale, - self.unitScale, cornerMinimumXString, cornerMinimumYString) + + def setDimensionTexts(self, key, valueString): + 'Set the texts to the valueString followed by mm.' + self.setTexts(key, valueString + ' mm') + + def setMetadataNoscriptElement(self, key, prefix, value): + 'Set the metadata value and the text.' + valueString = self.getRounded(value) + self.sliceDictionary[key] = valueString + self.setDimensionTexts(key, prefix + valueString) + + def setTexts(self, key, valueString): + 'Set the texts to the valueString.' + self.svgElement.getElementNodeByID(key + 'Iso').setTextContent(valueString) + self.svgElement.getElementNodeByID(key + 'Layer').setTextContent(valueString) + self.svgElement.getElementNodeByID(key + 'Scroll').setTextContent(valueString) diff --git a/SkeinPyPy/fabmetheus_utilities/templates/canvas_template.svg b/SkeinPyPy/fabmetheus_utilities/templates/canvas_template.svg new file mode 100644 index 0000000..b146e60 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/templates/canvas_template.svg @@ -0,0 +1,16 @@ + + + + + +replaceLineWithTitle + + + diff --git a/SkeinPyPy/fabmetheus_utilities/templates/layer_template.svg b/SkeinPyPy/fabmetheus_utilities/templates/layer_template.svg new file mode 100644 index 0000000..e410f9a --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/templates/layer_template.svg @@ -0,0 +1,519 @@ + + + + + + + + + replaceWith_Title + + + + + + + Layer 1, z:0.1 + + + + + + + + + + + + + Latitude + < + > + Longitude + < + > + Scale + 1 + < + > + + Min + + + + + + Max + + + + + + Dimension + + + + + + Statistics + + + + + + + + + + + Y + X + 0 + + + 1 + Layer + < + > + Scale + 1 + < + > + + Min + + + + + + Max + + + + + + Dimension + + + + + + Statistics + + + + + + + + + + Y + X + Scale + : 1 + < + > + + Min + + + + + + Max + + + + + + Dimension + + + + + + Statistics + + + + + + [Iso View] + Iso View + [Layer View] + Layer View + [Scroll View] + Scroll View + + diff --git a/SkeinPyPy/fabmetheus_utilities/vector3.py b/SkeinPyPy/fabmetheus_utilities/vector3.py new file mode 100644 index 0000000..e577bb8 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/vector3.py @@ -0,0 +1,336 @@ +""" +Vector3 is a three dimensional vector class. + +Below are examples of Vector3 use. + +>>> from vector3 import Vector3 +>>> origin = Vector3() +>>> origin +0.0, 0.0, 0.0 +>>> pythagoras = Vector3( 3, 4, 0 ) +>>> pythagoras +3.0, 4.0, 0.0 +>>> pythagoras.magnitude() +5.0 +>>> pythagoras.magnitudeSquared() +25 +>>> triplePythagoras = pythagoras * 3.0 +>>> triplePythagoras +9.0, 12.0, 0.0 +>>> plane = pythagoras.dropAxis() +>>> plane +(3+4j) +""" + +from __future__ import absolute_import +try: + import psyco + psyco.full() +except: + pass +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import xml_simple_writer +import math +import operator + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Nophead \nArt of Illusion ' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +class Vector3: + 'A three dimensional vector class.' + __slots__ = ['x', 'y', 'z'] + + def __init__(self, x=0.0, y=0.0, z=0.0): + self.x = x + self.y = y + self.z = z + + def __abs__(self): + 'Get the magnitude of the Vector3.' + return math.sqrt( self.x * self.x + self.y * self.y + self.z * self.z ) + + magnitude = __abs__ + + def __add__(self, other): + 'Get the sum of this Vector3 and other one.' + return Vector3( self.x + other.x, self.y + other.y, self.z + other.z ) + + def __copy__(self): + 'Get the copy of this Vector3.' + return Vector3( self.x, self.y, self.z ) + + __pos__ = __copy__ + + copy = __copy__ + + def __div__(self, other): + 'Get a new Vector3 by dividing each component of this one.' + return Vector3( self.x / other, self.y / other, self.z / other ) + + def __eq__(self, other): + 'Determine whether this vector is identical to other one.' + if other == None: + return False + if other.__class__ != self.__class__: + return False + return self.x == other.x and self.y == other.y and self.z == other.z + + def __floordiv__(self, other): + 'Get a new Vector3 by floor dividing each component of this one.' + return Vector3( self.x // other, self.y // other, self.z // other ) + + def __hash__(self): + 'Determine whether this vector is identical to other one.' + return self.__repr__().__hash__() + + def __iadd__(self, other): + 'Add other Vector3 to this one.' + self.x += other.x + self.y += other.y + self.z += other.z + return self + + def __idiv__(self, other): + 'Divide each component of this Vector3.' + self.x /= other + self.y /= other + self.z /= other + return self + + def __ifloordiv__(self, other): + 'Floor divide each component of this Vector3.' + self.x //= other + self.y //= other + self.z //= other + return self + + def __imul__(self, other): + 'Multiply each component of this Vector3.' + self.x *= other + self.y *= other + self.z *= other + return self + + def __isub__(self, other): + 'Subtract other Vector3 from this one.' + self.x -= other.x + self.y -= other.y + self.z -= other.z + return self + + def __itruediv__(self, other): + 'True divide each component of this Vector3.' + self.x = operator.truediv( self.x, other ) + self.y = operator.truediv( self.y, other ) + self.z = operator.truediv( self.z, other ) + return self + + def __mul__(self, other): + 'Get a new Vector3 by multiplying each component of this one.' + return Vector3( self.x * other, self.y * other, self.z * other ) + + def __ne__(self, other): + 'Determine whether this vector is not identical to other one.' + return not self.__eq__(other) + + def __neg__(self): + return Vector3( - self.x, - self.y, - self.z ) + + def __nonzero__(self): + return self.x != 0 or self.y != 0 or self.z != 0 + + def __rdiv__(self, other): + 'Get a new Vector3 by dividing each component of this one.' + return Vector3( other / self.x, other / self.y, other / self.z ) + + def __repr__(self): + 'Get the string representation of this Vector3.' + return '(%s, %s, %s)' % ( self.x, self.y, self.z ) + + def __rfloordiv__(self, other): + 'Get a new Vector3 by floor dividing each component of this one.' + return Vector3( other // self.x, other // self.y, other // self.z ) + + def __rmul__(self, other): + 'Get a new Vector3 by multiplying each component of this one.' + return Vector3( self.x * other, self.y * other, self.z * other ) + + def __rtruediv__(self, other): + 'Get a new Vector3 by true dividing each component of this one.' + return Vector3( operator.truediv( other , self.x ), operator.truediv( other, self.y ), operator.truediv( other, self.z ) ) + + def __sub__(self, other): + 'Get the difference between the Vector3 and other one.' + return Vector3( self.x - other.x, self.y - other.y, self.z - other.z ) + + def __truediv__(self, other): + 'Get a new Vector3 by true dividing each component of this one.' + return Vector3( operator.truediv( self.x, other ), operator.truediv( self.y, other ), operator.truediv( self.z, other ) ) + + def _getAccessibleAttribute(self, attributeName): + 'Get the accessible attribute.' + if attributeName in globalGetAccessibleAttributeSet: + return getattr(self, attributeName, None) + return None + + def _setAccessibleAttribute(self, attributeName, value): + 'Set the accessible attribute.' + if attributeName in globalSetAccessibleAttributeSet: + setattr(self, attributeName, value) + + def cross(self, other): + 'Calculate the cross product of this vector with other one.' + return Vector3(self.y * other.z - self.z * other.y, -self.x * other.z + self.z * other.x, self.x * other.y - self.y * other.x) + + def distance(self, other): + 'Get the Euclidean distance between this vector and other one.' + return math.sqrt( self.distanceSquared(other) ) + + def distanceSquared(self, other): + 'Get the square of the Euclidean distance between this vector and other one.' + separationX = self.x - other.x + separationY = self.y - other.y + separationZ = self.z - other.z + return separationX * separationX + separationY * separationY + separationZ * separationZ + + def dot(self, other): + 'Calculate the dot product of this vector with other one.' + return self.x * other.x + self.y * other.y + self.z * other.z + + def dropAxis( self, which = 2 ): + 'Get a complex by removing one axis of the vector3.' + if which == 0: + return complex( self.y, self.z ) + if which == 1: + return complex( self.x, self.z ) + if which == 2: + return complex( self.x, self.y ) + + def getFloatList(self): + 'Get the vector as a list of floats.' + return [ float( self.x ), float( self.y ), float( self.z ) ] + + def getIsDefault(self): + 'Determine if this is the zero vector.' + if self.x != 0.0: + return False + if self.y != 0.0: + return False + return self.z == 0.0 + + def getNormalized(self): + 'Get the normalized Vector3.' + magnitude = abs(self) + if magnitude == 0.0: + return self.copy() + return self / magnitude + + def magnitudeSquared(self): + 'Get the square of the magnitude of the Vector3.' + return self.x * self.x + self.y * self.y + self.z * self.z + + def maximize(self, other): + 'Maximize the Vector3.' + self.x = max(other.x, self.x) + self.y = max(other.y, self.y) + self.z = max(other.z, self.z) + + def minimize(self, other): + 'Minimize the Vector3.' + self.x = min(other.x, self.x) + self.y = min(other.y, self.y) + self.z = min(other.z, self.z) + + def normalize(self): + 'Scale each component of this Vector3 so that it has a magnitude of 1. If this Vector3 has a magnitude of 0, this method has no effect.' + magnitude = abs(self) + if magnitude != 0.0: + self /= magnitude + + def reflect( self, normal ): + 'Reflect the Vector3 across the normal, which is assumed to be normalized.' + distance = 2 * ( self.x * normal.x + self.y * normal.y + self.z * normal.z ) + return Vector3( self.x - distance * normal.x, self.y - distance * normal.y, self.z - distance * normal.z ) + + def setToVector3(self, other): + 'Set this Vector3 to be identical to other one.' + self.x = other.x + self.y = other.y + self.z = other.z + + def setToXYZ( self, x, y, z ): + 'Set the x, y, and z components of this Vector3.' + self.x = x + self.y = y + self.z = z + + +globalGetAccessibleAttributeSet = 'x y z'.split() +globalSetAccessibleAttributeSet = globalGetAccessibleAttributeSet + +""" +class Vector3: + __slots__ = ['x', 'y', 'z'] + + + copy = __copy__ + + def __eq__(self, other): + if isinstance(other, Vector3): + return self.x == other.x and \ + self.y == other.y and \ + self.z == other.z + else: + assert hasattr(other, '__len__') and len(other) == 3 + return self.x == other[0] and \ + self.y == other[1] and \ + self.z == other[2] + + def __getattr__(self, name): + try: + return tuple([(self.x, self.y, self.z)['xyz'.index(c)] \ + for c in name]) + except ValueError: + raise AttributeError, name + + def __getitem__(self, key): + return (self.x, self.y, self.z)[key] + + def __iter__(self): + return iter((self.x, self.y, self.z)) + + def __len__(self): + return 3 + + def __repr__(self): + return 'Vector3(%.2f, %.2f, %.2f)' % (self.x, + self.y, + self.z) + + if _enable_swizzle_set: + # This has detrimental performance on ordinary setattr as well + # if enabled + def __setattr__(self, name, value): + if len(name) == 1: + object.__setattr__(self, name, value) + else: + try: + l = [self.x, self.y, self.z] + for c, v in map(None, name, value): + l['xyz'.index(c)] = v + self.x, self.y, self.z = l + except ValueError: + raise AttributeError, name + + def __setitem__(self, key, value): + l = [self.x, self.y, self.z] + l[key] = value + self.x, self.y, self.z = l + +""" diff --git a/SkeinPyPy/fabmetheus_utilities/vector3index.py b/SkeinPyPy/fabmetheus_utilities/vector3index.py new file mode 100644 index 0000000..7d46075 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/vector3index.py @@ -0,0 +1,277 @@ +""" +Vector3 is a three dimensional vector class. + +Below are examples of Vector3 use. + +>>> from vector3 import Vector3 +>>> origin = Vector3() +>>> origin +0.0, 0.0, 0.0 +>>> pythagoras = Vector3( 3, 4, 0 ) +>>> pythagoras +3.0, 4.0, 0.0 +>>> pythagoras.magnitude() +5.0 +>>> pythagoras.magnitudeSquared() +25 +>>> triplePythagoras = pythagoras * 3.0 +>>> triplePythagoras +9.0, 12.0, 0.0 +>>> plane = pythagoras.dropAxis() +>>> plane +(3+4j) +""" + +from __future__ import absolute_import +try: + import psyco + psyco.full() +except: + pass +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import xml_simple_writer +import math +import operator + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Nophead \nArt of Illusion ' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +class Vector3Index: + 'A three dimensional vector index class.' + __slots__ = ['index', 'x', 'y', 'z'] + + def __init__( self, index, x = 0.0, y = 0.0, z = 0.0 ): + self.index = index + self.x = x + self.y = y + self.z = z + + def __abs__(self): + 'Get the magnitude of the Vector3.' + return math.sqrt( self.x * self.x + self.y * self.y + self.z * self.z ) + + magnitude = __abs__ + + def __add__(self, other): + 'Get the sum of this Vector3 and other one.' + return Vector3Index( self.index, self.x + other.x, self.y + other.y, self.z + other.z ) + + def __copy__(self): + 'Get the copy of this Vector3.' + return Vector3Index( self.index, self.x, self.y, self.z ) + + __pos__ = __copy__ + + copy = __copy__ + + def __div__(self, other): + 'Get a new Vector3 by dividing each component of this one.' + return Vector3Index( self.index, self.x / other, self.y / other, self.z / other ) + + def __eq__(self, other): + 'Determine whether this vector is identical to other one.' + if other == None: + return False + if other.__class__ != self.__class__: + return False + return self.x == other.x and self.y == other.y and self.z == other.z + + def __floordiv__(self, other): + 'Get a new Vector3 by floor dividing each component of this one.' + return Vector3Index( self.index, self.x // other, self.y // other, self.z // other ) + + def __hash__(self): + 'Determine whether this vector is identical to other one.' + return self.__repr__().__hash__() + + def __iadd__(self, other): + 'Add other Vector3 to this one.' + self.x += other.x + self.y += other.y + self.z += other.z + return self + + def __idiv__(self, other): + 'Divide each component of this Vector3.' + self.x /= other + self.y /= other + self.z /= other + return self + + def __ifloordiv__(self, other): + 'Floor divide each component of this Vector3.' + self.x //= other + self.y //= other + self.z //= other + return self + + def __imul__(self, other): + 'Multiply each component of this Vector3.' + self.x *= other + self.y *= other + self.z *= other + return self + + def __isub__(self, other): + 'Subtract other Vector3 from this one.' + self.x -= other.x + self.y -= other.y + self.z -= other.z + return self + + def __itruediv__(self, other): + 'True divide each component of this Vector3.' + self.x = operator.truediv( self.x, other ) + self.y = operator.truediv( self.y, other ) + self.z = operator.truediv( self.z, other ) + return self + + def __mul__(self, other): + 'Get a new Vector3 by multiplying each component of this one.' + return Vector3Index( self.index, self.x * other, self.y * other, self.z * other ) + + def __ne__(self, other): + 'Determine whether this vector is not identical to other one.' + return not self.__eq__(other) + + def __neg__(self): + return Vector3Index( self.index, - self.x, - self.y, - self.z ) + + def __nonzero__(self): + return self.x != 0 or self.y != 0 or self.z != 0 + + def __rdiv__(self, other): + 'Get a new Vector3 by dividing each component of this one.' + return Vector3Index( self.index, other / self.x, other / self.y, other / self.z ) + + def __repr__(self): + 'Get the string representation of this Vector3 index.' + return '(%s, %s, %s, %s)' % (self.index, self.x, self.y, self.z) + + def __rfloordiv__(self, other): + 'Get a new Vector3 by floor dividing each component of this one.' + return Vector3Index( self.index, other // self.x, other // self.y, other // self.z ) + + def __rmul__(self, other): + 'Get a new Vector3 by multiplying each component of this one.' + return Vector3Index( self.index, self.x * other, self.y * other, self.z * other ) + + def __rtruediv__(self, other): + 'Get a new Vector3 by true dividing each component of this one.' + return Vector3Index( self.index, operator.truediv( other , self.x ), operator.truediv( other, self.y ), operator.truediv( other, self.z ) ) + + def __sub__(self, other): + 'Get the difference between the Vector3 and other one.' + return Vector3Index( self.index, self.x - other.x, self.y - other.y, self.z - other.z ) + + def __truediv__(self, other): + 'Get a new Vector3 by true dividing each component of this one.' + return Vector3Index( self.index, operator.truediv( self.x, other ), operator.truediv( self.y, other ), operator.truediv( self.z, other ) ) + + def _getAccessibleAttribute(self, attributeName): + 'Get the accessible attribute.' + global globalGetAccessibleAttributeSet + if attributeName in globalGetAccessibleAttributeSet: + return getattr(self, attributeName, None) + return None + + def _setAccessibleAttribute(self, attributeName, value): + 'Set the accessible attribute.' + if attributeName in globalSetAccessibleAttributeSet: + setattr(self, attributeName, value) + + def cross(self, other): + 'Calculate the cross product of this vector with other one.' + return Vector3Index( self.index, self.y * other.z - self.z * other.y, - self.x * other.z + self.z * other.x, self.x * other.y - self.y * other.x ) + + def distance(self, other): + 'Get the Euclidean distance between this vector and other one.' + return math.sqrt( self.distanceSquared(other) ) + + def distanceSquared(self, other): + 'Get the square of the Euclidean distance between this vector and other one.' + separationX = self.x - other.x + separationY = self.y - other.y + separationZ = self.z - other.z + return separationX * separationX + separationY * separationY + separationZ * separationZ + + def dot(self, other): + 'Calculate the dot product of this vector with other one.' + return self.x * other.x + self.y * other.y + self.z * other.z + + def dropAxis( self, which = 2 ): + 'Get a complex by removing one axis of the vector3.' + if which == 0: + return complex( self.y, self.z ) + if which == 1: + return complex( self.x, self.z ) + if which == 2: + return complex( self.x, self.y ) + + def getFloatList(self): + 'Get the vector as a list of floats.' + return [ float( self.x ), float( self.y ), float( self.z ) ] + + def getIsDefault(self): + 'Determine if this is the zero vector.' + if self.x != 0.0: + return False + if self.y != 0.0: + return False + return self.z == 0.0 + + def getNormalized(self): + 'Get the normalized Vector3.' + magnitude = abs(self) + if magnitude == 0.0: + return self.copy() + return self / magnitude + + def magnitudeSquared(self): + 'Get the square of the magnitude of the Vector3.' + return self.x * self.x + self.y * self.y + self.z * self.z + + def maximize(self, other): + 'Maximize the Vector3.' + self.x = max(other.x, self.x) + self.y = max(other.y, self.y) + self.z = max(other.z, self.z) + + def minimize(self, other): + 'Minimize the Vector3.' + self.x = min(other.x, self.x) + self.y = min(other.y, self.y) + self.z = min(other.z, self.z) + + def normalize(self): + 'Scale each component of this Vector3 so that it has a magnitude of 1. If this Vector3 has a magnitude of 0, this method has no effect.' + magnitude = abs(self) + if magnitude != 0.0: + self /= magnitude + + def reflect( self, normal ): + 'Reflect the Vector3 across the normal, which is assumed to be normalized.' + distance = 2 * ( self.x * normal.x + self.y * normal.y + self.z * normal.z ) + return Vector3Index( self.index, self.x - distance * normal.x, self.y - distance * normal.y, self.z - distance * normal.z ) + + def setToVector3(self, other): + 'Set this Vector3 to be identical to other one.' + self.x = other.x + self.y = other.y + self.z = other.z + + def setToXYZ( self, x, y, z ): + 'Set the x, y, and z components of this Vector3.' + self.x = x + self.y = y + self.z = z + + +globalGetAccessibleAttributeSet = 'x y z'.split() +globalSetAccessibleAttributeSet = globalGetAccessibleAttributeSet diff --git a/SkeinPyPy/fabmetheus_utilities/version.txt b/SkeinPyPy/fabmetheus_utilities/version.txt new file mode 100644 index 0000000..a4bc447 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/version.txt @@ -0,0 +1 @@ +12.01.21 \ No newline at end of file diff --git a/SkeinPyPy/fabmetheus_utilities/xml_simple_reader.py b/SkeinPyPy/fabmetheus_utilities/xml_simple_reader.py new file mode 100644 index 0000000..87a8a08 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/xml_simple_reader.py @@ -0,0 +1,849 @@ +""" +The xml_simple_reader.py script is an xml parser that can parse a line separated xml text. + +This xml parser will read a line seperated xml text and produce a tree of the xml with a document element. Each element can have an attribute table, childNodes, a class name, parentNode, text and a link to the document element. + +This example gets an xml tree for the xml file boolean.xml. This example is run in a terminal in the folder which contains boolean.xml and xml_simple_reader.py. + + +> python +Python 2.5.1 (r251:54863, Sep 22 2007, 01:43:31) +[GCC 4.2.1 (SUSE Linux)] on linux2 +Type "help", "copyright", "credits" or "license" for more information. +>>> fileName = 'boolean.xml' +>>> file = open(fileName, 'r') +>>> xmlText = file.read() +>>> file.close() +>>> from xml_simple_reader import DocumentNode +>>> xmlParser = DocumentNode(fileName, xmlText) +>>> print(xmlParser) + ?xml, {'version': '1.0'} + ArtOfIllusion, {'xmlns:bf': '//babelfiche/codec', 'version': '2.0', 'fileversion': '3'} + Scene, {'bf:id': 'theScene'} + materials, {'bf:elem-type': 'java.lang.Object', 'bf:list': 'collection', 'bf:id': '1', 'bf:type': 'java.util.Vector'} +.. +many more lines of the xml tree +.. + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.geometry.geometry_utilities import evaluate +from fabmetheus_utilities.geometry.geometry_utilities import matrix +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import xml_simple_writer +import cStringIO + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Nophead \nArt of Illusion ' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +globalGetAccessibleAttributeSet = set('getPaths getPreviousVertex getPreviousElementNode getVertexes parentNode'.split()) + + +def createAppendByText(parentNode, xmlText): + 'Create and append the child nodes from the xmlText.' + monad = OpenMonad(parentNode) + for character in xmlText: + monad = monad.getNextMonad(character) + +def createAppendByTextb(parentNode, xmlText): + 'Create and append the child nodes from the xmlText.' + monad = OpenMonad(parentNode) + for character in xmlText: + monad = monad.getNextMonad(character) + +def getChildElementsByLocalName(childNodes, localName): + 'Get the childNodes which have the given local name.' + childElementsByLocalName = [] + for childNode in childNodes: + if localName.lower() == childNode.getNodeName(): + childElementsByLocalName.append(childNode) + return childElementsByLocalName + +def getDocumentNode(fileName): + 'Get the document from the file name.' + xmlText = getFileText('test.xml') + return DocumentNode(fileName, xmlText) + +def getElementsByLocalName(childNodes, localName): + 'Get the descendents which have the given local name.' + elementsByLocalName = getChildElementsByLocalName(childNodes, localName) + for childNode in childNodes: + if childNode.getNodeType() == 1: + elementsByLocalName += childNode.getElementsByLocalName(localName) + return elementsByLocalName + +def getFileText(fileName, printWarning=True, readMode='r'): + 'Get the entire text of a file.' + try: + file = open(fileName, readMode) + fileText = file.read() + file.close() + return fileText + except IOError: + if printWarning: + print('The file ' + fileName + ' does not exist.') + return '' + + +class CDATASectionMonad: + 'A monad to handle a CDATASection node.' + def __init__(self, input, parentNode): + 'Initialize.' + self.input = input + self.parentNode = parentNode + + def getNextMonad(self, character): + 'Get the next monad.' + self.input.write(character) + if character == '>': + inputString = self.input.getvalue() + if inputString.endswith(']]>'): + textContent = '<%s\n' % inputString + self.parentNode.childNodes.append(CDATASectionNode(self.parentNode, textContent)) + return OpenMonad(self.parentNode) + return self + + +class CDATASectionNode: + 'A CDATASection node.' + def __init__(self, parentNode, textContent=''): + 'Initialize.' + self.parentNode = parentNode + self.textContent = textContent + + def __repr__(self): + 'Get the string representation of this CDATASection node.' + return self.textContent + + def addToIdentifierDictionaries(self): + 'Add the element to the owner document identifier dictionaries.' + pass + + def addXML(self, depth, output): + 'Add xml for this CDATASection node.' + output.write(self.textContent) + + def appendSelfToParent(self): + 'Append self to the parentNode.' + self.parentNode.appendChild(self) + + def copyXMLChildNodes(self, idSuffix, parentNode): + 'Copy the xml childNodes.' + pass + + def getAttributes(self): + 'Get the attributes.' + return {} + + def getChildNodes(self): + 'Get the empty set.' + return [] + + def getCopy(self, idSuffix, parentNode): + 'Copy the xml element, set its dictionary and add it to the parentNode.' + copy = self.getCopyShallow() + copy.parentNode = parentNode + copy.appendSelfToParent() + return copy + + def getCopyShallow(self, attributes=None): + 'Copy the node and set its parentNode.' + return CDATASectionNode(self.parentNode, self.textContent) + + def getNodeName(self): + 'Get the node name.' + return '#cdata-section' + + def getNodeType(self): + 'Get the node type.' + return 4 + + def getOwnerDocument(self): + 'Get the owner document.' + return self.parentNode.getOwnerDocument() + + def getTextContent(self): + 'Get the text content.' + return self.textContent + + def removeChildNodesFromIDNameParent(self): + 'Remove the childNodes from the id and name dictionaries and the childNodes.' + pass + + def removeFromIDNameParent(self): + 'Remove this from the id and name dictionaries and the childNodes of the parentNode.' + if self.parentNode != None: + self.parentNode.childNodes.remove(self) + + def setParentAddToChildNodes(self, parentNode): + 'Set the parentNode and add this to its childNodes.' + self.parentNode = parentNode + if self.parentNode != None: + self.parentNode.childNodes.append(self) + + attributes = property(getAttributes) + childNodes = property(getChildNodes) + nodeName = property(getNodeName) + nodeType = property(getNodeType) + ownerDocument = property(getOwnerDocument) + + +class CommentMonad(CDATASectionMonad): + 'A monad to handle a comment node.' + def getNextMonad(self, character): + 'Get the next monad.' + self.input.write(character) + if character == '>': + inputString = self.input.getvalue() + if inputString.endswith('-->'): + textContent = '<%s\n' % inputString + self.parentNode.childNodes.append(CommentNode(self.parentNode, textContent)) + return OpenMonad(self.parentNode) + return self + + +class CommentNode(CDATASectionNode): + 'A comment node.' + def getCopyShallow(self, attributes=None): + 'Copy the node and set its parentNode.' + return CommentNode(self.parentNode, self.textContent) + + def getNodeName(self): + 'Get the node name.' + return '#comment' + + def getNodeType(self): + 'Get the node type.' + return 8 + + nodeName = property(getNodeName) + nodeType = property(getNodeType) + + +class DocumentNode: + 'A class to parse an xml text and store the elements.' + def __init__(self, fileName, xmlText): + 'Initialize.' + self.childNodes = [] + self.fileName = fileName + self.idDictionary = {} + self.nameDictionary = {} + self.parentNode = None + self.tagDictionary = {} + self.xmlText = xmlText + createAppendByText(self, xmlText) + + def __repr__(self): + 'Get the string representation of this xml document.' + output = cStringIO.StringIO() + for childNode in self.childNodes: + childNode.addXML(0, output) + return output.getvalue() + + def appendChild(self, elementNode): + 'Append child elementNode to the child nodes.' + self.childNodes.append(elementNode) + elementNode.addToIdentifierDictionaries() + return elementNode + + def getAttributes(self): + 'Get the attributes.' + return {} + + def getCascadeBoolean(self, defaultBoolean, key): + 'Get the cascade boolean.' + return defaultBoolean + + def getCascadeFloat(self, defaultFloat, key): + 'Get the cascade float.' + return defaultFloat + + def getDocumentElement(self): + 'Get the document element.' + if len(self.childNodes) == 0: + return None + return self.childNodes[-1] + + def getElementsByLocalName(self, localName): + 'Get the descendents which have the given local name.' + return getElementsByLocalName(self.childNodes, localName) + + def getImportNameChain(self, suffix=''): + 'Get the import name chain with the suffix at the end.' + return suffix + + def getNodeName(self): + 'Get the node name.' + return '#document' + + def getNodeType(self): + 'Get the node type.' + return 9 + + def getOriginalRoot(self): + 'Get the original reparsed document element.' + if evaluate.getEvaluatedBoolean(True, self.documentElement, 'getOriginalRoot'): + return DocumentNode(self.fileName, self.xmlText).documentElement + return None + + def getOwnerDocument(self): + 'Get the owner document.' + return self + + attributes = property(getAttributes) + documentElement = property(getDocumentElement) + nodeName = property(getNodeName) + nodeType = property(getNodeType) + ownerDocument = property(getOwnerDocument) + + +class DocumentTypeMonad(CDATASectionMonad): + 'A monad to handle a document type node.' + def getNextMonad(self, character): + 'Get the next monad.' + self.input.write(character) + if character == '>': + inputString = self.input.getvalue() + if inputString.endswith('?>'): + textContent = '%s\n' % inputString + self.parentNode.childNodes.append(DocumentTypeNode(self.parentNode, textContent)) + return OpenMonad(self.parentNode) + return self + + +class DocumentTypeNode(CDATASectionNode): + 'A document type node.' + def getCopyShallow(self, attributes=None): + 'Copy the node and set its parentNode.' + return DocumentTypeNode(self.parentNode, self.textContent) + + def getNodeName(self): + 'Get the node name.' + return '#forNowDocumentType' + + def getNodeType(self): + 'Get the node type.' + return 10 + + nodeName = property(getNodeName) + nodeType = property(getNodeType) + + +class ElementEndMonad: + 'A monad to look for the end of an ElementNode tag.' + def __init__(self, parentNode): + 'Initialize.' + self.parentNode = parentNode + + def getNextMonad(self, character): + 'Get the next monad.' + if character == '>': + return TextMonad(self.parentNode) + return self + + +class ElementLocalNameMonad: + 'A monad to set the local name of an ElementNode.' + def __init__(self, character, parentNode): + 'Initialize.' + self.input = cStringIO.StringIO() + self.input.write(character) + self.parentNode = parentNode + + def getNextMonad(self, character): + 'Get the next monad.' + if character == '[': + if (self.input.getvalue() + character).startswith('![CDATA['): + self.input.write(character) + return CDATASectionMonad(self.input, self.parentNode) + if character == '-': + if (self.input.getvalue() + character).startswith('!--'): + self.input.write(character) + return CommentMonad(self.input, self.parentNode) + if character.isspace(): + self.setLocalName() + return ElementReadMonad(self.elementNode) + if character == '/': + self.setLocalName() + self.elementNode.appendSelfToParent() + return ElementEndMonad(self.elementNode.parentNode) + if character == '>': + self.setLocalName() + self.elementNode.appendSelfToParent() + return TextMonad(self.elementNode) + self.input.write(character) + return self + + def setLocalName(self): + 'Set the class name.' + self.elementNode = ElementNode(self.parentNode) + self.elementNode.localName = self.input.getvalue().lower().strip() + + +class ElementNode: + 'An xml element.' + def __init__(self, parentNode=None): + 'Initialize.' + self.attributes = {} + self.childNodes = [] + self.localName = '' + self.parentNode = parentNode + self.xmlObject = None + + def __repr__(self): + 'Get the string representation of this xml document.' + return '%s\n%s\n%s' % (self.localName, self.attributes, self.getTextContent()) + + def _getAccessibleAttribute(self, attributeName): + 'Get the accessible attribute.' + global globalGetAccessibleAttributeSet + if attributeName in globalGetAccessibleAttributeSet: + return getattr(self, attributeName, None) + return None + + def addSuffixToID(self, idSuffix): + 'Add the suffix to the id.' + if 'id' in self.attributes: + self.attributes['id'] += idSuffix + + def addToIdentifierDictionaries(self): + 'Add the element to the owner document identifier dictionaries.' + ownerDocument = self.getOwnerDocument() + importNameChain = self.getImportNameChain() + idKey = self.getStrippedAttributesValue('id') + if idKey != None: + ownerDocument.idDictionary[importNameChain + idKey] = self + nameKey = self.getStrippedAttributesValue('name') + if nameKey != None: + euclidean.addElementToListDictionaryIfNotThere(self, importNameChain + nameKey, ownerDocument.nameDictionary) + for tagKey in self.getTagKeys(): + euclidean.addElementToListDictionaryIfNotThere(self, tagKey, ownerDocument.tagDictionary) + + def addXML(self, depth, output): + 'Add xml for this elementNode.' + innerOutput = cStringIO.StringIO() + xml_simple_writer.addXMLFromObjects(depth + 1, self.childNodes, innerOutput) + innerText = innerOutput.getvalue() + xml_simple_writer.addBeginEndInnerXMLTag(self.attributes, depth, innerText, self.localName, output, self.getTextContent()) + + def appendChild(self, elementNode): + 'Append child elementNode to the child nodes.' + self.childNodes.append(elementNode) + elementNode.addToIdentifierDictionaries() + return elementNode + + def appendSelfToParent(self): + 'Append self to the parentNode.' + self.parentNode.appendChild(self) + + def copyXMLChildNodes(self, idSuffix, parentNode): + 'Copy the xml childNodes.' + for childNode in self.childNodes: + childNode.getCopy(idSuffix, parentNode) + + def getCascadeBoolean(self, defaultBoolean, key): + 'Get the cascade boolean.' + if key in self.attributes: + value = evaluate.getEvaluatedBoolean(None, self, key) + if value != None: + return value + return self.parentNode.getCascadeBoolean(defaultBoolean, key) + + def getCascadeFloat(self, defaultFloat, key): + 'Get the cascade float.' + if key in self.attributes: + value = evaluate.getEvaluatedFloat(None, self, key) + if value != None: + return value + return self.parentNode.getCascadeFloat(defaultFloat, key) + + def getChildElementsByLocalName(self, localName): + 'Get the childNodes which have the given local name.' + return getChildElementsByLocalName(self.childNodes, localName) + + def getCopy(self, idSuffix, parentNode): + 'Copy the xml element, set its dictionary and add it to the parentNode.' + matrix4X4 = matrix.getBranchMatrixSetElementNode(self) + attributesCopy = self.attributes.copy() + attributesCopy.update(matrix4X4.getAttributes('matrix.')) + copy = self.getCopyShallow(attributesCopy) + copy.setParentAddToChildNodes(parentNode) + copy.addSuffixToID(idSuffix) + copy.addToIdentifierDictionaries() + self.copyXMLChildNodes(idSuffix, copy) + return copy + + def getCopyShallow(self, attributes=None): + 'Copy the xml element and set its dictionary and parentNode.' + if attributes == None: # to evade default initialization bug where a dictionary is initialized to the last dictionary + attributes = {} + copyShallow = ElementNode(self.parentNode) + copyShallow.attributes = attributes + copyShallow.localName = self.localName + return copyShallow + + def getDocumentElement(self): + 'Get the document element.' + return self.getOwnerDocument().getDocumentElement() + + def getElementNodeByID(self, idKey): + 'Get the xml element by id.' + idDictionary = self.getOwnerDocument().idDictionary + idKey = self.getImportNameChain() + idKey + if idKey in idDictionary: + return idDictionary[idKey] + return None + + def getElementNodesByName(self, nameKey): + 'Get the xml elements by name.' + nameDictionary = self.getOwnerDocument().nameDictionary + nameKey = self.getImportNameChain() + nameKey + if nameKey in nameDictionary: + return nameDictionary[nameKey] + return None + + def getElementNodesByTag(self, tagKey): + 'Get the xml elements by tag.' + tagDictionary = self.getOwnerDocument().tagDictionary + if tagKey in tagDictionary: + return tagDictionary[tagKey] + return None + + def getElementsByLocalName(self, localName): + 'Get the descendents which have the given local name.' + return getElementsByLocalName(self.childNodes, localName) + + def getFirstChildByLocalName(self, localName): + 'Get the first childNode which has the given class name.' + for childNode in self.childNodes: + if localName.lower() == childNode.getNodeName(): + return childNode + return None + + def getIDSuffix(self, elementIndex=None): + 'Get the id suffix from the dictionary.' + suffix = self.localName + if 'id' in self.attributes: + suffix = self.attributes['id'] + if elementIndex == None: + return '_%s' % suffix + return '_%s_%s' % (suffix, elementIndex) + + def getImportNameChain(self, suffix=''): + 'Get the import name chain with the suffix at the end.' + importName = self.getStrippedAttributesValue('_importName') + if importName != None: + suffix = '%s.%s' % (importName, suffix) + return self.parentNode.getImportNameChain(suffix) + + def getNodeName(self): + 'Get the node name.' + return self.localName + + def getNodeType(self): + 'Get the node type.' + return 1 + + def getOwnerDocument(self): + 'Get the owner document.' + return self.parentNode.getOwnerDocument() + + def getParser(self): + 'Get the parser.' + return self.getOwnerDocument() + + def getPaths(self): + 'Get all paths.' + if self.xmlObject == None: + return [] + return self.xmlObject.getPaths() + + def getPreviousElementNode(self): + 'Get previous ElementNode if it exists.' + if self.parentNode == None: + return None + previousElementNodeIndex = self.parentNode.childNodes.index(self) - 1 + if previousElementNodeIndex < 0: + return None + return self.parentNode.childNodes[previousElementNodeIndex] + + def getPreviousVertex(self, defaultVector3=None): + 'Get previous vertex if it exists.' + if self.parentNode == None: + return defaultVector3 + if self.parentNode.xmlObject == None: + return defaultVector3 + if len(self.parentNode.xmlObject.vertexes) < 1: + return defaultVector3 + return self.parentNode.xmlObject.vertexes[-1] + + def getStrippedAttributesValue(self, keyString): + 'Get the stripped attribute value if the length is at least one, otherwise return None.' + if keyString in self.attributes: + strippedAttributesValue = self.attributes[keyString].strip() + if len(strippedAttributesValue) > 0: + return strippedAttributesValue + return None + + def getSubChildWithID( self, idReference ): + 'Get the childNode which has the idReference.' + for childNode in self.childNodes: + if 'bf:id' in childNode.attributes: + if childNode.attributes['bf:id'] == idReference: + return childNode + subChildWithID = childNode.getSubChildWithID( idReference ) + if subChildWithID != None: + return subChildWithID + return None + + def getTagKeys(self): + 'Get stripped tag keys.' + if 'tags' not in self.attributes: + return [] + tagKeys = [] + tagString = self.attributes['tags'] + if tagString.startswith('='): + tagString = tagString[1 :] + if tagString.startswith('['): + tagString = tagString[1 :] + if tagString.endswith(']'): + tagString = tagString[: -1] + for tagWord in tagString.split(','): + tagKey = tagWord.strip() + if tagKey != '': + tagKeys.append(tagKey) + return tagKeys + + def getTextContent(self): + 'Get the text from the child nodes.' + if len(self.childNodes) == 0: + return '' + firstNode = self.childNodes[0] + if firstNode.nodeType == 3: + return firstNode.textContent + return '' + + def getValueByKey( self, key ): + 'Get value by the key.' + if key in evaluate.globalElementValueDictionary: + return evaluate.globalElementValueDictionary[key](self) + if key in self.attributes: + return evaluate.getEvaluatedLinkValue(self, self.attributes[key]) + return None + + def getVertexes(self): + 'Get the vertexes.' + if self.xmlObject == None: + return [] + return self.xmlObject.getVertexes() + + def getXMLProcessor(self): + 'Get the xmlProcessor.' + return self.getDocumentElement().xmlProcessor + + def linkObject(self, xmlObject): + 'Link self to xmlObject and add xmlObject to archivableObjects.' + self.xmlObject = xmlObject + self.xmlObject.elementNode = self + self.parentNode.xmlObject.archivableObjects.append(self.xmlObject) + + def printAllVariables(self): + 'Print all variables.' + print('attributes') + print(self.attributes) + print('childNodes') + print(self.childNodes) + print('localName') + print(self.localName) + print('parentNode') + print(self.parentNode.getNodeName()) + print('text') + print(self.getTextContent()) + print('xmlObject') + print(self.xmlObject) + print('') + + def printAllVariablesRoot(self): + 'Print all variables and the document element variables.' + self.printAllVariables() + documentElement = self.getDocumentElement() + if documentElement != None: + print('') + print('Root variables:') + documentElement.printAllVariables() + + def removeChildNodesFromIDNameParent(self): + 'Remove the childNodes from the id and name dictionaries and the childNodes.' + childNodesCopy = self.childNodes[:] + for childNode in childNodesCopy: + childNode.removeFromIDNameParent() + + def removeFromIDNameParent(self): + 'Remove this from the id and name dictionaries and the childNodes of the parentNode.' + self.removeChildNodesFromIDNameParent() + idKey = self.getStrippedAttributesValue('id') + if idKey != None: + idDictionary = self.getOwnerDocument().idDictionary + idKey = self.getImportNameChain() + idKey + if idKey in idDictionary: + del idDictionary[idKey] + nameKey = self.getStrippedAttributesValue('name') + if nameKey != None: + euclidean.removeElementFromListTable(self, self.getImportNameChain() + nameKey, self.getOwnerDocument().nameDictionary) + for tagKey in self.getTagKeys(): + euclidean.removeElementFromListTable(self, tagKey, self.getOwnerDocument().tagDictionary) + if self.parentNode != None: + self.parentNode.childNodes.remove(self) + + def setParentAddToChildNodes(self, parentNode): + 'Set the parentNode and add this to its childNodes.' + self.parentNode = parentNode + if self.parentNode != None: + self.parentNode.childNodes.append(self) + + def setTextContent(self, textContent=''): + 'Get the text from the child nodes.' + if len(self.childNodes) == 0: + self.childNodes.append(TextNode(self, textContent)) + return + firstNode = self.childNodes[0] + if firstNode.nodeType == 3: + firstNode.textContent = textContent + self.childNodes.append(TextNode(self, textContent)) + + nodeName = property(getNodeName) + nodeType = property(getNodeType) + ownerDocument = property(getOwnerDocument) + textContent = property(getTextContent) + + +class ElementReadMonad: + 'A monad to read the attributes of the ElementNode tag.' + def __init__(self, elementNode): + 'Initialize.' + self.elementNode = elementNode + + def getNextMonad(self, character): + 'Get the next monad.' + if character.isspace(): + return self + if character == '/': + self.elementNode.appendSelfToParent() + return ElementEndMonad(self.elementNode.parentNode) + if character == '>': + self.elementNode.appendSelfToParent() + return TextMonad(self.elementNode) + return KeyMonad(character, self.elementNode) + + +class KeyMonad: + 'A monad to set the key of an attribute of an ElementNode.' + def __init__(self, character, elementNode): + 'Initialize.' + self.input = cStringIO.StringIO() + self.input.write(character) + self.elementNode = elementNode + + def getNextMonad(self, character): + 'Get the next monad.' + if character == '=': + return ValueMonad(self.elementNode, self.input.getvalue().strip()) + self.input.write(character) + return self + + +class OpenChooseMonad(ElementEndMonad): + 'A monad to choose the next monad.' + def getNextMonad(self, character): + 'Get the next monad.' + if character.isspace(): + return self + if character == '?': + input = cStringIO.StringIO() + input.write(' 0: + self.parentNode.childNodes.append(TextNode(self.parentNode, inputString)) + return OpenChooseMonad(self.parentNode) + self.input.write(character) + return self + + +class TextNode(CDATASectionNode): + 'A text node.' + def addXML(self, depth, output): + 'Add xml for this text node.' + pass + + def getCopyShallow(self, attributes=None): + 'Copy the node and set its parentNode.' + return TextNode(self.parentNode, self.textContent) + + def getNodeName(self): + 'Get the node name.' + return '#text' + + def getNodeType(self): + 'Get the node type.' + return 3 + + nodeName = property(getNodeName) + nodeType = property(getNodeType) + + +class ValueMonad: + 'A monad to set the value of an attribute of an ElementNode.' + def __init__(self, elementNode, key): + 'Initialize.' + self.elementNode = elementNode + self.input = cStringIO.StringIO() + self.key = key + self.quoteCharacter = None + + def getNextMonad(self, character): + 'Get the next monad.' + if self.quoteCharacter == None: + if character == '"' or character == "'": + self.quoteCharacter = character + return self + if self.quoteCharacter == character: + self.elementNode.attributes[self.key] = self.input.getvalue() + return ElementReadMonad(self.elementNode) + self.input.write(character) + return self + diff --git a/SkeinPyPy/fabmetheus_utilities/xml_simple_writer.py b/SkeinPyPy/fabmetheus_utilities/xml_simple_writer.py new file mode 100644 index 0000000..99e5ca6 --- /dev/null +++ b/SkeinPyPy/fabmetheus_utilities/xml_simple_writer.py @@ -0,0 +1,132 @@ +""" +XML tag writer utilities. + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +import cStringIO + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Nophead \nArt of Illusion ' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def addBeginEndInnerXMLTag(attributes, depth, innerText, localName, output, text=''): + 'Add the begin and end xml tag and the inner text if any.' + if len( innerText ) > 0: + addBeginXMLTag(attributes, depth, localName, output, text) + output.write( innerText ) + addEndXMLTag(depth, localName, output) + else: + addClosedXMLTag(attributes, depth, localName, output, text) + +def addBeginXMLTag(attributes, depth, localName, output, text=''): + 'Add the begin xml tag.' + depthStart = '\t' * depth + output.write('%s<%s%s>%s\n' % (depthStart, localName, getAttributesString(attributes), text)) + +def addClosedXMLTag(attributes, depth, localName, output, text=''): + 'Add the closed xml tag.' + depthStart = '\t' * depth + attributesString = getAttributesString(attributes) + if len(text) > 0: + output.write('%s<%s%s >%s\n' % (depthStart, localName, attributesString, text, localName)) + else: + output.write('%s<%s%s />\n' % (depthStart, localName, attributesString)) + +def addEndXMLTag(depth, localName, output): + 'Add the end xml tag.' + depthStart = '\t' * depth + output.write('%s\n' % (depthStart, localName)) + +def addXMLFromLoopComplexZ(attributes, depth, loop, output, z): + 'Add xml from loop.' + addBeginXMLTag(attributes, depth, 'path', output) + for pointComplexIndex in xrange(len(loop)): + pointComplex = loop[pointComplexIndex] + addXMLFromXYZ(depth + 1, pointComplexIndex, output, pointComplex.real, pointComplex.imag, z) + addEndXMLTag(depth, 'path', output) + +def addXMLFromObjects(depth, objects, output): + 'Add xml from objects.' + for object in objects: + object.addXML(depth, output) + +def addXMLFromVertexes(depth, output, vertexes): + 'Add xml from loop.' + for vertexIndex in xrange(len(vertexes)): + vertex = vertexes[vertexIndex] + addXMLFromXYZ(depth + 1, vertexIndex, output, vertex.x, vertex.y, vertex.z) + +def addXMLFromXYZ(depth, index, output, x, y, z): + 'Add xml from x, y & z.' + attributes = {'index' : str(index)} + if x != 0.0: + attributes['x'] = str(x) + if y != 0.0: + attributes['y'] = str(y) + if z != 0.0: + attributes['z'] = str(z) + addClosedXMLTag(attributes, depth, 'vertex', output) + +def compareAttributeKeyAscending(key, otherKey): + 'Get comparison in order to sort attribute keys in ascending order, with the id key first and name second.' + if key == 'id': + return - 1 + if otherKey == 'id': + return 1 + if key == 'name': + return - 1 + if otherKey == 'name': + return 1 + if key < otherKey: + return - 1 + return int(key > otherKey) + +def getAttributesString(attributes): + 'Add the closed xml tag.' + attributesString = '' + attributesKeys = attributes.keys() + attributesKeys.sort(compareAttributeKeyAscending) + for attributesKey in attributesKeys: + valueString = str(attributes[attributesKey]) + if "'" in valueString: + attributesString += ' %s="%s"' % (attributesKey, valueString) + else: + attributesString += " %s='%s'" % (attributesKey, valueString) + return attributesString + +def getBeginGeometryXMLOutput(elementNode=None): + 'Get the beginning of the string representation of this boolean geometry object info.' + output = getBeginXMLOutput() + attributes = {} + if elementNode != None: + documentElement = elementNode.getDocumentElement() + attributes = documentElement.attributes + addBeginXMLTag(attributes, 0, 'fabmetheus', output) + return output + +def getBeginXMLOutput(): + 'Get the beginning of the string representation of this object info.' + output = cStringIO.StringIO() + output.write("\n") + return output + +def getDictionaryWithoutList(dictionary, withoutList): + 'Get the dictionary without the keys in the list.' + dictionaryWithoutList = {} + for key in dictionary: + if key not in withoutList: + dictionaryWithoutList[key] = dictionary[key] + return dictionaryWithoutList + +def getEndGeometryXMLString(output): + 'Get the string representation of this object info.' + addEndXMLTag(0, 'fabmetheus', output) + return output.getvalue() diff --git a/SkeinPyPy/models/Screw Holder Bottom.stl b/SkeinPyPy/models/Screw Holder Bottom.stl new file mode 100644 index 0000000..f372a18 --- /dev/null +++ b/SkeinPyPy/models/Screw Holder Bottom.stl @@ -0,0 +1,2886 @@ +solid "Screw_Holder_Bottom"; Produced by Art of Illusion 2.4, Fri Oct 16 16:42:04 PDT 2009 +facet normal 0 -0.831469612304 0.555570233017 + outer loop + vertex 54.002309837942 24.804173569059 0.110046151702 + vertex 48.011309837942 24.804173569059 0.110046151702 + vertex 54.002309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 24.730643081307 0 + vertex 14.883210818105 25.830303993361 0 + vertex 15.262563132924 25.262563132924 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 60 23 0 + vertex 54.002309837942 24.730643081307 0 + vertex 60 40 0 + endloop +endfacet +facet normal 0 0.831469612301 0.555570233022 + outer loop + vertex 54.002309837942 15.269356918694 0 + vertex 48.011309837942 15.269356918694 0 + vertex 54.002309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.464705848856 10 0 + vertex 15.830303993361 11.883210818105 0 + vertex 16.5 11.75 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 52 30 0 + vertex 43.737436867076 27.737436867076 0 + vertex 43.169696006639 28.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 43.169696006639 15.116789181895 0 + vertex 42.5 15.25 0 + vertex 41.422110817733 17 0 + endloop +endfacet +facet normal 0 -0.831469612304 0.555570233017 + outer loop + vertex 48.011309837942 24.730643081307 0 + vertex 54.002309837942 24.730643081307 0 + vertex 48.011309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 52 30 0 + vertex 43.169696006639 28.116789181895 0 + vertex 42.5 28.25 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 60 40 0 + vertex 52 30 0 + vertex 52 40 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.883210818105 14.169696006639 0 + vertex 14.75 13.5 0 + vertex 12.002309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.883210818105 27.169696006639 0 + vertex 14.042553191489 30 0 + vertex 15.262563132924 27.737436867076 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.464705848856 10 0 + vertex 16.5 11.75 0 + vertex 17.169696006639 11.883210818105 0 + endloop +endfacet +facet normal 0 0.831469612301 0.555570233022 + outer loop + vertex 48.011309837942 15.195826430941 0.110046151702 + vertex 54.002309837942 15.195826430941 0.110046151702 + vertex 48.011309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.042553191489 30 0 + vertex 17.169696006639 28.116789181895 0 + vertex 16.5 28.25 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 15.269356918694 0 + vertex 14.75 13.5 0 + vertex 14.883210818105 12.830303993361 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 54.002309837942 17 0 + vertex 60 17 0 + vertex 54.002309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 52 10 0 + vertex 42.5 11.75 0 + vertex 43.169696006639 11.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 15.269356918694 0 + vertex 15.262563132924 14.737436867076 0 + vertex 14.883210818105 14.169696006639 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 52 10 0 + vertex 14.464705848856 10 0 + vertex 42.5 11.75 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.042553191489 30 0 + vertex 15.830303993361 28.116789181895 0 + vertex 15.262563132924 27.737436867076 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.464705848856 10 0 + vertex 15.262563132924 12.262563132924 0 + vertex 15.830303993361 11.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.042553191489 30 0 + vertex 16.5 28.25 0 + vertex 15.830303993361 28.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 54.002309837942 24.730643081307 0 + vertex 60 23 0 + vertex 54.002309837942 23 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 23 0 + vertex 16.5 24.75 0 + vertex 17.169696006639 24.883210818105 0 + endloop +endfacet +facet normal 0 0.831469612301 0.555570233022 + outer loop + vertex 6.011309837942 15.269356918694 0 + vertex 6.011309837942 15.195826430941 0.110046151702 + vertex 12.002309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.75 26.5 0 + vertex 12.002309837942 24.730643081307 0 + vertex 14.883210818105 27.169696006639 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 52 30 0 + vertex 60 40 0 + vertex 54.002309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 41.422110817733 17 0 + vertex 48.011309837942 17 0 + vertex 43.169696006639 15.116789181895 0 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 14.75 13.5 0 + vertex 14.883210818105 12.830303993361 0.77775 + vertex 14.883210818105 12.830303993361 0 + endloop +endfacet +facet normal -0 -0 -1 + outer loop + vertex 6.011309837942 17 0 + vertex 6.011309837942 15.269356918694 0 + vertex 0 17 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 41.422110817733 17 0 + vertex 41.830303993361 15.116789181895 0 + vertex 41.262563132924 14.737436867076 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.169696006639 11.883210818105 0 + vertex 17.737436867076 12.262563132924 0 + vertex 41.830303993361 11.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 6.011309837942 15.269356918694 0 + vertex 0 10 0 + vertex 0 17 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.883210818105 25.830303993361 0 + vertex 12.002309837942 24.730643081307 0 + vertex 14.75 26.5 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 48.011309837942 24.730643081307 0 + vertex 52 30 0 + vertex 54.002309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 48.011309837942 17 0 + vertex 43.737436867076 14.737436867076 0 + vertex 43.169696006639 15.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 42.5 15.25 0 + vertex 41.830303993361 15.116789181895 0 + vertex 41.422110817733 17 0 + endloop +endfacet +facet normal -0 -0 -1 + outer loop + vertex 12.002309837942 15.269356918694 0 + vertex 14.883210818105 12.830303993361 0 + vertex 6.011309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 0 23 0 + vertex 0 30 0 + vertex 6.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0.831469612301 0.555570233022 + outer loop + vertex 12.002309837942 15.269356918694 0 + vertex 6.011309837942 15.269356918694 0 + vertex 12.002309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 54.002309837942 15.269356918694 0 + vertex 60 17 0 + vertex 52 10 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 6.011309837942 23 0 + vertex 0 23 0 + vertex 6.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.116789181895 14.169696006639 0 + vertex 48.011309837942 15.269356918694 0 + vertex 44.25 13.5 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 18.116789181895 27.169696006639 0 + vertex 41.262563132924 25.262563132924 0 + vertex 18.25 26.5 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 18.116789181895 25.830303993361 0 + vertex 18.25 26.5 0 + vertex 41.830303993361 24.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.737436867076 14.737436867076 0 + vertex 41.262563132924 14.737436867076 0 + vertex 40.883210818105 14.169696006639 0 + endloop +endfacet +facet normal -0 -0 -1 + outer loop + vertex 41.262563132924 25.262563132924 0 + vertex 41.830303993361 24.883210818105 0 + vertex 18.25 26.5 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.737436867076 25.262563132924 0 + vertex 18.116789181895 25.830303993361 0 + vertex 41.830303993361 24.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.737436867076 12.262563132924 0 + vertex 18.116789181895 12.830303993361 0 + vertex 40.883210818105 12.830303993361 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.737436867076 12.262563132924 0 + vertex 41.262563132924 12.262563132924 0 + vertex 41.830303993361 11.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 41.262563132924 27.737436867076 0 + vertex 17.169696006639 28.116789181895 0 + vertex 41.830303993361 28.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 42.5 11.75 0 + vertex 14.464705848856 10 0 + vertex 17.169696006639 11.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.25 26.5 0 + vertex 44.116789181895 27.169696006639 0 + vertex 48.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 40.883210818105 12.830303993361 0 + vertex 41.262563132924 12.262563132924 0 + vertex 17.737436867076 12.262563132924 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.169696006639 24.883210818105 0 + vertex 17.737436867076 25.262563132924 0 + vertex 41.830303993361 24.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.116789181895 12.830303993361 0 + vertex 44.25 13.5 0 + vertex 48.011309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 18.116789181895 12.830303993361 0 + vertex 40.75 13.5 0 + vertex 40.883210818105 12.830303993361 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 18.25 13.5 0 + vertex 18.116789181895 14.169696006639 0 + vertex 40.883210818105 14.169696006639 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 41.262563132924 14.737436867076 0 + vertex 17.737436867076 14.737436867076 0 + vertex 17.169696006639 15.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 18.116789181895 14.169696006639 0 + vertex 17.737436867076 14.737436867076 0 + vertex 40.883210818105 14.169696006639 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.116789181895 25.830303993361 0 + vertex 44.25 26.5 0 + vertex 48.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 14.883210818105 14.169696006639 0.77775 + vertex 14.75 13.5 0.77775 + vertex 14.883210818105 14.169696006639 0 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 14.75 13.5 0.77775 + vertex 14.75 13.5 0 + vertex 14.883210818105 14.169696006639 0 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 41.830303993361 28.116789181895 0 + vertex 42.5 28.25 0.77775 + vertex 41.830303993361 28.116789181895 0.77775 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 15.830303993361 24.883210818105 0.77775 + vertex 15.830303993361 24.883210818105 0 + vertex 15.262563132924 25.262563132924 0 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 0 + outer loop + vertex 16.5 28.25 0 + vertex 17.169696006639 28.116789181895 0.77775 + vertex 16.5 28.25 0.77775 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 43.169696006639 24.883210818105 0 + vertex 42.5 24.75 0.77775 + vertex 43.169696006639 24.883210818105 0.77775 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 16.5 28.25 0.77775 + vertex 15.830303993361 28.116789181895 0.77775 + vertex 16.5 28.25 0 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 15.830303993361 28.116789181895 0.77775 + vertex 15.830303993361 28.116789181895 0 + vertex 16.5 28.25 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 0 23 0 + vertex 6.011309837942 23 0.77775 + vertex 0 23 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 60 0 0.77775 + vertex 52 10 0.77775 + vertex 52 0 0.77775 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 40.883210818105 27.169696006639 0 + vertex 40.883210818105 27.169696006639 0.77775 + vertex 40.75 26.5 0 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 17.169696006639 24.883210818105 0.77775 + vertex 17.169696006639 24.883210818105 0 + vertex 16.5 24.75 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 52 0 0 + vertex 52 0 0.77775 + vertex 52 10 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 34.416922535211 17 0.77775 + vertex 40.883210818105 12.830303993361 0.77775 + vertex 40.75 13.5 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 12.002309837942 23 0.77775 + vertex 12.002309837942 24.936988122232 0.77775 + vertex 12.002309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 42.5 24.75 0 + vertex 41.830303993361 24.883210818105 0.77775 + vertex 42.5 24.75 0.77775 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 60 0 0 + vertex 60 0 0.77775 + vertex 52 0 0 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 15.830303993361 28.116789181895 0.77775 + vertex 15.262563132924 27.737436867076 0.77775 + vertex 15.830303993361 28.116789181895 0 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 15.262563132924 27.737436867076 0.77775 + vertex 15.262563132924 27.737436867076 0 + vertex 15.830303993361 28.116789181895 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 6.011309837942 17 0.77775 + vertex 6.011309837942 17 0 + vertex 0 17 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 48.011309837942 17 0 + vertex 48.011309837942 17 0.77775 + vertex 48.011309837942 15.063011877768 0.77775 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 0 + outer loop + vertex 18.25 26.5 0 + vertex 18.25 26.5 0.77775 + vertex 18.116789181895 27.169696006639 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 15.262563132924 14.737436867076 0.77775 + vertex 15.830303993361 15.116789181895 0.77775 + vertex 15.455907969142 17 0.77775 + endloop +endfacet +facet normal 0 0.980785280403 0.195090322015 + outer loop + vertex 54.002309837942 15.063011877768 0.77775 + vertex 54.002309837942 15.195826430941 0.110046151702 + vertex 48.011309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 0 + outer loop + vertex 44.25 26.5 0 + vertex 44.25 26.5 0.77775 + vertex 44.116789181895 27.169696006639 0.77775 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 52 10 0 + vertex 15.306548743796 10 0.77775 + vertex 14.464705848856 10 0 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 40.75 26.5 0.77775 + vertex 40.883210818105 25.830303993361 0 + vertex 40.75 26.5 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 48.011309837942 15.063011877768 0.77775 + vertex 48.011309837942 15.195826430941 0.110046151702 + vertex 48.011309837942 15.269356918694 0 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 -0 + outer loop + vertex 44.116789181895 27.169696006639 0.77775 + vertex 43.737436867076 27.737436867076 0.77775 + vertex 44.116789181895 27.169696006639 0 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 -0 + outer loop + vertex 43.737436867076 27.737436867076 0.77775 + vertex 43.737436867076 27.737436867076 0 + vertex 44.116789181895 27.169696006639 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 52 10 0 + vertex 52 10 0.77775 + vertex 15.306548743796 10 0.77775 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 6.011309837942 23 0.77775 + vertex 6.011309837942 23 0 + vertex 6.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 34.416922535211 17 0.77775 + vertex 40.883210818105 14.169696006639 0.77775 + vertex 41.262563132924 14.737436867076 0.77775 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 14.75 26.5 0 + vertex 14.883210818105 25.830303993361 0.77775 + vertex 14.883210818105 25.830303993361 0 + endloop +endfacet +facet normal 0 -0.980785280403 0.195090322015 + outer loop + vertex 6.011309837942 24.936988122232 0.77775 + vertex 6.011309837942 24.804173569059 0.110046151702 + vertex 12.002309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 -0 + outer loop + vertex 18.116789181895 14.169696006639 0.77775 + vertex 17.737436867076 14.737436867076 0.77775 + vertex 18.116789181895 14.169696006639 0 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 -0 + outer loop + vertex 17.737436867076 14.737436867076 0.77775 + vertex 17.737436867076 14.737436867076 0 + vertex 18.116789181895 14.169696006639 0 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 42.5 15.25 0.77775 + vertex 41.830303993361 15.116789181895 0.77775 + vertex 42.5 15.25 0 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 41.830303993361 15.116789181895 0.77775 + vertex 41.830303993361 15.116789181895 0 + vertex 42.5 15.25 0 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 15.262563132924 14.737436867076 0.77775 + vertex 14.883210818105 14.169696006639 0.77775 + vertex 15.262563132924 14.737436867076 0 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 14.883210818105 14.169696006639 0.77775 + vertex 14.883210818105 14.169696006639 0 + vertex 15.262563132924 14.737436867076 0 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 0 + outer loop + vertex 43.169696006639 15.116789181895 0 + vertex 43.737436867076 14.737436867076 0.77775 + vertex 43.169696006639 15.116789181895 0.77775 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 14.75 13.5 0.77775 + vertex 14.883210818105 12.830303993361 0.77775 + vertex 14.75 13.5 0 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 41.262563132924 14.737436867076 0.77775 + vertex 40.883210818105 14.169696006639 0.77775 + vertex 40.883210818105 14.169696006639 0 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 42.5 11.75 0.77775 + vertex 43.169696006639 11.883210818105 0.77775 + vertex 43.169696006639 11.883210818105 0 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 -0 + outer loop + vertex 17.169696006639 15.116789181895 0.77775 + vertex 17.169696006639 15.116789181895 0 + vertex 17.737436867076 14.737436867076 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 12.002309837942 23 0.77775 + vertex 12.002309837942 24.730643081307 0 + vertex 12.002309837942 23 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 6.011309837942 23 0.77775 + vertex 0 23 0 + vertex 6.011309837942 23 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.116789181895 25.830303993361 0 + vertex 48.011309837942 23 0 + vertex 43.737436867076 25.262563132924 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 12.002309837942 15.195826430941 0.110046151702 + vertex 12.002309837942 17 0.77775 + vertex 12.002309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 0 10 0 + vertex 6.011309837942 15.269356918694 0 + vertex 14.883210818105 12.830303993361 0 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 -0 + outer loop + vertex 17.169696006639 28.116789181895 0.77775 + vertex 16.5 28.25 0 + vertex 17.169696006639 28.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 48.011309837942 24.730643081307 0 + vertex 44.116789181895 27.169696006639 0 + vertex 43.737436867076 27.737436867076 0 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 44.25 26.5 0.77775 + vertex 44.25 26.5 0 + vertex 44.116789181895 25.830303993361 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 40.75 26.5 0.77775 + vertex 37.474084379442 23 0.77775 + vertex 40.883210818105 25.830303993361 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 34.416922535211 17 0.77775 + vertex 40.75 13.5 0.77775 + vertex 40.883210818105 14.169696006639 0.77775 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 17.169696006639 11.883210818105 0.77775 + vertex 17.169696006639 11.883210818105 0 + vertex 16.5 11.75 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 43.737436867076 27.737436867076 0.77775 + vertex 52 30 0.77775 + vertex 41.866228156844 30 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 12.002309837942 15.063011877768 0.77775 + vertex 14.883210818105 12.830303993361 0.77775 + vertex 14.75 13.5 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 15.063011877768 0.77775 + vertex 48.011309837942 15.063011877768 0.77775 + vertex 52 10 0.77775 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 41.830303993361 24.883210818105 0.77775 + vertex 42.5 24.75 0 + vertex 41.830303993361 24.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 6.011309837942 24.730643081307 0 + vertex 14.883210818105 27.169696006639 0 + vertex 12.002309837942 24.730643081307 0 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 -0 + outer loop + vertex 43.737436867076 14.737436867076 0.77775 + vertex 43.169696006639 15.116789181895 0 + vertex 43.737436867076 14.737436867076 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 43.737436867076 27.737436867076 0.77775 + vertex 41.866228156844 30 0.77775 + vertex 43.169696006639 28.116789181895 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 34.416922535211 17 0.77775 + vertex 41.830303993361 15.116789181895 0.77775 + vertex 42.5 15.25 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 16.5 11.75 0.77775 + vertex 15.830303993361 11.883210818105 0.77775 + vertex 15.306548743796 10 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 0 23 0.77775 + vertex 0 30 0 + vertex 0 23 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 0 30 0 + vertex 0 23 0.77775 + vertex 0 30 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 52 10 0 + vertex 52 0 0.77775 + vertex 52 10 0.77775 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 17.169696006639 11.883210818105 0 + vertex 17.169696006639 11.883210818105 0.77775 + vertex 17.737436867076 12.262563132924 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 43.169696006639 28.116789181895 0.77775 + vertex 41.866228156844 30 0.77775 + vertex 42.5 28.25 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 14.042553191489 30 0 + vertex 41.866228156844 30 0.77775 + vertex 52 30 0 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 41.830303993361 11.883210818105 0.77775 + vertex 42.5 11.75 0 + vertex 41.830303993361 11.883210818105 0 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 15.262563132924 12.262563132924 0.77775 + vertex 15.830303993361 11.883210818105 0 + vertex 15.262563132924 12.262563132924 0 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 40.883210818105 25.830303993361 0 + vertex 40.75 26.5 0.77775 + vertex 40.883210818105 25.830303993361 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 18.116789181895 27.169696006639 0 + vertex 40.883210818105 25.830303993361 0 + vertex 41.262563132924 25.262563132924 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 42.5 28.25 0 + vertex 17.169696006639 28.116789181895 0 + vertex 14.042553191489 30 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.607403236621 30 0.77775 + vertex 15.262563132924 27.737436867076 0.77775 + vertex 15.830303993361 28.116789181895 0.77775 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 14.883210818105 25.830303993361 0.77775 + vertex 15.262563132924 25.262563132924 0.77775 + vertex 14.883210818105 25.830303993361 0 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 15.262563132924 25.262563132924 0.77775 + vertex 15.262563132924 25.262563132924 0 + vertex 14.883210818105 25.830303993361 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 48.011309837942 24.804173569059 0.110046151702 + vertex 48.011309837942 23 0.77775 + vertex 48.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 48.011309837942 24.730643081307 0 + vertex 43.737436867076 27.737436867076 0 + vertex 52 30 0 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 18.116789181895 25.830303993361 0 + vertex 18.116789181895 25.830303993361 0.77775 + vertex 18.25 26.5 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 54.002309837942 15.195826430941 0.110046151702 + vertex 54.002309837942 15.063011877768 0.77775 + vertex 54.002309837942 17 0.77775 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 41.830303993361 15.116789181895 0 + vertex 41.830303993361 15.116789181895 0.77775 + vertex 41.262563132924 14.737436867076 0.77775 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 -0 + outer loop + vertex 43.737436867076 27.737436867076 0.77775 + vertex 43.169696006639 28.116789181895 0.77775 + vertex 43.737436867076 27.737436867076 0 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 -0 + outer loop + vertex 43.169696006639 28.116789181895 0.77775 + vertex 43.169696006639 28.116789181895 0 + vertex 43.737436867076 27.737436867076 0 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 41.830303993361 11.883210818105 0.77775 + vertex 42.5 11.75 0.77775 + vertex 42.5 11.75 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 12.002309837942 23 0.77775 + vertex 15.830303993361 24.883210818105 0.77775 + vertex 15.262563132924 25.262563132924 0.77775 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 41.262563132924 14.737436867076 0.77775 + vertex 41.262563132924 14.737436867076 0 + vertex 41.830303993361 15.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 60 17 0 + vertex 60 0 0 + vertex 52 0 0 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 15.262563132924 25.262563132924 0 + vertex 15.262563132924 25.262563132924 0.77775 + vertex 15.830303993361 24.883210818105 0.77775 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 12.002309837942 23 0.77775 + vertex 12.002309837942 23 0 + vertex 17.379510917074 23 0.77775 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 6.011309837942 24.936988122232 0.77775 + vertex 6.011309837942 23 0.77775 + vertex 6.011309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.169696006639 11.883210818105 0 + vertex 41.830303993361 11.883210818105 0 + vertex 42.5 11.75 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 42.5 28.25 0 + vertex 14.042553191489 30 0 + vertex 52 30 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 17 0.77775 + vertex 43.169696006639 15.116789181895 0.77775 + vertex 43.737436867076 14.737436867076 0.77775 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 6.011309837942 15.269356918694 0 + vertex 6.011309837942 17 0.77775 + vertex 6.011309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 -0 + outer loop + vertex 18.116789181895 27.169696006639 0.77775 + vertex 18.116789181895 27.169696006639 0 + vertex 18.25 26.5 0 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 40.75 26.5 0 + vertex 40.883210818105 27.169696006639 0.77775 + vertex 40.75 26.5 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.607403236621 30 0.77775 + vertex 0 30 0.77775 + vertex 14.883210818105 27.169696006639 0.77775 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 42.5 11.75 0.77775 + vertex 43.169696006639 11.883210818105 0 + vertex 42.5 11.75 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.607403236621 30 0.77775 + vertex 15.830303993361 28.116789181895 0.77775 + vertex 16.5 28.25 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 52 30 0.77775 + vertex 52 30 0 + vertex 41.866228156844 30 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 60 17 0 + vertex 52 0 0 + vertex 52 10 0 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 0 + outer loop + vertex 44.116789181895 14.169696006639 0.77775 + vertex 43.737436867076 14.737436867076 0.77775 + vertex 43.737436867076 14.737436867076 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 14.607403236621 30 0.77775 + vertex 41.866228156844 30 0.77775 + vertex 14.042553191489 30 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.169696006639 11.883210818105 0.77775 + vertex 15.306548743796 10 0.77775 + vertex 17.737436867076 12.262563132924 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 12.002309837942 15.269356918694 0 + vertex 12.002309837942 17 0.77775 + vertex 12.002309837942 17 0 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 17.169696006639 24.883210818105 0.77775 + vertex 16.5 24.75 0 + vertex 16.5 24.75 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 23 0.77775 + vertex 43.169696006639 24.883210818105 0.77775 + vertex 42.5 24.75 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 6.011309837942 17 0.77775 + vertex 0 17 0 + vertex 0 17 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.607403236621 30 0.77775 + vertex 16.5 28.25 0.77775 + vertex 17.169696006639 28.116789181895 0.77775 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 18.116789181895 25.830303993361 0.77775 + vertex 17.737436867076 25.262563132924 0 + vertex 17.737436867076 25.262563132924 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 16.5 24.75 0.77775 + vertex 17.379510917074 23 0.77775 + vertex 17.169696006639 24.883210818105 0.77775 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 -0 + outer loop + vertex 44.116789181895 27.169696006639 0.77775 + vertex 44.116789181895 27.169696006639 0 + vertex 44.25 26.5 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 40.75 13.5 0 + vertex 18.25 13.5 0 + vertex 40.883210818105 14.169696006639 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.116789181895 25.830303993361 0 + vertex 48.011309837942 24.730643081307 0 + vertex 48.011309837942 23 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 0 10 0.77775 + vertex 0 10 0 + vertex 14.464705848856 10 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 15.306548743796 10 0.77775 + vertex 15.830303993361 11.883210818105 0.77775 + vertex 15.262563132924 12.262563132924 0.77775 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 0 + outer loop + vertex 43.169696006639 28.116789181895 0 + vertex 43.169696006639 28.116789181895 0.77775 + vertex 42.5 28.25 0.77775 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 17.169696006639 11.883210818105 0.77775 + vertex 16.5 11.75 0 + vertex 16.5 11.75 0.77775 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 0 + outer loop + vertex 18.25 13.5 0.77775 + vertex 18.116789181895 14.169696006639 0.77775 + vertex 18.116789181895 14.169696006639 0 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 17.169696006639 24.883210818105 0.77775 + vertex 17.737436867076 25.262563132924 0 + vertex 17.169696006639 24.883210818105 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 0 10 0.77775 + vertex 14.464705848856 10 0 + vertex 15.306548743796 10 0.77775 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 0 + outer loop + vertex 17.169696006639 15.116789181895 0.77775 + vertex 17.737436867076 14.737436867076 0 + vertex 17.737436867076 14.737436867076 0.77775 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 48.011309837942 23 0.77775 + vertex 48.011309837942 24.804173569059 0.110046151702 + vertex 48.011309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.830303993361 28.116789181895 0.77775 + vertex 41.866228156844 30 0.77775 + vertex 41.262563132924 27.737436867076 0.77775 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 16.5 11.75 0.77775 + vertex 15.830303993361 11.883210818105 0 + vertex 15.830303993361 11.883210818105 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.75 26.5 0.77775 + vertex 12.002309837942 24.936988122232 0.77775 + vertex 14.883210818105 25.830303993361 0.77775 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 -0 + outer loop + vertex 44.116789181895 14.169696006639 0.77775 + vertex 43.737436867076 14.737436867076 0 + vertex 44.116789181895 14.169696006639 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 12.002309837942 23 0.77775 + vertex 12.002309837942 24.804173569059 0.110046151702 + vertex 12.002309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 15.063011877768 0.77775 + vertex 60 17 0.77775 + vertex 54.002309837942 17 0.77775 + endloop +endfacet +facet normal 0 0.980785280403 0.195090322015 + outer loop + vertex 6.011309837942 15.063011877768 0.77775 + vertex 12.002309837942 15.195826430941 0.110046151702 + vertex 6.011309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 12.002309837942 23 0.77775 + vertex 17.379510917074 23 0.77775 + vertex 15.830303993361 24.883210818105 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 15.830303993361 24.883210818105 0.77775 + vertex 17.379510917074 23 0.77775 + vertex 16.5 24.75 0.77775 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 18.25 26.5 0.77775 + vertex 18.25 26.5 0 + vertex 18.116789181895 25.830303993361 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.262563132924 25.262563132924 0.77775 + vertex 37.474084379442 23 0.77775 + vertex 41.830303993361 24.883210818105 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.607403236621 30 0.77775 + vertex 14.883210818105 27.169696006639 0.77775 + vertex 15.262563132924 27.737436867076 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.737436867076 14.737436867076 0.77775 + vertex 15.455907969142 17 0.77775 + vertex 17.169696006639 15.116789181895 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.737436867076 14.737436867076 0.77775 + vertex 34.416922535211 17 0.77775 + vertex 15.455907969142 17 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 15.306548743796 10 0.77775 + vertex 15.262563132924 12.262563132924 0.77775 + vertex 14.883210818105 12.830303993361 0.77775 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 17.169696006639 24.883210818105 0.77775 + vertex 17.737436867076 25.262563132924 0.77775 + vertex 17.737436867076 25.262563132924 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 16.5 15.25 0.77775 + vertex 15.455907969142 17 0.77775 + vertex 15.830303993361 15.116789181895 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.830303993361 28.116789181895 0.77775 + vertex 42.5 28.25 0.77775 + vertex 41.866228156844 30 0.77775 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 18.116789181895 12.830303993361 0.77775 + vertex 18.25 13.5 0.77775 + vertex 18.25 13.5 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 60 17 0.77775 + vertex 60 17 0 + vertex 54.002309837942 17 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 15.455907969142 17 0.77775 + vertex 12.002309837942 17 0 + vertex 12.002309837942 17 0.77775 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 43.169696006639 11.883210818105 0 + vertex 43.737436867076 12.262563132924 0.77775 + vertex 43.737436867076 12.262563132924 0 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 43.169696006639 11.883210818105 0 + vertex 43.169696006639 11.883210818105 0.77775 + vertex 43.737436867076 12.262563132924 0.77775 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 -0 + outer loop + vertex 18.25 13.5 0.77775 + vertex 18.116789181895 14.169696006639 0 + vertex 18.25 13.5 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 42.5 28.25 0 + vertex 41.830303993361 28.116789181895 0 + vertex 17.169696006639 28.116789181895 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 48.011309837942 23 0.77775 + vertex 48.011309837942 23 0 + vertex 48.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.169696006639 15.116789181895 0.77775 + vertex 15.455907969142 17 0.77775 + vertex 16.5 15.25 0.77775 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 17.737436867076 12.262563132924 0.77775 + vertex 17.737436867076 12.262563132924 0 + vertex 17.169696006639 11.883210818105 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 52 40 0.77775 + vertex 60 23 0.77775 + vertex 60 40 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 40.75 13.5 0 + vertex 18.116789181895 12.830303993361 0 + vertex 18.25 13.5 0 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 0 + outer loop + vertex 43.169696006639 28.116789181895 0 + vertex 42.5 28.25 0.77775 + vertex 42.5 28.25 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 48.011309837942 23 0 + vertex 12.002309837942 23 0 + vertex 42.5 24.75 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 23 0.77775 + vertex 60 23 0.77775 + vertex 54.002309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 6.011309837942 23 0.77775 + vertex 6.011309837942 24.730643081307 0 + vertex 6.011309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 0 + outer loop + vertex 17.169696006639 15.116789181895 0 + vertex 16.5 15.25 0.77775 + vertex 16.5 15.25 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 6.011309837942 17 0.77775 + vertex 6.011309837942 15.063011877768 0.77775 + vertex 6.011309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 60 17 0.77775 + vertex 60 0 0 + vertex 60 17 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 60 0 0.77775 + vertex 60 0 0 + vertex 60 17 0.77775 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 15.262563132924 12.262563132924 0.77775 + vertex 15.830303993361 11.883210818105 0.77775 + vertex 15.830303993361 11.883210818105 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 52 10 0.77775 + vertex 44.116789181895 12.830303993361 0.77775 + vertex 43.737436867076 12.262563132924 0.77775 + endloop +endfacet +facet normal 0 0.980785280403 0.195090322015 + outer loop + vertex 6.011309837942 15.063011877768 0.77775 + vertex 12.002309837942 15.063011877768 0.77775 + vertex 12.002309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.169696006639 24.883210818105 0.77775 + vertex 17.379510917074 23 0.77775 + vertex 17.737436867076 25.262563132924 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 52 10 0.77775 + vertex 43.737436867076 12.262563132924 0.77775 + vertex 43.169696006639 11.883210818105 0.77775 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 42.5 28.25 0.77775 + vertex 41.830303993361 28.116789181895 0 + vertex 42.5 28.25 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 6.011309837942 15.269356918694 0 + vertex 6.011309837942 17 0 + vertex 6.011309837942 17 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 40.883210818105 25.830303993361 0.77775 + vertex 37.474084379442 23 0.77775 + vertex 41.262563132924 25.262563132924 0.77775 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 18.116789181895 12.830303993361 0.77775 + vertex 18.25 13.5 0 + vertex 18.116789181895 12.830303993361 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.737436867076 25.262563132924 0.77775 + vertex 17.379510917074 23 0.77775 + vertex 18.116789181895 25.830303993361 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 34.416922535211 17 0.77775 + vertex 41.262563132924 14.737436867076 0.77775 + vertex 41.830303993361 15.116789181895 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 0 30 0.77775 + vertex 6.011309837942 24.936988122232 0.77775 + vertex 12.002309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.116789181895 25.830303993361 0.77775 + vertex 17.379510917074 23 0.77775 + vertex 37.474084379442 23 0.77775 + endloop +endfacet +facet normal 0 -0.980785280403 0.195090322015 + outer loop + vertex 6.011309837942 24.936988122232 0.77775 + vertex 12.002309837942 24.804173569059 0.110046151702 + vertex 12.002309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 52 10 0.77775 + vertex 43.169696006639 11.883210818105 0.77775 + vertex 42.5 11.75 0.77775 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 41.262563132924 14.737436867076 0.77775 + vertex 40.883210818105 14.169696006639 0 + vertex 41.262563132924 14.737436867076 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 48.011309837942 23 0 + vertex 43.169696006639 24.883210818105 0 + vertex 43.737436867076 25.262563132924 0 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 44.25 26.5 0.77775 + vertex 44.116789181895 25.830303993361 0 + vertex 44.116789181895 25.830303993361 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 60 17 0.77775 + vertex 54.002309837942 17 0 + vertex 54.002309837942 17 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 48.011309837942 23 0 + vertex 42.5 24.75 0 + vertex 43.169696006639 24.883210818105 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 43.737436867076 27.737436867076 0.77775 + vertex 44.116789181895 27.169696006639 0.77775 + vertex 52 30 0.77775 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 18.116789181895 25.830303993361 0.77775 + vertex 18.116789181895 25.830303993361 0 + vertex 17.737436867076 25.262563132924 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 54.002309837942 15.195826430941 0.110046151702 + vertex 54.002309837942 17 0.77775 + vertex 54.002309837942 15.269356918694 0 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 14.883210818105 27.169696006639 0.77775 + vertex 14.75 26.5 0 + vertex 14.883210818105 27.169696006639 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 52 0 0.77775 + vertex 52 0 0 + vertex 60 0 0.77775 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 14.883210818105 27.169696006639 0.77775 + vertex 14.75 26.5 0.77775 + vertex 14.75 26.5 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 54.002309837942 15.269356918694 0 + vertex 54.002309837942 17 0.77775 + vertex 54.002309837942 17 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 16.5 11.75 0.77775 + vertex 15.306548743796 10 0.77775 + vertex 17.169696006639 11.883210818105 0.77775 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 0 + outer loop + vertex 17.169696006639 15.116789181895 0 + vertex 17.169696006639 15.116789181895 0.77775 + vertex 16.5 15.25 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 12.002309837942 15.195826430941 0.110046151702 + vertex 12.002309837942 15.063011877768 0.77775 + vertex 12.002309837942 17 0.77775 + endloop +endfacet +facet normal 0 0.980785280403 0.195090322015 + outer loop + vertex 54.002309837942 15.063011877768 0.77775 + vertex 48.011309837942 15.195826430941 0.110046151702 + vertex 48.011309837942 15.063011877768 0.77775 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 16.5 11.75 0.77775 + vertex 16.5 11.75 0 + vertex 15.830303993361 11.883210818105 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 48.011309837942 15.063011877768 0.77775 + vertex 48.011309837942 15.269356918694 0 + vertex 48.011309837942 17 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 24.936988122232 0.77775 + vertex 52 30 0.77775 + vertex 48.011309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 42.5 24.75 0.77775 + vertex 43.169696006639 24.883210818105 0 + vertex 42.5 24.75 0 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 14.75 26.5 0 + vertex 14.75 26.5 0.77775 + vertex 14.883210818105 25.830303993361 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.116789181895 14.169696006639 0 + vertex 43.737436867076 14.737436867076 0 + vertex 48.011309837942 17 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.116789181895 14.169696006639 0 + vertex 48.011309837942 17 0 + vertex 48.011309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.169696006639 24.883210818105 0 + vertex 41.830303993361 24.883210818105 0 + vertex 42.5 24.75 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.169696006639 24.883210818105 0 + vertex 42.5 24.75 0 + vertex 12.002309837942 23 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 52 40 0 + vertex 52 40 0.77775 + vertex 60 40 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 52 40 0 + vertex 60 40 0.77775 + vertex 60 40 0 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 0 + outer loop + vertex 18.116789181895 27.169696006639 0.77775 + vertex 17.737436867076 27.737436867076 0.77775 + vertex 17.737436867076 27.737436867076 0 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 -0 + outer loop + vertex 18.116789181895 27.169696006639 0.77775 + vertex 17.737436867076 27.737436867076 0 + vertex 18.116789181895 27.169696006639 0 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 15.830303993361 24.883210818105 0 + vertex 15.830303993361 24.883210818105 0.77775 + vertex 16.5 24.75 0.77775 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 15.830303993361 24.883210818105 0 + vertex 16.5 24.75 0.77775 + vertex 16.5 24.75 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 12.002309837942 24.936988122232 0.77775 + vertex 12.002309837942 23 0.77775 + vertex 15.262563132924 25.262563132924 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 12.002309837942 24.936988122232 0.77775 + vertex 15.262563132924 25.262563132924 0.77775 + vertex 14.883210818105 25.830303993361 0.77775 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 60 40 0.77775 + vertex 60 23 0.77775 + vertex 60 23 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 60 40 0.77775 + vertex 60 23 0 + vertex 60 40 0 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 15.262563132924 27.737436867076 0 + vertex 15.262563132924 27.737436867076 0.77775 + vertex 14.883210818105 27.169696006639 0.77775 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 15.262563132924 27.737436867076 0 + vertex 14.883210818105 27.169696006639 0.77775 + vertex 14.883210818105 27.169696006639 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 37.474084379442 23 0.77775 + vertex 48.011309837942 23 0.77775 + vertex 42.5 24.75 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 37.474084379442 23 0.77775 + vertex 42.5 24.75 0.77775 + vertex 41.830303993361 24.883210818105 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 41.262563132924 14.737436867076 0 + vertex 12.002309837942 17 0 + vertex 41.422110817733 17 0 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 0 + outer loop + vertex 43.169696006639 15.116789181895 0.77775 + vertex 42.5 15.25 0.77775 + vertex 42.5 15.25 0 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 -0 + outer loop + vertex 43.169696006639 15.116789181895 0.77775 + vertex 42.5 15.25 0 + vertex 43.169696006639 15.116789181895 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 15.063011877768 0.77775 + vertex 52 10 0.77775 + vertex 60 0 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 15.063011877768 0.77775 + vertex 60 0 0.77775 + vertex 60 17 0.77775 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 40.75 13.5 0.77775 + vertex 40.883210818105 12.830303993361 0.77775 + vertex 40.883210818105 12.830303993361 0 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 40.75 13.5 0.77775 + vertex 40.883210818105 12.830303993361 0 + vertex 40.75 13.5 0 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 0 + outer loop + vertex 17.737436867076 27.737436867076 0 + vertex 17.737436867076 27.737436867076 0.77775 + vertex 17.169696006639 28.116789181895 0.77775 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 0 + outer loop + vertex 17.737436867076 27.737436867076 0 + vertex 17.169696006639 28.116789181895 0.77775 + vertex 17.169696006639 28.116789181895 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.883210818105 27.169696006639 0.77775 + vertex 0 30 0.77775 + vertex 12.002309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.883210818105 27.169696006639 0.77775 + vertex 12.002309837942 24.936988122232 0.77775 + vertex 14.75 26.5 0.77775 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 43.737436867076 12.262563132924 0.77775 + vertex 44.116789181895 12.830303993361 0.77775 + vertex 44.116789181895 12.830303993361 0 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 43.737436867076 12.262563132924 0.77775 + vertex 44.116789181895 12.830303993361 0 + vertex 43.737436867076 12.262563132924 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 48.011309837942 23 0 + vertex 48.011309837942 23 0.77775 + vertex 37.474084379442 23 0.77775 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 16.5 15.25 0.77775 + vertex 15.830303993361 15.116789181895 0.77775 + vertex 15.830303993361 15.116789181895 0 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 16.5 15.25 0.77775 + vertex 15.830303993361 15.116789181895 0 + vertex 16.5 15.25 0 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 40.883210818105 12.830303993361 0.77775 + vertex 41.262563132924 12.262563132924 0.77775 + vertex 41.262563132924 12.262563132924 0 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 40.883210818105 12.830303993361 0.77775 + vertex 41.262563132924 12.262563132924 0 + vertex 40.883210818105 12.830303993361 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 17 0 + vertex 16.5 15.25 0 + vertex 15.830303993361 15.116789181895 0 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 43.737436867076 25.262563132924 0 + vertex 43.737436867076 25.262563132924 0.77775 + vertex 44.116789181895 25.830303993361 0.77775 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 43.737436867076 25.262563132924 0 + vertex 44.116789181895 25.830303993361 0.77775 + vertex 44.116789181895 25.830303993361 0 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 40.883210818105 14.169696006639 0.77775 + vertex 40.75 13.5 0.77775 + vertex 40.75 13.5 0 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 40.883210818105 14.169696006639 0.77775 + vertex 40.75 13.5 0 + vertex 40.883210818105 14.169696006639 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 60 23 0 + vertex 60 23 0.77775 + vertex 54.002309837942 23 0.77775 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 60 23 0 + vertex 54.002309837942 23 0.77775 + vertex 54.002309837942 23 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 0 30 0 + vertex 14.042553191489 30 0 + vertex 14.883210818105 27.169696006639 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 0 30 0 + vertex 14.883210818105 27.169696006639 0 + vertex 6.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 41.262563132924 25.262563132924 0 + vertex 41.262563132924 25.262563132924 0.77775 + vertex 41.830303993361 24.883210818105 0.77775 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 41.262563132924 25.262563132924 0 + vertex 41.830303993361 24.883210818105 0.77775 + vertex 41.830303993361 24.883210818105 0 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 41.830303993361 28.116789181895 0.77775 + vertex 41.262563132924 27.737436867076 0.77775 + vertex 41.262563132924 27.737436867076 0 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 41.830303993361 28.116789181895 0.77775 + vertex 41.262563132924 27.737436867076 0 + vertex 41.830303993361 28.116789181895 0 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 43.169696006639 24.883210818105 0.77775 + vertex 43.737436867076 25.262563132924 0.77775 + vertex 43.737436867076 25.262563132924 0 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 43.169696006639 24.883210818105 0.77775 + vertex 43.737436867076 25.262563132924 0 + vertex 43.169696006639 24.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 52 10 0 + vertex 43.169696006639 11.883210818105 0 + vertex 43.737436867076 12.262563132924 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 52 10 0 + vertex 43.737436867076 12.262563132924 0 + vertex 54.002309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 24.936988122232 0.77775 + vertex 60 23 0.77775 + vertex 52 40 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 24.936988122232 0.77775 + vertex 52 40 0.77775 + vertex 52 30 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 15.455907969142 17 0.77775 + vertex 34.416922535211 17 0.77775 + vertex 41.422110817733 17 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 15.455907969142 17 0.77775 + vertex 41.422110817733 17 0 + vertex 12.002309837942 17 0 + endloop +endfacet +facet normal 0 -0.831469612304 0.555570233017 + outer loop + vertex 12.002309837942 24.804173569059 0.110046151702 + vertex 6.011309837942 24.804173569059 0.110046151702 + vertex 6.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 -0.831469612304 0.555570233017 + outer loop + vertex 12.002309837942 24.804173569059 0.110046151702 + vertex 6.011309837942 24.730643081307 0 + vertex 12.002309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 15.262563132924 14.737436867076 0.77775 + vertex 15.455907969142 17 0.77775 + vertex 12.002309837942 17 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 15.262563132924 14.737436867076 0.77775 + vertex 12.002309837942 17 0.77775 + vertex 14.883210818105 14.169696006639 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 44.116789181895 25.830303993361 0.77775 + vertex 43.737436867076 25.262563132924 0.77775 + vertex 48.011309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 0 17 0.77775 + vertex 0 10 0.77775 + vertex 6.011309837942 15.063011877768 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 0 17 0.77775 + vertex 6.011309837942 15.063011877768 0.77775 + vertex 6.011309837942 17 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 17 0.77775 + vertex 34.416922535211 17 0.77775 + vertex 42.5 15.25 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 17 0.77775 + vertex 42.5 15.25 0.77775 + vertex 43.169696006639 15.116789181895 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.883210818105 14.169696006639 0.77775 + vertex 12.002309837942 17 0.77775 + vertex 12.002309837942 15.063011877768 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.883210818105 14.169696006639 0.77775 + vertex 12.002309837942 15.063011877768 0.77775 + vertex 14.75 13.5 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 6.011309837942 24.936988122232 0.77775 + vertex 0 30 0.77775 + vertex 0 23 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 6.011309837942 24.936988122232 0.77775 + vertex 0 23 0.77775 + vertex 6.011309837942 23 0.77775 + endloop +endfacet +facet normal -0 -0 -1 + outer loop + vertex 40.75 26.5 0 + vertex 40.883210818105 25.830303993361 0 + vertex 18.116789181895 27.169696006639 0 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 0 + outer loop + vertex 44.25 13.5 0.77775 + vertex 44.116789181895 14.169696006639 0.77775 + vertex 44.116789181895 14.169696006639 0 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 -0 + outer loop + vertex 44.25 13.5 0.77775 + vertex 44.116789181895 14.169696006639 0 + vertex 44.25 13.5 0 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 17.737436867076 12.262563132924 0 + vertex 17.737436867076 12.262563132924 0.77775 + vertex 18.116789181895 12.830303993361 0.77775 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 17.737436867076 12.262563132924 0 + vertex 18.116789181895 12.830303993361 0.77775 + vertex 18.116789181895 12.830303993361 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 0 10 0.77775 + vertex 15.306548743796 10 0.77775 + vertex 14.883210818105 12.830303993361 0.77775 + endloop +endfacet +facet normal 0 -0.980785280403 0.195090322015 + outer loop + vertex 54.002309837942 24.804173569059 0.110046151702 + vertex 54.002309837942 24.936988122232 0.77775 + vertex 48.011309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 -0.980785280403 0.195090322015 + outer loop + vertex 54.002309837942 24.804173569059 0.110046151702 + vertex 48.011309837942 24.936988122232 0.77775 + vertex 48.011309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 14.883210818105 12.830303993361 0 + vertex 14.883210818105 12.830303993361 0.77775 + vertex 15.262563132924 12.262563132924 0.77775 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 14.883210818105 12.830303993361 0 + vertex 15.262563132924 12.262563132924 0.77775 + vertex 15.262563132924 12.262563132924 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 54.002309837942 15.269356918694 0 + vertex 43.737436867076 12.262563132924 0 + vertex 44.116789181895 12.830303993361 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 54.002309837942 15.269356918694 0 + vertex 44.116789181895 12.830303993361 0 + vertex 48.011309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 0 30 0 + vertex 0 30 0.77775 + vertex 14.607403236621 30 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 0 30 0 + vertex 14.607403236621 30 0.77775 + vertex 14.042553191489 30 0 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 41.262563132924 12.262563132924 0 + vertex 41.262563132924 12.262563132924 0.77775 + vertex 41.830303993361 11.883210818105 0.77775 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 41.262563132924 12.262563132924 0 + vertex 41.830303993361 11.883210818105 0.77775 + vertex 41.830303993361 11.883210818105 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 0 10 0 + vertex 0 10 0.77775 + vertex 0 17 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 0 10 0 + vertex 0 17 0.77775 + vertex 0 17 0 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 44.116789181895 12.830303993361 0.77775 + vertex 44.25 13.5 0.77775 + vertex 44.25 13.5 0 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 44.116789181895 12.830303993361 0.77775 + vertex 44.25 13.5 0 + vertex 44.116789181895 12.830303993361 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 41.422110817733 17 0 + vertex 34.416922535211 17 0.77775 + vertex 48.011309837942 17 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 41.422110817733 17 0 + vertex 48.011309837942 17 0.77775 + vertex 48.011309837942 17 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 52 30 0.77775 + vertex 52 40 0.77775 + vertex 52 40 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 52 30 0.77775 + vertex 52 40 0 + vertex 52 30 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 15.830303993361 24.883210818105 0 + vertex 16.5 24.75 0 + vertex 12.002309837942 23 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 41.262563132924 27.737436867076 0 + vertex 40.883210818105 27.169696006639 0 + vertex 17.737436867076 27.737436867076 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 41.262563132924 27.737436867076 0 + vertex 17.737436867076 27.737436867076 0 + vertex 17.169696006639 28.116789181895 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 6.011309837942 15.063011877768 0.77775 + vertex 0 10 0.77775 + vertex 14.883210818105 12.830303993361 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 6.011309837942 15.063011877768 0.77775 + vertex 14.883210818105 12.830303993361 0.77775 + vertex 12.002309837942 15.063011877768 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 17 0.77775 + vertex 43.737436867076 14.737436867076 0.77775 + vertex 44.116789181895 14.169696006639 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 17 0.77775 + vertex 44.116789181895 14.169696006639 0.77775 + vertex 48.011309837942 15.063011877768 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 15.262563132924 25.262563132924 0 + vertex 15.830303993361 24.883210818105 0 + vertex 12.002309837942 23 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 15.262563132924 25.262563132924 0 + vertex 12.002309837942 23 0 + vertex 12.002309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 44.25 26.5 0.77775 + vertex 44.116789181895 25.830303993361 0.77775 + vertex 48.011309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 40.883210818105 27.169696006639 0 + vertex 40.75 26.5 0 + vertex 18.116789181895 27.169696006639 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 40.883210818105 27.169696006639 0 + vertex 18.116789181895 27.169696006639 0 + vertex 17.737436867076 27.737436867076 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 17 0 + vertex 41.262563132924 14.737436867076 0 + vertex 17.169696006639 15.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 17 0 + vertex 17.169696006639 15.116789181895 0 + vertex 16.5 15.25 0 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 15.830303993361 15.116789181895 0 + vertex 15.830303993361 15.116789181895 0.77775 + vertex 15.262563132924 14.737436867076 0.77775 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 15.830303993361 15.116789181895 0 + vertex 15.262563132924 14.737436867076 0.77775 + vertex 15.262563132924 14.737436867076 0 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 41.262563132924 27.737436867076 0.77775 + vertex 40.883210818105 27.169696006639 0.77775 + vertex 40.883210818105 27.169696006639 0 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 41.262563132924 27.737436867076 0.77775 + vertex 40.883210818105 27.169696006639 0 + vertex 41.262563132924 27.737436867076 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 48.011309837942 23 0 + vertex 37.474084379442 23 0.77775 + vertex 17.379510917074 23 0.77775 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 48.011309837942 23 0 + vertex 17.379510917074 23 0.77775 + vertex 12.002309837942 23 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 17 0 + vertex 15.830303993361 15.116789181895 0 + vertex 15.262563132924 14.737436867076 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 17 0 + vertex 15.262563132924 14.737436867076 0 + vertex 12.002309837942 15.269356918694 0 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 40.883210818105 25.830303993361 0 + vertex 40.883210818105 25.830303993361 0.77775 + vertex 41.262563132924 25.262563132924 0.77775 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 40.883210818105 25.830303993361 0 + vertex 41.262563132924 25.262563132924 0.77775 + vertex 41.262563132924 25.262563132924 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.883210818105 12.830303993361 0 + vertex 15.262563132924 12.262563132924 0 + vertex 14.464705848856 10 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.883210818105 12.830303993361 0 + vertex 14.464705848856 10 0 + vertex 0 10 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.737436867076 27.737436867076 0.77775 + vertex 40.883210818105 27.169696006639 0.77775 + vertex 41.262563132924 27.737436867076 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.262563132924 27.737436867076 0.77775 + vertex 17.169696006639 28.116789181895 0.77775 + vertex 17.737436867076 27.737436867076 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 23 0.77775 + vertex 48.011309837942 24.936988122232 0.77775 + vertex 43.737436867076 25.262563132924 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 23 0.77775 + vertex 43.737436867076 25.262563132924 0.77775 + vertex 43.169696006639 24.883210818105 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 15.063011877768 0.77775 + vertex 44.116789181895 14.169696006639 0.77775 + vertex 44.25 13.5 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 44.116789181895 27.169696006639 0.77775 + vertex 44.25 26.5 0.77775 + vertex 48.011309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 44.116789181895 27.169696006639 0.77775 + vertex 48.011309837942 24.936988122232 0.77775 + vertex 52 30 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.116789181895 27.169696006639 0.77775 + vertex 40.883210818105 27.169696006639 0.77775 + vertex 17.737436867076 27.737436867076 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 54.002309837942 23 0.77775 + vertex 54.002309837942 24.936988122232 0.77775 + vertex 54.002309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.262563132924 27.737436867076 0.77775 + vertex 41.866228156844 30 0.77775 + vertex 14.607403236621 30 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.262563132924 27.737436867076 0.77775 + vertex 14.607403236621 30 0.77775 + vertex 17.169696006639 28.116789181895 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 15.063011877768 0.77775 + vertex 44.25 13.5 0.77775 + vertex 44.116789181895 12.830303993361 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 15.063011877768 0.77775 + vertex 44.116789181895 12.830303993361 0.77775 + vertex 52 10 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.116789181895 14.169696006639 0.77775 + vertex 18.25 13.5 0.77775 + vertex 41.262563132924 12.262563132924 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.25 26.5 0.77775 + vertex 40.883210818105 27.169696006639 0.77775 + vertex 18.116789181895 27.169696006639 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 54.002309837942 24.730643081307 0 + vertex 54.002309837942 23 0 + vertex 54.002309837942 23 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 54.002309837942 24.730643081307 0 + vertex 54.002309837942 23 0.77775 + vertex 54.002309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.830303993361 11.883210818105 0.77775 + vertex 41.262563132924 12.262563132924 0.77775 + vertex 18.25 13.5 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.830303993361 11.883210818105 0.77775 + vertex 18.25 13.5 0.77775 + vertex 18.116789181895 12.830303993361 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.116789181895 12.830303993361 0.77775 + vertex 17.737436867076 12.262563132924 0.77775 + vertex 42.5 11.75 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.116789181895 12.830303993361 0.77775 + vertex 42.5 11.75 0.77775 + vertex 41.830303993361 11.883210818105 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.737436867076 12.262563132924 0.77775 + vertex 15.306548743796 10 0.77775 + vertex 52 10 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.737436867076 12.262563132924 0.77775 + vertex 52 10 0.77775 + vertex 42.5 11.75 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 40.883210818105 12.830303993361 0.77775 + vertex 18.116789181895 14.169696006639 0.77775 + vertex 41.262563132924 12.262563132924 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.25 26.5 0.77775 + vertex 40.75 26.5 0.77775 + vertex 40.883210818105 27.169696006639 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.116789181895 25.830303993361 0.77775 + vertex 37.474084379442 23 0.77775 + vertex 40.75 26.5 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.116789181895 25.830303993361 0.77775 + vertex 40.75 26.5 0.77775 + vertex 18.25 26.5 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 40.883210818105 12.830303993361 0.77775 + vertex 34.416922535211 17 0.77775 + vertex 17.737436867076 14.737436867076 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 40.883210818105 12.830303993361 0.77775 + vertex 17.737436867076 14.737436867076 0.77775 + vertex 18.116789181895 14.169696006639 0.77775 + endloop +endfacet +endsolid diff --git a/SkeinPyPy/models/Screw Holder.gts b/SkeinPyPy/models/Screw Holder.gts new file mode 100644 index 0000000..6399a12 --- /dev/null +++ b/SkeinPyPy/models/Screw Holder.gts @@ -0,0 +1,4493 @@ +737 2253 1502 Number of Vertices,Number of Edges,Number of Faces +-6.859863212247494 0.22628773741583247 9.642249999999999 Vertex Coordinates XYZ +-6.649863212247495 0.946287737415833 9.642249999999999 +-6.319863212247495 1.396287737415833 9.642249999999999 +-5.809863212247494 1.726287737415833 9.642249999999999 +-5.2998632122474945 1.8462877374158326 9.642249999999999 +-4.519863212247495 1.8462877374158326 9.642249999999999 +-4.009863212247495 1.636287737415833 9.642249999999999 +-3.6798632122474944 1.2762877374158328 9.642249999999999 +-3.4698632122474935 0.7962877374158329 9.642249999999999 +-3.3498632122474934 0.016287737415832892 9.642249999999999 +-3.3498632122474934 -0.37371226258416745 9.642249999999999 +-5.689863212247494 -0.4037122625841675 9.642249999999999 +-5.509863212247495 -0.9437122625841673 9.642249999999999 +-5.179863212247494 -1.2437122625841672 9.642249999999999 +-4.699863212247494 -1.3937122625841676 9.642249999999999 +-3.949863212247494 -1.3637122625841673 9.642249999999999 +-3.319863212247494 -1.1837122625841672 9.642249999999999 +-3.3498632122474934 -1.9637122625841674 9.642249999999999 +-4.279863212247495 -2.143712262584168 9.642249999999999 +-5.209863212247494 -2.143712262584168 9.642249999999999 +-5.929863212247494 -1.9037122625841678 9.642249999999999 +-6.409863212247495 -1.4837122625841674 9.642249999999999 +-6.679863212247494 -1.0337122625841673 9.642249999999999 +-6.829863212247495 -0.4337122625841675 9.642249999999999 +-5.4473632122474935 0.8687877374158329 9.642249999999999 +-4.457363212247493 0.8387877374158327 9.642249999999999 +-4.367363212247493 0.26878773741583245 9.642249999999999 +-5.627363212247492 0.3887877374158325 9.642249999999999 +-4.667363212247494 1.1087877374158324 9.642249999999999 +-5.627363212247492 0.2987877374158325 9.642249999999999 +-5.177363212247494 1.1087877374158324 9.642249999999999 +-4.009863212247495 1.636287737415833 11.322250000000002 +-4.519863212247496 1.8462877374158326 11.322250000000002 +-3.6798632122474944 1.276287737415834 11.32225 +-3.3498632122474934 -0.37371226258416734 11.32225 +-3.3498632122474934 0.016287737415832892 11.32225 +-5.689863212247495 -0.4037122625841676 11.32225 +-3.9498632122474935 -1.3637122625841673 11.32225 +-3.319863212247494 -1.1837122625841672 11.32225 +-3.3498632122474943 -1.9637122625841679 11.32225 +-6.859863212247496 0.2262877374158324 11.322249999999999 +-6.829863212247496 -0.4337122625841676 11.32225 +-6.319863212247493 1.396287737415832 11.322249999999999 +-5.809863212247494 1.726287737415833 11.32225 +-6.649863212247495 0.9462877374158329 11.32225 +-5.209863212247495 -2.143712262584168 11.32225 +-5.929863212247496 -1.9037122625841687 11.32225 +-5.179863212247494 -1.2437122625841672 11.32225 +-4.699863212247494 -1.3937122625841676 11.32225 +-4.279863212247495 -2.1437122625841676 11.32225 +-6.4098632122474966 -1.483712262584167 11.32225 +-6.679863212247495 -1.0337122625841677 11.32225 +-5.299863212247494 1.846287737415833 11.32225 +-3.4698632122474944 0.796287737415833 11.32225 +-5.509863212247496 -0.9437122625841675 11.32225 +-4.667363212247494 1.1087877374158326 11.32225 +-5.177363212247494 1.1087877374158317 11.32225 +-4.457363212247494 0.8387877374158329 11.32225 +-5.627363212247493 0.38878773741583256 11.32225 +-4.367363212247493 0.2687877374158326 11.32225 +-5.627363212247492 0.2987877374158324 11.32225 +-5.4473632122474935 0.8687877374158326 11.32225 +11.759863212247497 1.8856484886916096 9.64225 +12.899863212247496 1.8556484886916098 9.64225 +12.929863212247495 1.22564848869161 9.64225 +13.439863212247495 1.6756484886916105 9.64225 +13.889863212247496 1.8556484886916098 9.64225 +14.519863212247497 1.8556484886916098 9.64225 +14.999863212247497 1.5856484886916102 9.64225 +15.359863212247497 1.0756484886916096 9.64225 +15.539863212247496 0.3556484886916095 9.64225 +15.539863212247496 -0.60435151130839 9.64225 +15.329863212247497 -1.23435151130839 9.64225 +14.999863212247497 -1.6843515113083907 9.64225 +14.609863212247497 -1.9543515113083907 9.64225 +14.039863212247496 -2.1343515113083904 9.64225 +12.899863212247496 -2.194351511308391 9.64225 +12.869863212247497 -3.5143515113083907 9.64225 +11.759863212247497 -3.484351511308391 9.64225 +12.899863212247496 -1.3843515113083904 9.64225 +13.079863212247497 -1.4443515113083905 9.64225 +14.279863212247497 -0.9043515113083903 9.64225 +12.899863212247496 0.4456484886916098 9.64225 +13.379863212247496 0.8956484886916104 9.64225 +13.769863212247497 1.0156484886916095 9.64225 +14.369863212247497 -0.21435151130839014 9.64225 +14.279863212247497 0.5056484886916098 9.64225 +14.009863212247497 -1.2943515113083905 9.64225 +14.099863212247497 0.8656484886916099 9.64225 +13.649863212247496 -1.4443515113083905 9.64225 +11.759863212247497 1.8856484886916096 11.32225 +12.899863212247498 1.8556484886916098 11.32225 +12.929863212247488 1.2256484886916095 11.32225 +13.439863212247495 1.6756484886916105 11.32225 +13.889863212247494 1.8556484886916107 11.32225 +14.519863212247497 1.8556484886916098 11.32225 +14.999863212247497 1.5856484886916107 11.32225 +15.359863212247493 1.0756484886916098 11.32225 +15.5398632122475 0.35564848869160914 11.32225 +15.539863212247498 -0.6043515113083899 11.32225 +15.329863212247494 -1.234351511308391 11.32225 +14.999863212247503 -1.684351511308392 11.32225 +14.609863212247497 -1.9543515113083898 11.32225 +14.039863212247498 -2.1343515113083895 11.32225 +12.899863212247496 -2.194351511308391 11.32225 +12.869863212247498 -3.514351511308391 11.32225 +11.759863212247494 -3.4843515113083914 11.32225 +14.279863212247493 -0.9043515113083888 11.322250000000002 +14.009863212247494 -1.2943515113083883 11.322250000000002 +13.379863212247498 0.8956484886916118 11.32225 +13.769863212247497 1.0156484886916093 11.322249999999997 +12.899863212247496 0.4456484886916098 11.32225 +12.899863212247496 -1.38435151130839 11.32225 +14.36986321224749 -0.2143515113083907 11.32225 +14.279863212247497 0.5056484886916095 11.32225 +14.099863212247497 0.8656484886916107 11.32225 +13.649863212247496 -1.4443515113083905 11.32225 +13.0798632122475 -1.4443515113083896 11.32225 +2.270136787752506 3.2862877374158317 9.64225 +5.210136787752507 3.256287737415831 9.64225 +5.7801367877525065 3.0462877374158315 9.64225 +6.110136787752507 2.716287737415832 9.64225 +6.260136787752507 2.356287737415831 9.64225 +6.320136787752506 1.6662877374158316 9.64225 +6.140136787752507 1.0962877374158313 9.64225 +5.600136787752507 0.5562877374158314 9.64225 +5.270136787752507 0.3462877374158312 9.64225 +7.040136787752506 -2.1737122625841687 9.64225 +5.720136787752507 -2.143712262584169 9.64225 +4.220136787752507 0.04628773741583159 9.64225 +3.380136787752506 0.01628773741583156 9.64225 +3.350136787752507 -2.1737122625841687 9.64225 +2.270136787752506 -2.143712262584169 9.64225 +3.3801367877525057 2.5062877374158314 9.64225 +3.3801367877525057 0.8562877374158319 9.64225 +5.120136787752506 1.9962877374158312 9.64225 +4.520136787752507 0.9162877374158317 9.64225 +5.120136787752506 1.4562877374158316 9.64225 +3.920136787752506 0.8262877374158316 9.64225 +4.580136787752507 2.476287737415832 9.64225 +4.940136787752506 2.296287737415832 9.64225 +4.880136787752506 1.0962877374158313 9.64225 +2.2701367877525054 3.286287737415832 11.32225 +5.210136787752507 3.256287737415833 11.322250000000004 +5.780136787752506 3.046287737415832 11.32225 +6.1101367877525075 2.7162877374158323 11.32225 +6.260136787752506 2.356287737415831 11.32225 +6.320136787752505 1.6662877374158316 11.32225 +6.140136787752509 1.0962877374158313 11.32225 +5.600136787752508 0.5562877374158319 11.32225 +5.270136787752508 0.3462877374158302 11.32225 +7.040136787752505 -2.1737122625841687 11.32225 +5.7201367877525096 -2.143712262584169 11.32225 +4.220136787752508 0.046287737415831476 11.32225 +3.3801367877525066 0.01628773741583056 11.32225 +3.3501367877525063 -2.1737122625841683 11.32225 +2.2701367877525063 -2.143712262584169 11.322250000000002 +3.9201367877525066 0.8262877374158334 11.32225 +4.520136787752508 0.9162877374158318 11.32225 +4.880136787752507 1.096287737415831 11.32225 +5.120136787752507 1.9962877374158317 11.32225 +4.940136787752505 2.296287737415833 11.32225 +5.120136787752507 1.4562877374158316 11.32225 +3.380136787752506 0.8562877374158319 11.32225 +4.580136787752508 2.476287737415833 11.32225 +3.3801367877525057 2.506287737415832 11.32225 +-2.4198632122474937 1.876287737415832 9.64225 +-1.279863212247494 1.8462877374158322 9.64225 +-1.2498632122474946 1.2162877374158323 9.64225 +-0.7398632122474949 1.666287737415833 9.64225 +-0.2898632122474938 1.8462877374158322 9.64225 +0.3401367877525061 1.8462877374158322 9.64225 +0.8201367877525065 1.5762877374158326 9.64225 +1.1801367877525069 1.066287737415832 9.64225 +1.3601367877525057 0.34628773741583185 9.64225 +1.3601367877525057 -0.6137122625841677 9.64225 +1.1501367877525075 -1.2437122625841677 9.64225 +0.8201367877525065 -1.6937122625841683 9.64225 +0.43013678775250686 -1.9637122625841683 9.64225 +-0.13986321224749437 -2.143712262584168 9.64225 +-1.279863212247494 -2.2037122625841685 9.64225 +-1.3098632122474934 -3.5237122625841684 9.64225 +-2.4198632122474937 -3.4937122625841686 9.64225 +-1.0998632122474925 -1.453712262584168 9.64225 +0.10013678775250578 -0.9137122625841679 9.64225 +-1.279863212247494 0.43628773741583216 9.64225 +-0.7998632122474936 0.8862877374158328 9.64225 +-0.40986321224749395 1.0062877374158319 9.64225 +-1.279863212247494 -1.393712262584168 9.64225 +0.19013678775250686 -0.22371226258416776 9.64225 +0.10013678775250612 0.4962877374158322 9.64225 +-0.16986321224749384 -1.3037122625841682 9.64225 +-0.07986321224749288 0.8562877374158323 9.64225 +-0.5298632122474941 -1.453712262584168 9.64225 +-2.4198632122474937 1.876287737415832 11.32225 +-1.2798632122474942 1.8462877374158313 11.32225 +-1.2498632122474944 1.2162877374158318 11.32225 +-0.7398632122474951 1.6662877374158342 11.322249999999999 +-0.28986321224749373 1.8462877374158322 11.32225 +0.340136787752506 1.8462877374158322 11.32225 +0.8201367877525063 1.5762877374158322 11.32225 +1.180136787752507 1.0662877374158315 11.32225 +1.3601367877525052 0.34628773741583185 11.32225 +1.3601367877525052 -0.6137122625841679 11.32225 +1.1501367877525073 -1.2437122625841677 11.32225 +0.8201367877525064 -1.6937122625841687 11.322250000000002 +0.43013678775250686 -1.9637122625841683 11.32225 +-0.13986321224749487 -2.143712262584167 11.32225 +-1.2798632122474942 -2.203712262584169 11.32225 +-1.3098632122474934 -3.5237122625841684 11.32225 +-2.4198632122474937 -3.493712262584169 11.32225 +0.10013678775250634 -0.9137122625841673 11.32225 +-0.16986321224749368 -1.3037122625841677 11.32225 +-0.40986321224749384 1.0062877374158323 11.32225 +-1.2798632122474942 0.43628773741583216 11.32225 +-1.2798632122474942 -1.393712262584168 11.32225 +0.19013678775250709 -0.22371226258416776 11.32225 +0.10013678775250609 0.4962877374158318 11.32225 +-0.07986321224749288 0.8562877374158324 11.32225 +-0.5298632122474942 -1.453712262584168 11.32225 +-1.0998632122474927 -1.453712262584168 11.32225 +-0.7998632122474936 0.8862877374158331 11.32225 +-11.929863212247497 3.2862877374158317 9.64225 +-8.989863212247496 3.256287737415831 9.64225 +-8.419863212247495 3.0462877374158315 9.64225 +-8.089863212247495 2.716287737415832 9.64225 +-7.939863212247495 2.356287737415831 9.64225 +-7.879863212247496 1.6662877374158316 9.64225 +-8.059863212247494 1.0962877374158313 9.64225 +-8.599863212247495 0.5562877374158314 9.64225 +-8.929863212247495 0.3462877374158312 9.64225 +-7.159863212247496 -2.1737122625841687 9.64225 +-8.479863212247496 -2.143712262584169 9.64225 +-9.979863212247496 0.04628773741583159 9.64225 +-10.819863212247496 0.01628773741583156 9.64225 +-10.849863212247495 -2.1737122625841687 9.64225 +-11.929863212247497 -2.143712262584169 9.64225 +-10.819863212247496 2.5062877374158314 9.64225 +-10.819863212247496 0.8562877374158319 9.64225 +-9.079863212247496 1.9962877374158312 9.64225 +-9.679863212247495 0.9162877374158317 9.64225 +-9.079863212247496 1.4562877374158316 9.64225 +-10.279863212247495 0.8262877374158316 9.64225 +-9.619863212247495 2.476287737415832 9.64225 +-9.259863212247495 2.296287737415832 9.64225 +-9.319863212247496 1.0962877374158313 9.64225 +-11.9298632122475 3.2862877374158317 11.32225 +-8.989863212247498 3.2562877374158314 11.32225 +-8.419863212247495 3.046287737415831 11.32225 +-8.089863212247495 2.7162877374158314 11.32225 +-7.939863212247496 2.3562877374158306 11.322250000000002 +-7.879863212247495 1.666287737415833 11.32225 +-8.059863212247494 1.096287737415831 11.32225 +-8.599863212247495 0.5562877374158306 11.32225 +-8.929863212247497 0.3462877374158324 11.32225 +-7.159863212247495 -2.1737122625841683 11.32225 +-8.479863212247496 -2.143712262584169 11.32225 +-9.979863212247498 0.04628773741582992 11.32225 +-10.819863212247498 0.016287737415831227 11.32225 +-10.849863212247497 -2.1737122625841687 11.32225 +-11.929863212247499 -2.1437122625841694 11.32225 +-9.679863212247495 0.9162877374158314 11.32225 +-9.319863212247498 1.096287737415831 11.322250000000002 +-9.079863212247496 1.4562877374158316 11.32225 +-9.079863212247496 1.996287737415832 11.322250000000002 +-9.259863212247495 2.2962877374158324 11.32225 +-10.819863212247496 0.8562877374158329 11.322250000000002 +-9.619863212247497 2.476287737415832 11.32225 +-10.279863212247498 0.8262877374158322 11.32225 +-10.819863212247494 2.5062877374158314 11.32225 +30.0 15.073000000000004 7.822250000000002 +30.0 15.206210818105248 8.491946006638905 +30.0 15.585563132923546 9.059686867076463 +30.0 16.153303993361096 9.43903918189475 +30.0 16.823 9.572249999999997 +30.0 17.49269600663891 9.43903918189475 +30.0 18.06043686707646 9.059686867076458 +30.0 18.439789181894753 8.49194600663891 +30.0 18.573000000000004 7.822250000000006 +30.0 18.439789181894753 7.152553993361098 +22.000000000000007 18.43978918189476 7.152553993361091 +30.0 18.06043686707646 6.5848131329235455 +30.0 17.492696006638916 6.205460818105249 +30.0 16.823 6.072249999999999 +30.0 16.153303993361092 6.205460818105249 +30.0 15.585563132923546 6.584813132923542 +30.0 15.206210818105244 7.152553993361091 +22.000000000000007 15.073000000000006 7.822250000000001 +22.000000000000007 15.206210818105252 8.491946006638905 +22.000000000000007 15.585563132923546 9.05968686707646 +22.000000000000007 16.823000000000008 9.572249999999997 +22.000000000000007 16.153303993361096 9.439039181894755 +22.000000000000014 17.492696006638912 9.439039181894753 +22.000000000000007 18.060436867076454 9.059686867076454 +22.000000000000007 18.439789181894753 8.491946006638907 +22.0 18.57300000000001 7.822250000000001 +22.000000000000007 18.060436867076465 6.584813132923542 +22.000000000000007 17.492696006638912 6.20546081810525 +22.000000000000007 16.823 6.0722499999999995 +22.000000000000007 16.1533039933611 6.205460818105244 +22.000000000000007 15.585563132923546 6.584813132923544 +22.000000000000007 15.206210818105255 7.152553993361091 +22.000000000000007 15.073000000000002 -0.17774999999999785 +22.000000000000007 15.585563132923546 1.0596868670764614 +22.000000000000007 15.206210818105255 0.49194600663890586 +22.000000000000007 16.153303993361096 1.439039181894751 +22.000000000000007 16.823000000000004 1.5722499999999995 +22.000000000000007 17.492696006638912 1.4390391818947483 +22.000000000000007 18.06043686707646 1.059686867076457 +22.000000000000014 18.573000000000004 -0.1777500000000014 +22.000000000000007 18.43978918189476 0.49194600663890853 +22.000000000000007 18.060436867076458 -1.4151868670764545 +22.000000000000007 18.43978918189476 -0.8474460066389078 +22.000000000000007 17.492696006638912 -1.7945391818947485 +22.000000000000007 16.153303993361092 -1.7945391818947503 +22.000000000000007 16.823000000000004 -1.9277499999999996 +22.000000000000007 15.585563132923546 -1.4151868670764598 +22.000000000000007 15.206210818105253 -0.8474460066389087 +30.0 15.072999999999999 -0.17774999999999785 +30.0 15.206210818105257 0.49194600663890586 +30.0 15.58556313292355 1.0596868670764596 +30.0 16.153303993361096 1.4390391818947554 +30.0 16.823000000000004 1.5722499999999995 +30.0 18.060436867076465 1.0596868670764579 +30.0 17.492696006638912 1.4390391818947492 +30.0 18.439789181894753 0.49194600663890586 +30.0 18.573 -0.1777500000000014 +30.0 18.43978918189476 -0.8474460066389105 +30.0 17.492696006638912 -1.7945391818947511 +30.0 18.06043686707646 -1.4151868670764562 +30.0 16.823000000000004 -1.9277499999999996 +30.0 16.153303993361096 -1.7945391818947494 +30.0 15.585563132923548 -1.4151868670764598 +30.0 15.20621081810525 -0.8474460066389096 +29.999999999999993 -17.927 -0.1777500000000014 +22.000000000000007 -17.793789181894752 0.491946006638905 +30.0 -17.414436867076454 1.0596868670764552 +30.0 -16.846696006638904 1.4390391818947492 +30.0 -16.176999999999996 1.5722499999999995 +30.0 -15.507303993361093 1.4390391818947545 +30.0 -14.939563132923539 1.059686867076457 +29.999999999999993 -14.560210818105249 0.4919460066389041 +29.999999999999993 -14.427 -0.17774999999999963 +30.0 -14.560210818105247 -0.8474460066389069 +30.0 -14.93956313292354 -1.4151868670764562 +30.0 -15.507303993361088 -1.794539181894752 +30.0 -16.177 -1.9277500000000023 +30.0 -16.846696006638904 -1.7945391818947556 +30.0 -17.414436867076454 -1.4151868670764527 +29.999999999999993 -17.793789181894756 -0.8474460066389087 +30.0 -17.793789181894756 0.4919460066389023 +22.000000000000007 -17.927000000000003 -0.17774999999999785 +22.000000000000007 -17.414436867076454 1.059686867076456 +22.000000000000007 -16.846696006638904 1.4390391818947519 +22.000000000000007 -16.176999999999996 1.5722499999999995 +22.000000000000007 -15.507303993361091 1.4390391818947519 +22.000000000000007 -14.939563132923539 1.0596868670764579 +22.000000000000007 -14.560210818105244 0.49194600663890764 +22.000000000000007 -14.427000000000001 -0.1777500000000023 +22.000000000000007 -14.560210818105245 -0.8474460066389105 +22.000000000000007 -14.939563132923539 -1.4151868670764625 +22.000000000000007 -15.507303993361091 -1.7945391818947538 +22.000000000000007 -16.177 -1.9277500000000014 +22.000000000000007 -16.846696006638904 -1.79453918189476 +22.000000000000007 -17.414436867076454 -1.415186867076459 +22.000000000000007 -17.79378918189475 -0.8474460066389069 +22.0 -17.92699999999999 7.822249999999998 +22.0 -17.793789181894745 8.491946006638901 +22.0 -17.414436867076454 9.059686867076454 +22.000000000000007 -16.8466960066389 9.43903918189475 +22.000000000000007 -16.177 9.572249999999999 +22.000000000000007 -15.507303993361091 9.439039181894753 +22.000000000000007 -14.939563132923539 9.059686867076458 +22.000000000000007 -14.560210818105242 8.491946006638909 +22.0 -14.426999999999989 7.822249999999997 +22.0 -14.560210818105247 7.152553993361094 +22.000000000000007 -14.939563132923539 6.584813132923543 +22.000000000000007 -15.50730399336109 6.205460818105245 +22.000000000000007 -16.177 6.072249999999999 +22.000000000000007 -16.846696006638904 6.205460818105244 +22.000000000000007 -17.414436867076454 6.584813132923531 +22.0 -17.793789181894745 7.15255399336109 +30.0 -17.92699999999999 7.822249999999999 +29.999999999999993 -17.79378918189475 8.491946006638901 +30.0 -16.8466960066389 9.439039181894747 +29.999999999999993 -17.41443686707645 9.059686867076447 +30.0 -16.177000000000003 9.572249999999999 +30.0 -15.507303993361093 9.439039181894755 +30.0 -14.939563132923542 9.05968686707646 +29.999999999999993 -14.560210818105244 8.49194600663891 +30.0 -14.426999999999994 7.822249999999999 +30.0 -14.560210818105247 7.15255399336109 +30.0 -15.507303993361091 6.205460818105246 +30.0 -14.939563132923539 6.58481313292354 +30.0 -16.846696006638908 6.20546081810525 +30.0 -16.177000000000003 6.072249999999999 +30.0 -17.414436867076454 6.5848131329235375 +29.999999999999993 -17.793789181894738 7.152553993361091 +22.000000000000007 20.323 11.32225 +-15.957446808510642 10.323 -3.6777500000000014 +22.000000000000007 10.323 -3.677749999999998 +22.000000000000007 20.323 -3.6777499999999996 +-30.0 10.323 11.32225 +22.000000000000007 10.323 11.32225 +18.512888012183033 10.323 -3.6777500000000014 +3.649215653894089 10.323 11.32225 +-30.0 10.323 -3.6777500000000014 +22.000000000000007 -19.677 -3.677750000000003 +-15.535294151144047 -9.677 -3.677749999999998 +-15.551298997002412 -9.677 11.32225 +-30.0 -9.677 11.32225 +22.000000000000007 -9.677 -3.677749999999998 +22.000000000000007 -9.677 11.32225 +14.771385376467595 -9.677000000000001 11.322249999999997 +-30.0 -9.677 -3.6777500000000014 +22.000000000000007 -19.677 11.32225 +14.25 -6.1770000000000005 11.32225 +14.116789181894752 -6.846696006638907 11.32225 +13.737436867076458 -7.414436867076454 11.32225 +13.169696006638908 -7.793789181894753 11.32225 +12.5 -7.927000000000002 11.32225 +11.830303993361095 -7.793789181894753 -3.677749999999998 +10.883210818105248 -6.846696006638907 11.32225 +10.750000000000002 -6.177000000000002 11.32225 +10.883210818105248 -5.507303993361092 11.32225 +11.262563132923546 -4.939563132923541 11.32225 +11.830303993361092 -4.56021081810525 11.32225 +12.5 -4.4270000000000005 11.32225 +13.169696006638912 -4.560210818105252 11.32225 +13.737436867076458 -4.939563132923545 11.32225 +14.116789181894745 -5.507303993361094 11.32225 +11.262563132923546 -7.414436867076459 -3.677749999999998 +14.116789181894752 -6.846696006638906 -3.677749999999998 +14.250000000000004 -6.177000000000001 -3.677749999999998 +13.737436867076454 -7.414436867076457 -3.677749999999998 +12.5 -7.9270000000000005 -3.677749999999998 +13.169696006638908 -7.793789181894753 -3.677749999999998 +11.262563132923546 -7.4144368670764615 11.322250000000004 +11.830303993361099 -7.793789181894749 11.322249999999997 +10.883210818105246 -6.846696006638908 -3.6777500000000014 +10.749999999999998 -6.177000000000003 -3.6777500000000014 +10.883210818105246 -5.50730399336109 -3.677749999999998 +11.262563132923546 -4.939563132923544 -3.677749999999998 +11.830303993361092 -4.560210818105249 -3.677749999999998 +12.5 -4.427 -3.6777500000000014 +13.169696006638912 -4.560210818105251 -3.6777500000000014 +13.737436867076458 -4.939563132923547 -3.6777500000000014 +14.116789181894745 -5.507303993361091 -3.677749999999998 +14.25 6.823000000000001 11.32225 +14.116789181894752 6.153303993361095 11.32225 +13.737436867076458 5.585563132923542 11.32225 +13.169696006638908 5.206210818105249 11.32225 +12.5 5.0729999999999995 11.32225 +11.830303993361092 5.206210818105249 11.32225 +11.262563132923542 5.58556313292354 11.32225 +10.88321081810525 6.153303993361093 11.32225 +10.75 6.823000000000001 11.32225 +10.88321081810525 7.4926960066389094 11.32225 +11.262563132923542 8.060436867076458 11.32225 +11.830303993361092 8.439789181894751 11.32225 +12.500000000000004 8.572999999999997 11.32225 +13.169696006638908 8.43978918189475 11.32225 +13.737436867076454 8.060436867076458 11.32225 +14.116789181894745 7.492696006638908 11.32225 +14.116789181894756 6.153303993361094 -3.677749999999998 +14.250000000000004 6.822999999999999 -3.677749999999998 +13.169696006638908 5.206210818105248 -3.677749999999998 +13.737436867076454 5.585563132923541 -3.6777500000000014 +11.830303993361092 5.206210818105244 -3.677749999999998 +12.500000000000007 5.073 -3.677749999999998 +11.262563132923539 5.585563132923539 -3.6777500000000014 +10.883210818105248 6.153303993361094 -3.677749999999998 +10.75 6.823000000000002 -3.677749999999998 +10.88321081810525 7.49269600663891 -3.677749999999998 +11.262563132923542 8.060436867076458 -3.677749999999998 +11.830303993361092 8.439789181894751 -3.677749999999998 +13.169696006638908 8.439789181894747 -3.677749999999998 +12.500000000000007 8.572999999999999 -3.677749999999998 +13.73743686707645 8.060436867076456 -3.677749999999998 +14.116789181894752 7.492696006638909 -3.677749999999998 +-11.75 6.8229999999999995 11.32225 +-11.883210818105248 6.153303993361091 11.32225 +-12.262563132923546 5.585563132923542 11.32225 +-12.830303993361092 5.206210818105249 11.32225 +-13.5 5.0729999999999995 11.32225 +-14.169696006638908 5.206210818105247 11.32225 +-14.737436867076461 5.585563132923542 11.32225 +-15.116789181894752 6.153303993361095 11.32225 +-15.25 6.8229999999999995 11.32225 +-15.116789181894749 7.492696006638904 11.32225 +-14.737436867076454 8.060436867076458 11.32225 +-14.169696006638908 8.439789181894753 11.32225 +-13.500000000000007 8.573 11.32225 +-12.830303993361099 8.439789181894755 11.32225 +-12.262563132923542 8.060436867076463 11.32225 +-11.883210818105244 7.492696006638908 11.32225 +-11.749999999999996 6.823 -3.6777500000000014 +-11.883210818105255 6.153303993361096 -3.677749999999998 +-12.830303993361095 5.2062108181052515 -3.677750000000005 +-12.262563132923542 5.5855631329235464 -3.677749999999998 +-13.5 5.073 -3.6777500000000014 +-14.169696006638908 5.20621081810525 -3.677749999999998 +-15.116789181894756 6.153303993361094 -3.6777500000000014 +-14.737436867076461 5.5855631329235464 -3.6777500000000014 +-15.25 6.823000000000004 -3.6777500000000014 +-15.116789181894749 7.492696006638907 -3.677749999999998 +-14.169696006638908 8.439789181894753 -3.6777500000000014 +-14.737436867076454 8.06043686707646 -3.677749999999998 +-13.500000000000007 8.573 -3.6777500000000014 +-12.830303993361099 8.439789181894756 -3.677749999999998 +-12.262563132923546 8.060436867076461 -3.677749999999998 +-11.883210818105248 7.492696006638909 -3.6777500000000014 +-11.75 -6.177000000000002 11.32225 +-11.883210818105248 -6.846696006638909 11.32225 +-12.262563132923546 -7.4144368670764615 11.32225 +-12.830303993361095 -7.793789181894753 11.32225 +-13.5 -7.927000000000004 11.32225 +-14.169696006638912 -7.793789181894753 11.32225 +-14.737436867076458 -7.414436867076458 11.32225 +-15.116789181894752 -6.846696006638909 11.32225 +-15.25 -6.177000000000002 11.32225 +-15.116789181894752 -5.507303993361099 11.32225 +-14.737436867076461 -4.9395631329235465 11.32225 +-14.169696006638908 -4.56021081810525 11.32225 +-13.5 -4.4270000000000005 11.32225 +-12.830303993361095 -4.56021081810525 11.32225 +-12.262563132923539 -4.939563132923539 11.32225 +-11.88321081810524 -5.507303993361094 11.32225 +-11.883210818105248 -6.846696006638911 -3.677749999999998 +-11.750000000000004 -6.177 -3.6777500000000014 +-12.262563132923546 -7.414436867076457 -3.677749999999998 +-12.830303993361095 -7.7937891818947564 -3.677749999999998 +-14.169696006638908 -7.793789181894753 -3.6777500000000014 +-13.5 -7.927000000000008 -3.677749999999998 +-14.737436867076454 -7.414436867076457 -3.677749999999998 +-15.116789181894752 -6.846696006638908 -3.677749999999998 +-15.250000000000004 -6.177 -3.6777500000000014 +-15.116789181894749 -5.507303993361097 -3.6777500000000014 +-14.737436867076461 -4.939563132923546 -3.677749999999998 +-14.169696006638908 -4.560210818105251 -3.677749999999998 +-13.5 -4.427 -3.6777500000000014 +-12.830303993361095 -4.560210818105254 -3.6777500000000014 +-12.262563132923546 -4.93956313292354 -3.677749999999998 +-11.88321081810524 -5.507303993361091 -3.677749999999998 +-17.997690162057708 -2.677 2.3222500000000004 +-17.997690162057708 3.323 2.3222500000000004 +30.0 3.323 2.3222500000000004 +30.0 3.323 -3.6777500000000014 +-23.988690162057708 3.323 2.3222500000000004 +-23.988690162057708 -2.677 2.3222500000000004 +30.0 -2.677 -3.6777500000000014 +18.011309837942285 -2.677 2.3222500000000004 +30.0 -2.677 2.3222500000000004 +18.011309837942285 3.323 -3.677749999999998 +-30.0 3.323 -3.6777500000000014 +-17.997690162057708 3.323 -3.6777500000000014 +-23.988690162057708 3.323 -3.677749999999998 +11.422110817733461 -2.677 -3.677749999999998 +-17.997690162057708 -2.677 -3.6777500000000014 +-30.0 -2.677 2.3222500000000004 +-23.988690162057708 -2.677 -3.677749999999998 +-30.0 3.323 2.3222500000000004 +24.002309837942285 -2.677 2.3222500000000004 +18.011309837942285 3.323 2.3222500000000004 +24.002309837942285 3.323 -3.6777500000000014 +-30.0 -2.677 -3.6777500000000014 +24.002309837942285 -2.677 -3.6777500000000014 +24.002309837942285 3.323 2.3222500000000004 +18.011309837942285 -2.677 -3.677749999999998 +-17.997690162057708 -4.876999999999995 -1.5777500000000053 +-17.997690162057708 -4.481173569058687 0.41220384829846246 +-17.997690162057708 -3.3539552621700417 2.099205262170041 +-17.997690162057708 -1.6669538482984616 3.226423569058687 +-17.997690162057708 0.3230000000000051 3.6222499999999958 +-17.997690162057708 2.312953848298472 3.226423569058687 +-17.997690162057708 3.9999552621700523 2.099205262170044 +-17.997690162057708 5.127173569058697 0.41220384829846424 +-17.997690162057708 5.523000000000007 -1.5777500000000053 +-17.997690162057708 5.127173569058698 -3.5677038482984695 +-17.997690162057708 -4.481173569058685 -3.5677038482984766 +-23.988690162057708 -4.876999999999992 -1.5777500000000053 +-23.988690162057708 -4.481173569058684 0.41220384829846246 +-23.988690162057708 -3.35395526217004 2.099205262170041 +-23.988690162057708 -1.6669538482984607 3.226423569058687 +-23.988690162057708 0.323000000000006 3.6222499999999958 +-23.988690162057708 2.312953848298473 3.226423569058687 +-23.988690162057708 3.999955262170054 2.099205262170044 +-23.988690162057708 5.127173569058697 0.41220384829846246 +-23.988690162057708 5.523000000000007 -1.5777500000000053 +-23.988690162057708 5.1271735690587 -3.567703848298473 +-23.988690162057708 -4.481173569058683 -3.5677038482984766 +-17.997690162057708 5.053643081306509 -3.6777500000000014 +-17.997690162057708 -4.4076430813064995 -3.6777500000000014 +-23.988690162057708 5.0536430813065145 -3.677749999999998 +-23.988690162057708 -4.4076430813064995 -3.677749999999998 +30.0 20.323 -3.6777499999999996 +30.0 20.323 11.32225 +30.0 -19.677 -3.677750000000003 +30.0 -19.677 11.32225 +24.002309837942285 5.053643081306509 -3.6777500000000014 +18.011309837942285 5.053643081306513 -3.677749999999998 +24.002309837942285 -4.4076430813064995 -3.6777500000000014 +18.011309837942285 -4.407643081306494 -3.677749999999998 +24.002309837942285 -4.876999999999995 -1.5777500000000053 +24.002309837942285 -4.481173569058687 0.41220384829846246 +24.002309837942285 -3.3539552621700417 2.099205262170041 +24.002309837942285 -1.6669538482984616 3.226423569058687 +24.002309837942285 0.3230000000000051 3.6222499999999958 +24.002309837942285 2.312953848298472 3.226423569058687 +24.002309837942285 3.9999552621700523 2.099205262170044 +24.002309837942285 5.127173569058697 0.41220384829846424 +24.002309837942285 5.523000000000007 -1.5777500000000053 +24.002309837942285 5.127173569058698 -3.5677038482984695 +24.002309837942285 -4.481173569058685 -3.5677038482984766 +18.011309837942285 -4.876999999999992 -1.5777500000000053 +18.011309837942285 -4.481173569058684 0.41220384829846246 +18.011309837942285 -3.35395526217004 2.099205262170041 +18.011309837942285 -1.6669538482984607 3.226423569058687 +18.011309837942285 0.323000000000006 3.6222499999999958 +18.011309837942285 2.312953848298473 3.226423569058687 +18.011309837942285 3.999955262170054 2.099205262170044 +18.011309837942285 5.127173569058697 0.41220384829846246 +18.011309837942285 5.523000000000007 -1.5777500000000053 +18.011309837942285 5.1271735690587 -3.567703848298473 +18.011309837942285 -4.481173569058683 -3.5677038482984766 +-12.919863212247494 1.006287737415828 11.322250000000007 +-13.189863212247493 1.4862877374158323 11.32225 +-15.109863212247499 3.4362877374158303 11.32225 +-16.999863212247483 1.5762877374158215 11.32225 +-17.53986321224749 0.19628773741582983 11.322249999999999 +-12.739863212247494 -0.3737122625841701 11.322250000000006 +-12.739863212247498 0.34628773741582886 11.322250000000004 +-12.979863212247494 -1.0937122625841718 11.322250000000004 +-13.999863212247496 -2.08371226258417 11.32225 +-14.689863212247495 -2.3237122625841695 11.32225 +-13.459863212247495 -1.7237122625841699 11.32225 +-16.939863212247495 -1.57371226258417 11.32225 +-17.35986321224749 0.9762877374158305 11.32225 +-16.3398632122475 -2.05371226258417 11.322250000000002 +-15.649863212247492 -2.3237122625841677 11.32225 +-17.329863212247496 -0.9137122625841699 11.32224999999999 +-17.509863212247502 -0.25371226258417345 11.322249999999986 +-17.539863212247496 0.1962877374158304 9.64225 +-17.359863212247497 0.9762877374158304 9.64225 +-16.999863212247497 1.5762877374158304 9.64225 +-15.109863212247495 3.43628773741583 9.64225 +-13.189863212247493 1.48628773741583 9.64225 +-12.919863212247494 1.0062877374158297 9.64225 +-12.739863212247494 0.34628773741582985 9.64225 +-12.739863212247494 -0.3737122625841701 9.64225 +-12.979863212247494 -1.09371226258417 9.64225 +-13.459863212247495 -1.7237122625841703 9.64225 +-13.999863212247494 -2.08371226258417 9.64225 +-14.689863212247495 -2.32371226258417 9.64225 +-15.649863212247494 -2.32371226258417 9.64225 +-16.339863212247494 -2.05371226258417 9.64225 +-16.939863212247495 -1.57371226258417 9.64225 +-17.329863212247496 -0.9137122625841699 9.64225 +-17.509863212247495 -0.25371226258417 9.64225 +7.756738351254477 -1.9418924731182776 11.32225 +7.60673835125448 -0.38189247311828 11.32225 +7.996738351254475 -0.05189247311827905 11.322249999999997 +8.14673835125448 -2.18189247311828 11.32225 +10.906738351254479 -1.4618924731182803 11.32225 +8.746738351254475 -2.2418924731182805 11.32225 +9.226738351254475 -2.0918924731182775 11.322249999999997 +7.786738351254477 0.7881075268817213 11.32225 +7.426738351254476 -0.891892473118279 11.32225 +7.426738351254478 -1.1618924731182783 11.322249999999993 +11.146738351254479 -1.4918924731182788 11.32225 +11.146738351254477 -2.1518924731182794 11.32225 +8.56673835125448 1.7481075268817183 11.322249999999999 +7.816738351254481 1.5381075268817215 11.32225 +8.05673835125448 0.9081075268817214 11.322249999999999 +8.776738351254474 -1.4318924731182783 11.32225 +8.536738351254476 0.12810752688172422 11.322249999999997 +9.61673835125448 -1.7018924731182792 11.322250000000002 +8.83673835125448 1.1181075268817215 11.322249999999997 +7.546738351254474 -1.6418924731182787 11.322249999999997 +8.536738351254476 -1.1618924731182818 11.32225 +9.316738351254479 1.0581075268817208 11.32225 +9.256738351254473 -1.4318924731182792 11.322250000000002 +10.36673835125448 -2.2418924731182828 11.322249999999997 +10.096738351254476 1.6581075268817225 11.322249999999997 +8.686738351254476 -0.5618924731182797 11.322249999999997 +8.506738351254475 -0.8618924731182792 11.32225 +9.946738351254476 -2.091892473118282 11.322249999999997 +9.496738351254475 1.8081075268817197 11.32225 +9.616738351254476 0.24810752688172077 11.322250000000004 +9.586738351254477 -1.1918924731182794 11.322249999999993 +10.726738351254475 -1.3118924731182808 11.322249999999993 +10.666738351254477 0.8781075268817216 11.32225 +9.586738351254478 0.7881075268817216 11.32225 +9.706738351254476 -1.7918924731182786 11.32225 +9.166738351254473 -0.41189247311827915 11.322249999999997 +10.486738351254473 1.3281075268817206 11.32225 +9.496738351254475 0.21810752688172008 11.322250000000002 +9.61673835125448 -0.44189247311827906 11.322249999999997 +7.4267383512544765 -0.891892473118279 9.67725 +7.606738351254478 -0.3818924731182791 9.67725 +7.996738351254475 -0.05189247311827877 9.67725 +8.536738351254474 0.12810752688172145 9.67725 +9.496738351254475 0.21810752688172086 9.67725 +9.616738351254478 0.24810752688172089 9.67725 +9.586738351254478 0.7881075268817215 9.67725 +9.316738351254479 1.058107526881721 9.67725 +8.836738351254478 1.118107526881721 9.67725 +8.056738351254479 0.9081075268817216 9.67725 +7.786738351254474 0.7881075268817215 9.67725 +7.816738351254479 1.5381075268817215 9.67725 +8.566738351254479 1.748107526881721 9.67725 +9.496738351254475 1.8081075268817215 9.67725 +10.096738351254478 1.6581075268817216 9.67725 +10.486738351254475 1.3281075268817215 9.67725 +10.666738351254477 0.8781075268817216 9.67725 +10.726738351254475 -1.311892473118279 9.67725 +10.906738351254477 -1.461892473118279 9.67725 +11.146738351254477 -1.4918924731182792 9.67725 +11.146738351254477 -2.15189247311828 9.67725 +10.366738351254478 -2.241892473118279 9.67725 +9.946738351254476 -2.0918924731182797 9.67725 +9.706738351254476 -1.7918924731182795 9.67725 +9.616738351254478 -1.7018924731182796 9.67725 +9.226738351254475 -2.0918924731182797 9.67725 +8.746738351254475 -2.241892473118279 9.67725 +8.146738351254477 -2.1818924731182796 9.67725 +7.756738351254475 -1.9418924731182798 9.67725 +7.546738351254474 -1.6418924731182791 9.67725 +7.4267383512544765 -1.1618924731182791 9.67725 +8.536738351254474 -1.1618924731182791 9.67725 +8.686738351254476 -0.5618924731182792 9.67725 +8.506738351254475 -0.861892473118279 9.67725 +9.586738351254478 -1.1918924731182794 9.67725 +9.256738351254475 -1.4318924731182792 9.67725 +9.616738351254478 -0.44189247311827906 9.67725 +9.166738351254477 -0.41189247311827915 9.67725 +8.776738351254474 -1.4318924731182792 9.67725 +671 426 Edge Vertex Indices Starting from 1 +426 107 +647 646 +646 645 +633 261 +672 455 +431 417 +417 101 +193 174 +174 172 +80 63 +627 247 +190 176 +176 175 +191 190 +190 175 +188 172 +172 171 +481 143 +83 63 +18 17 +17 16 +142 137 +137 127 +85 67 +67 66 +85 68 +68 67 +736 731 +731 685 +671 107 +667 148 +661 148 +193 191 +191 174 +20 19 +19 14 +190 177 +177 176 +19 15 +15 14 +448 434 +434 431 +481 249 +249 33 +19 18 +18 16 +19 16 +16 15 +484 247 +665 425 +708 674 +24 23 +23 12 +188 171 +171 170 +694 677 +677 666 +191 175 +175 174 +193 172 +153 128 +142 127 +127 126 +627 626 +626 247 +20 14 +14 13 +30 12 +710 673 +673 667 +366 352 +352 335 +32 6 +597 276 +34 7 +470 453 +34 32 +32 7 +33 32 +195 33 +280 279 +296 280 +300 285 +187 170 +170 169 +295 278 +314 312 +402 314 +639 637 +637 522 +474 458 +389 373 +358 342 +40 17 +646 627 +194 181 +181 180 +328 310 +44 43 +43 3 +44 3 +92 63 +111 84 +401 317 +317 315 +404 289 +289 288 +43 2 +624 600 +45 2 +562 549 +46 20 +510 494 +731 702 +702 701 +38 35 +49 38 +643 630 +348 347 +363 348 +679 152 +44 4 +222 219 +219 214 +402 313 +313 310 +38 15 +683 665 +665 426 +49 15 +363 362 +408 363 +42 23 +323 322 +322 306 +481 195 +195 143 +52 23 +569 558 +558 552 +55 12 +724 665 +55 37 +37 12 +614 604 +604 567 +153 152 +152 128 +434 417 +735 733 +733 716 +673 146 +53 5 +620 564 +621 620 +366 365 +408 366 +494 493 +509 494 +596 328 +251 43 +57 56 +56 29 +57 29 +293 291 +399 293 +287 286 +301 287 +56 26 +30 27 +60 30 +339 338 +354 339 +61 59 +59 28 +61 28 +467 453 +453 452 +509 493 +210 209 +209 182 +615 605 +605 604 +391 271 +357 341 +597 274 +413 404 +404 99 +417 100 +562 555 +555 549 +512 497 +497 496 +569 552 +616 569 +253 229 +254 253 +505 489 +402 312 +658 641 +326 309 +596 327 +292 291 +291 274 +328 312 +330 328 +598 349 +365 349 +610 568 +253 41 +325 323 +323 307 +640 639 +639 522 +324 308 +625 615 +615 604 +412 361 +361 360 +501 485 +659 643 +643 642 +592 503 +399 296 +296 295 +325 307 +408 362 +412 408 +401 318 +318 317 +399 281 +291 275 +275 274 +459 458 +475 459 +295 294 +399 295 +195 34 +325 308 +53 6 +621 611 +611 610 +256 51 +470 454 +454 453 +350 349 +365 350 +323 306 +596 329 +362 346 +607 563 +281 280 +296 281 +326 324 +324 309 +597 275 +332 315 +333 332 +506 490 +278 277 +294 278 +327 310 +328 327 +566 560 +399 294 +294 293 +39 35 +282 280 +485 484 +499 485 +341 340 +356 341 +616 615 +615 569 +497 481 +401 305 +305 303 +554 467 +508 491 +597 278 +412 360 +360 359 +252 43 +638 629 +629 488 +211 39 +554 470 +470 467 +320 286 +299 284 +251 53 +46 21 +47 46 +576 546 +620 609 +642 411 +282 281 +297 282 +623 613 +613 612 +596 330 +330 329 +585 574 +618 608 +608 607 +503 489 +489 488 +582 571 +466 449 +408 336 +366 335 +622 611 +619 608 +36 35 +195 36 +554 469 +61 30 +647 626 +408 365 +365 364 +302 287 +195 54 +54 36 +256 47 +623 612 +55 48 +48 37 +597 277 +479 464 +464 463 +480 464 +597 279 +331 314 +401 303 +346 345 +361 346 +404 290 +290 289 +540 524 +251 44 +598 335 +329 312 +331 329 +329 314 +408 352 +328 313 +313 312 +285 284 +299 285 +330 312 +333 315 +404 292 +292 290 +614 602 +284 283 +298 284 +58 26 +638 488 +483 247 +669 150 +246 231 +231 230 +78 77 +79 78 +736 685 +84 65 +86 71 +87 86 +80 77 +81 80 +561 560 +560 550 +534 409 +264 262 +93 92 +92 64 +93 64 +363 347 +430 104 +479 477 +477 405 +93 65 +417 414 +414 413 +94 93 +93 66 +431 101 +96 67 +616 605 +97 68 +446 445 +558 446 +98 97 +97 69 +98 69 +100 71 +445 428 +101 72 +102 101 +101 73 +165 163 +163 162 +362 347 +347 346 +103 74 +451 94 +104 75 +718 670 +670 664 +452 94 +518 410 +105 77 +429 428 +445 429 +451 95 +583 550 +584 583 +79 63 +107 79 +107 63 +446 429 +539 523 +601 600 +624 601 +352 336 +412 362 +362 361 +598 348 +242 229 +229 228 +50 20 +101 100 +100 72 +514 210 +394 377 +102 73 +439 410 +562 411 +411 403 +605 567 +106 78 +108 88 +88 82 +364 349 +349 348 +478 405 +113 80 +413 375 +375 374 +364 348 +109 90 +90 88 +611 568 +608 563 +596 401 +591 570 +592 579 +594 592 +624 613 +599 385 +421 420 +437 421 +374 373 +413 374 +573 546 +546 545 +350 335 +598 350 +463 404 +450 95 +453 92 +605 563 +557 549 +517 410 +599 408 +538 537 +593 538 +582 572 +572 571 +520 410 +505 490 +490 489 +621 610 +598 408 +462 404 +618 552 +619 618 +437 420 +402 399 +596 402 +572 545 +461 406 +117 90 +461 404 +188 170 +113 81 +118 113 +506 400 +508 506 +196 167 +584 574 +574 573 +414 412 +597 399 +494 406 +460 406 +413 99 +534 532 +532 409 +614 567 +515 514 +529 515 +396 378 +438 422 +368 367 +416 368 +588 577 +420 414 +625 604 +414 410 +410 409 +531 515 +591 581 +581 570 +625 614 +614 603 +106 79 +107 106 +419 418 +433 419 +387 385 +385 370 +562 560 +560 411 +406 403 +493 406 +510 509 +509 400 +352 351 +351 335 +382 367 +383 382 +416 370 +370 369 +192 180 +180 179 +427 107 +593 537 +537 536 +567 551 +602 567 +715 692 +437 436 +436 421 +439 414 +586 549 +587 586 +418 414 +453 91 +357 342 +342 341 +476 400 +532 517 +517 516 +491 403 +109 88 +435 419 +599 387 +396 379 +464 404 +366 350 +396 393 +393 378 +524 523 +539 524 +493 403 +534 517 +599 384 +617 606 +437 412 +428 107 +622 612 +612 611 +373 372 +413 373 +491 490 +506 491 +421 414 +439 422 +593 539 +539 538 +490 403 +599 386 +386 385 +553 547 +547 391 +581 571 +571 570 +525 524 +540 525 +420 419 +435 420 +416 369 +369 368 +599 416 +416 408 +412 409 +436 412 +386 384 +384 368 +508 400 +598 347 +387 370 +450 96 +386 368 +606 563 +599 413 +535 533 +533 409 +449 98 +408 364 +364 363 +538 523 +523 522 +398 382 +541 525 +613 600 +600 565 +562 550 +394 392 +392 376 +622 554 +623 622 +618 607 +507 493 +493 492 +573 545 +431 102 +464 449 +449 404 +428 427 +444 428 +562 407 +384 367 +478 476 +476 405 +509 507 +507 400 +429 106 +492 403 +114 109 +109 108 +395 379 +394 376 +519 410 +384 383 +383 367 +428 106 +419 414 +562 403 +587 577 +577 576 +619 609 +609 608 +596 399 +597 596 +355 339 +617 552 +618 617 +480 479 +479 405 +393 377 +599 383 +412 359 +359 358 +594 579 +114 110 +110 109 +115 110 +30 24 +24 12 +632 247 +26 8 +8 7 +129 128 +128 127 +115 111 +111 110 +134 120 +120 119 +135 134 +134 119 +139 130 +140 120 +141 136 +136 123 +144 143 +143 119 +144 119 +334 303 +203 175 +298 283 +145 120 +532 516 +516 515 +502 501 +556 502 +392 341 +600 548 +147 122 +460 459 +475 460 +585 584 +586 585 +418 417 +434 418 +584 550 +550 549 +592 400 +533 518 +534 533 +495 458 +556 501 +501 499 +415 409 +416 367 +154 129 +407 400 +592 407 +399 291 +638 489 +489 403 +351 336 +353 351 +583 572 +592 556 +157 133 +133 119 +260 259 +259 236 +145 144 +144 120 +80 79 +79 77 +147 146 +146 122 +148 124 +616 606 +606 605 +542 526 +526 525 +149 125 +587 575 +162 136 +164 135 +166 134 +479 463 +463 462 +578 576 +166 140 +140 134 +587 576 +576 575 +597 388 +388 274 +548 332 +559 540 +540 539 +570 559 +593 570 +590 579 +519 518 +533 519 +595 591 +591 580 +321 304 +296 279 +279 278 +592 506 +506 505 +598 398 +599 598 +575 546 +389 274 +569 446 +564 554 +622 564 +518 517 +534 518 +612 565 +359 343 +343 342 +516 439 +439 438 +609 568 +621 564 +622 621 +164 139 +354 337 +559 541 +541 540 +322 321 +321 306 +571 559 +118 117 +117 113 +495 459 +402 315 +579 556 +402 310 +160 158 +504 486 +206 157 +594 407 +535 519 +401 315 +402 401 +558 542 +559 558 +201 143 +563 553 +567 563 +334 318 +318 303 +292 273 +296 278 +354 338 +338 337 +316 314 +402 316 +620 610 +610 609 +542 525 +566 415 +595 566 +566 561 +157 119 +583 582 +582 550 +565 548 +548 547 +620 619 +619 564 +398 383 +599 398 +558 444 +444 443 +372 371 +416 372 +613 565 +532 515 +571 545 +475 458 +448 430 +459 406 +494 459 +573 572 +583 573 +479 462 +415 411 +560 415 +611 565 +617 607 +607 606 +520 519 +535 520 +292 274 +274 273 +568 565 +565 547 +412 358 +413 412 +301 300 +304 301 +589 578 +578 577 +556 546 +578 556 +586 584 +584 549 +413 372 +416 413 +165 140 +166 165 +556 499 +205 157 +595 415 +319 303 +153 129 +154 153 +542 541 +559 542 +592 505 +505 503 +575 573 +340 339 +355 340 +457 143 +578 546 +359 342 +584 573 +388 387 +387 372 +588 549 +456 144 +504 487 +487 486 +536 535 +535 409 +569 447 +447 446 +594 590 +590 557 +371 370 +416 371 +595 561 +200 143 +580 570 +593 580 +568 563 +608 568 +445 444 +558 445 +579 578 +589 579 +455 454 +469 455 +555 407 +594 555 +619 552 +590 589 +589 557 +360 344 +344 343 +404 399 +399 292 +589 577 +551 345 +499 484 +500 499 +283 282 +297 283 +356 340 +405 400 +406 405 +202 143 +166 135 +448 431 +431 430 +471 455 +484 483 +500 484 +552 545 +564 552 +595 580 +415 410 +598 412 +457 144 +593 559 +559 539 +564 545 +553 551 +567 553 +302 271 +591 561 +516 410 +288 271 +302 288 +425 152 +476 460 +161 136 +162 161 +599 388 +602 551 +551 412 +404 302 +433 418 +434 433 +458 457 +473 458 +360 343 +353 337 +334 333 +333 318 +557 555 +594 557 +589 588 +588 557 +480 466 +466 464 +603 434 +387 371 +404 288 +320 319 +319 305 +617 616 +616 552 +333 317 +203 143 +587 549 +588 587 +536 520 +470 469 +469 454 +512 474 +474 473 +683 666 +666 665 +174 173 +173 172 +221 184 +183 182 +182 181 +673 148 +187 169 +194 180 +323 284 +270 269 +269 262 +393 339 +389 273 +197 168 +355 354 +379 355 +504 502 +556 504 +196 195 +195 167 +199 170 +354 353 +380 354 +598 351 +673 147 +672 456 +456 146 +510 400 +396 395 +395 338 +404 98 +204 203 +203 176 +597 280 +496 458 +514 423 +423 210 +553 342 +378 355 +379 378 +218 190 +496 481 +481 457 +211 210 +210 182 +211 182 +543 443 +443 442 +302 301 +305 302 +561 550 +582 561 +200 199 +199 171 +200 171 +379 354 +380 379 +670 107 +107 91 +210 208 +438 423 +515 438 +496 457 +298 297 +309 298 +325 324 +324 282 +380 353 +381 380 +322 286 +358 357 +376 358 +392 342 +553 392 +208 207 +207 179 +208 179 +394 393 +393 340 +320 287 +547 320 +297 281 +311 297 +213 192 +192 185 +212 192 +602 435 +435 433 +215 187 +187 186 +390 273 +548 333 +215 186 +397 337 +198 196 +402 311 +311 281 +217 212 +212 185 +217 185 +219 193 +193 188 +214 193 +480 405 +601 480 +376 375 +375 358 +596 280 +599 597 +597 413 +548 334 +334 319 +500 498 +498 469 +597 404 +396 338 +627 484 +353 336 +381 353 +531 432 +432 422 +357 356 +377 357 +325 282 +382 381 +381 336 +398 351 +351 337 +510 476 +476 475 +325 283 +548 319 +322 284 +416 336 +652 633 +436 409 +532 436 +601 466 +531 422 +500 469 +398 337 +327 280 +603 433 +553 391 +323 283 +547 319 +602 433 +603 602 +551 344 +592 504 +322 285 +215 189 +216 215 +377 356 +378 377 +326 280 +356 355 +378 356 +516 438 +598 346 +326 282 +543 542 +542 443 +309 297 +311 309 +222 214 +214 187 +396 339 +222 187 +413 358 +544 543 +543 442 +601 465 +551 346 +598 551 +394 340 +497 473 +473 472 +515 423 +305 301 +581 561 +582 581 +397 338 +558 443 +510 475 +475 474 +304 300 +306 304 +321 286 +416 382 +382 336 +532 531 +531 436 +394 341 +401 302 +554 499 +499 469 +402 281 +210 207 +376 357 +377 376 +308 298 +309 308 +512 473 +472 457 +457 456 +190 185 +185 177 +699 668 +241 234 +234 231 +239 238 +238 223 +243 234 +241 231 +246 241 +220 184 +249 248 +248 225 +712 688 +250 249 +249 225 +250 225 +251 250 +250 227 +90 77 +77 76 +252 227 +253 228 +473 457 +255 230 +234 233 +233 231 +648 626 +257 232 +663 660 +660 152 +258 233 +720 719 +719 716 +259 235 +189 181 +261 237 +237 223 +247 237 +634 524 +635 524 +248 224 +250 226 +252 251 +251 227 +253 252 +252 228 +719 717 +717 716 +254 229 +655 639 +257 256 +256 232 +697 689 +260 236 +630 411 +642 630 +263 262 +262 241 +263 241 +267 238 +243 241 +262 243 +635 634 +654 635 +629 628 +628 488 +257 233 +258 257 +260 258 +628 487 +656 639 +654 634 +647 627 +530 441 +441 440 +111 85 +85 84 +636 634 +634 525 +652 636 +636 633 +270 238 +269 243 +711 688 +688 672 +641 411 +156 132 +209 181 +692 670 +670 91 +113 109 +665 663 +663 425 +711 672 +498 472 +472 471 +710 667 +25 3 +3 2 +704 693 +693 689 +482 249 +668 661 +661 149 +714 692 +718 717 +719 718 +674 667 +667 662 +705 693 +702 662 +720 687 +687 683 +112 84 +84 83 +729 669 +705 681 +202 173 +709 708 +710 709 +245 224 +736 735 +735 703 +707 681 +150 149 +149 126 +62 57 +57 31 +679 669 +669 152 +90 76 +648 632 +632 626 +649 632 +708 678 +669 151 +511 510 +510 474 +736 702 +707 678 +712 707 +707 706 +240 228 +228 227 +165 160 +149 148 +148 125 +544 441 +210 157 +92 91 +91 63 +688 684 +684 455 +691 664 +711 707 +712 711 +136 124 +124 123 +735 704 +704 703 +233 232 +232 231 +713 712 +712 706 +112 110 +110 84 +717 664 +696 684 +714 696 +86 82 +82 72 +498 497 +497 472 +710 708 +711 710 +390 389 +389 374 +737 725 +725 724 +50 19 +714 684 +726 663 +725 663 +717 691 +688 455 +710 672 +692 691 +691 670 +449 97 +729 699 +699 669 +694 666 +698 695 +695 690 +737 724 +736 703 +703 702 +450 97 +711 708 +708 707 +62 31 +31 25 +721 687 +706 681 +242 228 +725 665 +117 109 +514 211 +211 46 +514 46 +25 4 +4 3 +27 10 +10 9 +156 155 +155 132 +214 188 +188 187 +512 511 +511 474 +31 4 +696 692 +692 91 +31 5 +5 4 +33 6 +36 11 +36 10 +40 39 +39 17 +211 50 +50 46 +478 477 +477 461 +477 462 +462 461 +49 48 +48 15 +48 14 +52 51 +51 22 +52 22 +53 44 +44 5 +163 161 +54 34 +34 8 +54 8 +103 102 +102 74 +478 460 +478 461 +461 460 +50 40 +40 18 +50 18 +253 45 +252 45 +45 43 +704 689 +211 40 +528 257 +260 257 +650 632 +650 631 +61 60 +60 59 +60 58 +544 442 +442 441 +195 32 +211 195 +195 39 +195 35 +600 401 +548 401 +596 548 +116 111 +687 666 +290 272 +290 273 +273 272 +294 276 +294 277 +277 276 +361 344 +361 345 +345 344 +327 326 +326 310 +326 311 +311 310 +657 641 +641 637 +289 271 +289 272 +272 271 +301 285 +301 286 +286 285 +500 482 +500 483 +483 482 +332 331 +331 316 +332 316 +316 315 +51 47 +47 21 +51 21 +512 495 +512 496 +496 495 +511 494 +511 495 +495 494 +663 152 +49 37 +49 35 +83 65 +65 63 +218 217 +217 190 +65 64 +64 63 +90 81 +81 77 +96 95 +95 67 +508 507 +507 491 +507 492 +492 491 +105 104 +104 76 +105 76 +106 105 +105 78 +268 264 +386 370 +386 369 +97 96 +96 68 +100 99 +99 71 +202 201 +201 173 +104 103 +103 75 +113 112 +112 80 +112 83 +83 80 +114 108 +108 86 +108 82 +439 421 +422 421 +436 422 +538 521 +538 522 +522 521 +389 388 +388 373 +388 372 +431 103 +430 103 +452 92 +94 92 +397 395 +395 381 +395 380 +521 410 +521 411 +411 410 +398 397 +397 382 +397 381 +543 526 +543 527 +527 526 +414 409 +89 70 +70 69 +544 527 +544 528 +528 527 +586 574 +586 575 +575 574 +443 426 +442 426 +426 425 +537 520 +537 521 +521 520 +417 413 +413 100 +118 81 +117 81 +429 104 +106 104 +133 132 +132 131 +139 135 +135 131 +139 131 +131 130 +456 145 +504 503 +503 487 +503 488 +488 487 +152 151 +151 128 +151 127 +593 536 +536 409 +155 131 +625 603 +615 603 +603 569 +146 145 +145 122 +145 121 +559 545 +558 545 +466 465 +465 449 +465 450 +450 449 +159 158 +158 139 +159 139 +139 137 +160 159 +159 142 +159 137 +441 423 +441 424 +424 423 +624 623 +623 601 +623 554 +637 521 +637 411 +468 451 +465 451 +451 450 +425 153 +444 427 +443 427 +427 426 +407 403 +403 400 +406 400 +442 424 +442 425 +425 424 +293 276 +291 276 +276 275 +596 332 +596 331 +472 456 +471 456 +456 455 +498 482 +497 482 +482 481 +199 196 +199 143 +196 143 +468 467 +467 452 +468 452 +452 451 +204 157 +203 157 +157 143 +321 320 +320 304 +320 305 +305 304 +156 154 +156 153 +603 448 +569 448 +448 447 +194 184 +184 181 +632 631 +631 261 +218 216 +628 486 +498 471 +471 469 +632 261 +261 247 +211 167 +211 183 +183 167 +199 198 +198 170 +203 202 +202 175 +202 174 +659 653 +653 643 +219 218 +218 191 +219 191 +308 307 +307 299 +308 299 +299 298 +197 196 +196 168 +30 28 +28 24 +307 306 +306 300 +307 300 +300 299 +207 157 +481 33 +553 343 +551 343 +391 272 +390 272 +667 661 +86 72 +72 71 +237 236 +236 235 +270 267 +630 403 +256 255 +255 232 +255 231 +636 525 +659 658 +658 657 +157 156 +156 133 +88 76 +76 75 +268 265 +255 254 +254 230 +261 260 +260 237 +718 664 +530 440 +654 653 +656 654 +656 655 +655 654 +653 652 +652 650 +652 651 +651 650 +254 41 +640 522 +26 9 +9 8 +58 56 +683 426 +669 668 +668 150 +668 149 +734 723 +734 724 +724 723 +729 679 +700 668 +700 661 +113 110 +27 9 +673 672 +672 146 +638 403 +702 676 +676 662 +678 674 +676 674 +674 662 +703 689 +721 694 +694 687 +23 22 +22 13 +23 13 +13 12 +636 261 +22 21 +21 13 +21 20 +20 13 +736 698 +736 695 +42 1 +42 24 +24 1 +27 12 +12 11 +27 11 +11 10 +568 553 +568 547 +250 33 +250 53 +53 33 +37 35 +35 11 +37 11 +649 648 +648 644 +245 240 +240 226 +240 227 +227 226 +246 229 +246 230 +230 229 +85 66 +84 66 +66 65 +595 593 +593 415 +593 409 +640 523 +635 523 +95 94 +94 67 +94 66 +45 41 +41 1 +45 1 +447 429 +447 430 +430 429 +116 115 +115 89 +115 87 +115 114 +114 87 +114 86 +116 85 +116 89 +89 85 +644 630 +644 638 +638 630 +696 453 +696 91 +89 87 +87 70 +87 71 +71 70 +148 147 +147 124 +147 123 +728 679 +727 679 +679 660 +99 98 +98 70 +99 70 +163 136 +163 138 +138 136 +502 486 +501 486 +486 485 +544 530 +530 528 +530 513 +406 401 +406 404 +404 401 +438 432 +440 438 +440 423 +601 405 +600 405 +405 401 +28 25 +25 2 +183 181 +189 183 +201 200 +200 173 +200 172 +221 216 +216 189 +221 189 +189 184 +531 529 +529 432 +529 440 +440 432 +601 554 +554 468 +601 468 +468 465 +530 529 +529 513 +529 514 +514 513 +547 271 +547 287 +287 271 +29 26 +26 7 +29 7 +7 6 +697 678 +678 676 +653 634 +652 634 +88 74 +88 75 +75 74 +264 263 +263 242 +263 246 +246 242 +266 265 +265 245 +265 240 +265 264 +264 240 +264 242 +242 240 +268 266 +266 244 +266 245 +245 244 +528 260 +527 260 +735 716 +716 715 +735 715 +715 704 +138 124 +138 125 +125 124 +31 29 +29 5 +29 6 +6 5 +255 41 +255 42 +42 41 +645 638 +645 629 +727 660 +726 660 +723 677 +735 698 +733 698 +698 690 +715 714 +714 705 +715 705 +705 704 +185 178 +178 177 +732 730 +730 686 +730 680 +483 249 +483 248 +142 125 +142 126 +126 125 +89 68 +89 69 +69 68 +724 666 +723 666 +186 169 +169 167 +169 168 +168 167 +734 733 +733 690 +734 690 +690 682 +733 723 +723 722 +733 722 +722 716 +714 713 +713 705 +713 706 +706 705 +716 691 +715 691 +720 683 +719 683 +683 671 +165 158 +82 74 +74 73 +82 73 +73 72 +719 671 +718 671 +671 670 +701 662 +700 662 +662 661 +737 726 +726 725 +155 154 +154 130 +155 130 +696 454 +684 454 +732 686 +731 686 +686 685 +209 208 +208 180 +209 180 +722 721 +721 720 +722 720 +720 716 +39 38 +38 17 +38 16 +54 9 +36 9 +222 218 +392 391 +391 375 +392 375 +602 412 +435 412 +437 435 +133 131 +135 133 +135 119 +137 130 +130 127 +130 129 +129 127 +151 150 +150 126 +151 126 +163 160 +160 138 +160 142 +142 138 +556 554 +564 556 +564 546 +391 390 +390 375 +390 374 +198 197 +197 169 +198 169 +628 627 +627 485 +628 485 +703 676 +703 697 +697 676 +205 204 +204 176 +205 176 +207 206 +206 178 +207 178 +220 194 +213 194 +194 192 +709 674 +709 667 +248 247 +247 223 +248 223 +651 631 +651 633 +633 631 +259 258 +258 235 +258 234 +659 657 +657 656 +659 656 +656 653 +649 644 +644 643 +659 642 +658 642 +642 641 +646 629 +646 628 +210 156 +657 639 +657 637 +713 688 +713 684 +141 121 +140 121 +121 120 +423 156 +528 513 +513 257 +513 256 +732 728 +730 728 +728 727 +28 1 +28 2 +2 1 +722 677 +721 677 +729 728 +732 729 +732 699 +141 123 +123 121 +123 122 +122 121 +636 526 +636 527 +527 261 +62 59 +59 58 +62 58 +58 57 +55 14 +55 13 +62 25 +59 25 +513 46 +256 46 +648 647 +647 645 +648 645 +645 644 +655 640 +654 640 +640 635 +165 162 +162 141 +165 141 +141 140 +189 186 +186 183 +186 167 +206 205 +205 177 +206 177 +256 52 +255 52 +52 42 +697 693 +693 681 +697 681 +681 678 +244 224 +238 224 +224 223 +732 731 +731 701 +737 675 +730 675 +270 262 +268 262 +192 178 +192 179 +179 178 +166 158 +166 164 +164 158 +686 682 +695 685 +690 685 +685 682 +737 730 +730 727 +737 727 +727 726 +60 26 +60 27 +27 26 +222 215 +218 215 +270 268 +268 244 +270 244 +244 238 +237 235 +239 237 +239 223 +243 239 +239 235 +243 235 +235 234 +653 650 +650 643 +650 649 +649 643 +732 701 +701 699 +701 700 +700 699 +221 217 +217 216 +220 213 +213 212 +424 153 +423 153 +737 734 +734 675 +734 682 +682 675 +686 680 +682 680 +680 675 +245 226 +226 225 +245 225 +225 224 +269 267 +267 239 +269 239 +221 220 +220 212 +221 212 +1 2 31 Face Edge Indices Starting from 1 +2161 3 4 +1709 2105 5 +6 1683 1035 +388 7 8 +9 10 61 +11 419 744 +12 50 1126 +16 13 14 +59 15 16 +17 18 55 +899 1052 19 +11 1569 20 +21 22 47 +63 23 24 +25 26 1842 +27 28 25 +29 30 366 +2027 31 1067 +1747 32 33 +33 1362 1320 +9 34 35 +36 37 67 +13 38 39 +40 41 37 +949 42 43 +44 45 1742 +46 47 48 +48 49 40 +50 360 952 +130 51 1609 +52 1801 1351 +53 54 682 +55 56 496 +57 58 1408 +35 59 60 +17 1111 61 +62 686 890 +1992 63 64 +65 66 12 +67 68 1815 +1821 171 69 +1313 70 71 +72 73 312 +1933 79 74 +1501 329 75 +1463 76 685 +289 77 178 +78 79 76 +80 1487 81 +82 778 83 +84 350 1740 +85 86 1016 +87 262 237 +88 200 89 +90 91 215 +92 981 1007 +1393 93 464 +561 1080 94 +21 1471 95 +96 2083 2119 +97 98 1017 +347 99 127 +102 100 101 +1428 123 102 +103 1541 377 +1382 689 104 +105 106 817 +994 107 108 +1315 101 109 +460 110 628 +1475 111 109 +719 630 112 +1814 293 113 +160 538 114 +115 116 2188 +117 1535 118 +1272 223 119 +120 379 121 +1246 1882 122 +1441 1460 123 +124 125 1175 +126 127 811 +49 2050 128 +1784 129 130 +118 131 128 +132 228 133 +53 1819 134 +249 135 136 +19 137 138 +2179 139 134 +195 140 141 +1810 2155 142 +143 1009 1997 +144 145 142 +511 146 147 +148 149 62 +43 150 7 +151 152 1958 +1034 153 746 +1968 241 154 +155 797 156 +157 320 158 +159 180 160 +161 207 302 +100 341 162 +165 163 164 +1965 1344 165 +166 731 167 +168 1515 169 +1930 164 170 +171 2208 172 +173 828 174 +177 175 176 +1735 318 177 +1691 178 179 +180 636 648 +1054 181 182 +183 184 218 +1513 1745 185 +1131 186 273 +234 257 187 +508 188 189 +8 190 433 +112 191 192 +1528 193 194 +195 998 196 +197 1265 198 +307 895 199 +200 348 126 +1759 201 1509 +1174 1507 202 +203 264 161 +868 204 205 +206 351 207 +247 469 208 +321 209 445 +210 796 833 +1853 1473 211 +227 212 213 +214 215 1780 +1212 256 216 +217 218 520 +282 219 220 +271 725 221 +222 223 2115 +224 1626 1159 +238 225 226 +1729 240 227 +228 427 229 +230 231 105 +232 254 225 +205 233 234 +235 854 236 +237 266 238 +1462 323 239 +1075 240 216 +241 1442 1829 +242 243 484 +1456 2177 244 +77 245 246 +247 209 248 +1737 213 249 +344 1680 250 +428 251 337 +252 617 865 +253 83 254 +202 255 256 +257 1678 75 +258 352 259 +781 260 482 +261 1500 262 +263 99 264 +265 372 837 +167 266 267 +2048 268 117 +253 298 269 +270 940 271 +272 944 273 +196 274 275 +1051 194 276 +277 278 335 +1690 1920 279 +500 280 588 +779 333 281 +675 282 283 +284 162 1259 +285 286 359 +1445 1477 287 +279 288 289 +168 1089 290 +291 357 1732 +292 1828 1233 +293 1525 294 +295 784 764 +843 296 664 +2117 297 1302 +298 1091 299 +300 301 326 +302 303 250 +714 304 502 +305 306 635 +1628 307 308 +1191 309 598 +640 991 310 +426 346 311 +468 572 312 +798 313 242 +488 314 305 +315 1490 316 +1004 288 317 +318 172 1482 +319 65 1289 +622 320 321 +1058 322 169 +323 324 316 +1524 244 325 +634 326 583 +327 328 144 +261 281 329 +758 330 331 +671 332 330 +82 1043 333 +830 1521 334 +825 230 335 +336 1503 337 +107 338 339 +576 769 340 +1459 292 341 +342 541 1033 +345 343 88 +334 344 345 +158 346 72 +206 347 348 +349 291 350 +303 351 343 +106 999 352 +338 353 354 +1157 527 355 +356 703 357 +358 170 1783 +359 308 732 +1990 360 2100 +1352 361 2067 +362 363 1840 +364 745 365 +366 2200 1817 +367 1536 1331 +368 1875 369 +370 1543 371 +1060 372 373 +620 722 374 +375 1273 1941 +378 376 377 +1540 383 378 +379 404 132 +380 1617 1858 +672 381 382 +387 383 1844 +1613 384 385 +1852 386 387 +400 639 388 +28 1559 389 +274 390 183 +1996 395 391 +392 924 393 +396 394 395 +1600 1884 396 +1749 434 397 +643 923 398 +2024 401 399 +400 401 437 +2167 402 403 +251 404 405 +1940 1565 406 +1850 416 407 +1551 408 1763 +1769 409 410 +407 1693 411 +475 789 412 +364 1554 413 +414 398 415 +416 471 1665 +417 718 418 +419 421 420 +1068 421 1367 +422 415 392 +423 624 592 +424 110 425 +734 540 426 +219 427 428 +446 208 429 +430 431 1421 +113 1448 432 +399 433 434 +1424 435 1053 +1087 436 673 +2022 1466 437 +521 556 438 +661 439 440 +441 147 184 +528 442 365 +1572 443 444 +451 445 446 +382 1449 447 +371 497 448 +449 450 465 +451 121 623 +567 452 453 +210 243 454 +455 252 306 +456 818 491 +457 919 776 +458 677 459 +1658 460 300 +461 532 569 +462 489 463 +464 586 465 +466 467 638 +468 342 469 +486 759 470 +1544 615 471 +472 1584 179 +473 823 441 +192 986 474 +967 565 475 +476 485 783 +477 549 478 +479 480 309 +656 866 481 +199 482 483 +156 484 832 +229 958 485 +495 1452 486 +487 931 488 +2059 489 603 +490 666 491 +853 480 492 +507 1469 493 +494 452 1423 +1896 493 495 +1434 496 85 +497 1615 498 +499 612 500 +501 2002 1734 +502 503 902 +873 385 504 +936 1124 505 +537 159 506 +856 712 507 +1614 508 1560 +374 509 510 +552 355 511 +512 1925 513 +570 514 1049 +1130 1898 515 +516 727 517 +1002 518 662 +660 602 519 +520 146 526 +521 522 1598 +1915 523 513 +524 525 457 +526 527 1636 +528 420 529 +530 978 531 +532 533 614 +534 535 439 +536 577 537 +1037 538 539 +73 540 541 +542 658 543 +544 545 604 +546 547 2194 +548 2 1669 +549 550 1633 +551 975 552 +1980 553 1321 +463 554 555 +589 1573 556 +557 1001 558 +559 384 716 +560 1366 472 +186 561 562 +563 945 647 +705 564 565 +593 587 566 +567 443 653 +531 1097 568 +569 903 974 +1038 570 654 +470 331 571 +572 248 157 +514 573 574 +575 423 576 +651 637 577 +578 564 509 +610 594 579 +997 580 749 +609 554 581 +582 548 642 +583 584 313 +585 882 586 +587 260 588 +519 462 589 +795 590 515 +591 592 478 +733 483 593 +594 595 461 +596 597 1153 +598 599 525 +600 340 601 +602 568 603 +604 605 517 +606 607 476 +608 1145 609 +616 610 611 +649 1546 612 +120 429 613 +915 993 614 +1414 615 1558 +605 1557 616 +617 473 750 +618 883 606 +910 619 620 +621 1040 641 +622 623 133 +1577 624 625 +626 543 845 +601 802 627 +851 628 629 +630 373 534 +655 631 632 +633 1660 634 +670 635 864 +1548 636 637 +638 492 858 +1465 1582 639 +571 640 641 +642 1667 643 +929 191 644 +516 611 645 +447 646 647 +539 648 649 +650 659 414 +566 1549 651 +652 653 1570 +1066 1588 654 +1210 436 655 +412 773 656 +645 657 658 +529 582 659 +559 530 660 +1670 644 661 +662 663 763 +664 665 314 +666 505 667 +668 174 1024 +669 487 670 +1113 671 672 +574 673 1164 +657 579 674 +675 676 872 +772 913 677 +678 679 652 +678 1862 680 +681 682 69 +1349 683 66 +1931 684 685 +686 687 2066 +680 688 689 +693 690 691 +692 693 2062 +2063 1651 694 +762 695 690 +2143 696 697 +698 699 700 +691 743 700 +1121 701 889 +1042 702 14 +703 943 1073 +1641 704 2127 +705 706 852 +707 724 708 +709 562 1082 +710 841 629 +2145 1879 711 +712 236 713 +714 880 715 +716 150 717 +718 719 881 +720 499 780 +721 790 722 +235 808 723 +724 725 886 +726 522 957 +727 542 1199 +2065 2034 728 +729 720 730 +731 204 937 +732 733 1798 +734 1127 735 +839 736 479 +737 810 458 +838 738 739 +740 741 1270 +704 742 743 +744 745 370 +711 746 747 +1964 1363 748 +749 750 390 +751 752 834 +1993 1342 753 +558 754 1605 +696 2168 755 +1621 799 756 +692 948 757 +758 759 860 +877 760 663 +757 761 762 +763 764 754 +187 765 766 +767 259 1101 +768 769 961 +770 960 771 +772 926 932 +773 721 774 +775 776 956 +777 1197 804 +778 779 827 +894 780 781 +782 846 783 +784 466 896 +766 1579 785 +140 786 393 +787 633 788 +789 578 790 +791 863 584 +792 793 901 +1168 794 795 +796 922 665 +797 788 798 +799 1649 2198 +1031 800 983 +801 802 768 +803 804 136 +599 805 770 +498 806 807 +857 1532 808 +1523 831 809 +810 879 925 +811 1508 1105 +1648 1652 812 +1026 813 1889 +814 887 2174 +815 730 459 +816 774 619 +817 809 818 +819 893 820 +918 1908 821 +822 964 823 +824 825 701 +354 826 1497 +226 827 87 +828 829 800 +830 89 831 +832 833 296 +892 834 627 +835 862 265 +917 836 837 +838 699 1696 +839 840 417 +841 842 871 +843 844 155 +845 674 846 +847 848 1193 +849 916 850 +851 791 301 +1201 852 523 +853 1642 805 +92 1195 854 +1705 855 1857 +856 506 857 +858 736 859 +381 860 1451 +861 535 862 +870 454 863 +864 865 580 +866 816 867 +868 869 826 +870 871 1826 +872 1179 873 +874 1196 875 +876 877 938 +878 900 879 +880 881 557 +882 850 883 +884 761 885 +2074 886 1205 +1694 2089 887 +888 835 836 +889 278 996 +890 728 891 +892 801 893 +224 894 895 +1606 896 503 +897 668 898 +698 959 899 +900 295 760 +676 901 94 +902 859 418 +903 904 1581 +905 474 989 +906 742 1625 +907 908 813 +909 910 1634 +911 912 786 +913 914 987 +915 544 916 +917 966 775 +1688 1062 918 +919 771 920 +921 455 922 +923 847 924 +925 876 926 +927 1005 928 +929 815 930 +931 955 844 +932 933 914 +934 935 982 +936 937 353 +988 938 518 +336 1183 939 +940 953 941 +942 299 943 +944 898 1166 +945 1672 946 +821 1562 947 +948 756 2197 +949 950 855 +951 928 1713 +952 1518 953 +954 962 955 +1845 956 920 +861 957 1591 +1184 958 976 +906 1215 959 +960 961 591 +467 2076 962 +963 551 964 +1929 322 965 +966 1190 524 +967 438 794 +968 965 969 +1666 970 148 +713 1139 971 +972 755 973 +1118 974 765 +975 976 2057 +1897 977 1204 +978 717 979 +980 1239 981 +283 982 792 +1137 735 983 +984 985 824 +986 930 987 +988 989 933 +332 990 991 +1703 992 42 +993 849 904 +994 969 977 +995 996 1699 +669 997 998 +999 231 985 +1000 947 1721 +1001 905 1002 +1003 867 909 +245 1004 1005 +1006 1007 1213 +1008 1009 129 +1010 1011 10 +2251 1012 1226 +1906 1013 1014 +32 71 1015 +1016 1999 1099 +1017 546 2097 +135 1018 1142 +1019 1020 2191 +1021 897 1088 +869 785 1022 +2001 2081 1023 +1024 1065 1025 +1026 708 1027 +1028 1029 501 +56 1063 1030 +1031 1077 1032 +1033 1136 782 +1015 1034 1877 +1035 1036 1797 +1037 563 1138 +1038 1039 1125 +189 1040 1883 +2090 1041 1042 +1043 1117 667 +723 1529 1044 +435 1045 1046 +793 1743 1047 +1048 1025 1049 +1050 15 1727 +1072 1051 1052 +1055 1053 1054 +1013 1717 1055 +1056 1057 1181 +1058 1189 1059 +1060 840 1061 +1064 1062 1063 +18 1910 1064 +1065 1032 1066 +1067 1068 1306 +1069 2041 181 +1070 1188 1071 +980 1044 1072 +1073 1173 1074 +1075 1076 1133 +1077 1128 1078 +803 1079 1198 +1080 1209 1081 +1082 1047 1083 +1086 1084 1085 +547 2042 1086 +1087 1088 1185 +1089 1928 1090 +1091 1106 1092 +1095 2234 1093 +1094 1108 1095 +1156 1096 1097 +1102 1098 1099 +1022 1392 1100 +1101 984 1120 +1161 1102 2171 +1103 829 1192 +2080 1104 1733 +1105 1106 1207 +1109 1107 1108 +1216 1539 1109 +1112 125 1110 +1111 1433 1112 +1113 1901 1114 +1081 1115 1116 +1117 1151 203 +1118 1119 618 +1120 1121 1141 +1122 1123 1149 +1124 188 1119 +1125 173 1177 +2084 1126 270 +1127 1135 1128 +1129 1130 1148 +1131 1163 1132 +1133 942 1140 +1134 1135 1200 +1136 1137 1150 +1194 1138 1139 +212 1140 1154 +842 1141 1155 +1142 349 1160 +607 1143 311 +1144 2104 1777 +1145 510 1146 +990 1114 1147 +1148 1575 1202 +941 1149 1206 +1592 1150 1103 +1165 1505 1151 +979 992 1152 +2054 1083 1153 +1154 356 1018 +995 1090 1155 +1156 1152 1157 +1504 939 1158 +1159 1027 737 +1160 1516 1079 +1161 1912 1162 +1163 1167 1164 +1165 269 1170 +1166 1048 1167 +1168 1071 706 +405 613 1169 +255 1170 1076 +1056 1171 1172 +1173 1092 1174 +1178 1175 1176 +573 1177 1021 +1178 1098 2210 +1179 1116 449 +1180 1181 1485 +1644 1147 1182 +1183 1169 1184 +1185 272 1203 +1186 1187 1389 +512 1188 1045 +875 1700 1189 +1190 1061 1191 +1586 1192 1039 +1193 1172 819 +1354 1194 1195 +1196 1738 1197 +1697 1198 290 +1199 1200 1143 +1201 1202 1146 +631 1203 709 +1204 1059 277 +1205 1206 317 +490 1207 232 +1069 1208 1084 +1209 1132 1210 +1211 1074 1212 +193 1213 1186 +1214 1215 1681 +1216 1217 38 +1218 1785 1407 +1224 1219 1220 +1221 1222 2218 +1219 1277 1223 +1224 362 1225 +1226 1706 2095 +1231 1227 1228 +1372 1229 1300 +1232 1230 1231 +2245 1258 1232 +1260 1233 1234 +1235 1236 1347 +1360 1262 1237 +431 197 1238 +1187 1239 1214 +363 1756 1240 +1220 1241 1242 +2160 1243 319 +1377 1283 1244 +1245 1246 1533 +1241 2108 1247 +2047 1248 1249 +1751 741 1250 +1914 1251 1707 +1715 1252 1254 +1253 2101 1254 +1295 1255 600 +1279 1256 1255 +2247 1228 1257 +1838 1234 1258 +1237 1259 1260 +1238 1261 1262 +1249 1263 1264 +1841 1766 1265 +214 2164 1266 +1244 1267 1268 +1269 1317 2180 +1750 1768 1270 +1271 297 1272 +1275 1273 1274 +1225 1943 1275 +1221 2249 1276 +1277 1274 1278 +1279 1288 1280 +1281 1282 286 +1283 1247 1284 +740 1285 2106 +1282 1286 1629 +1287 1266 1773 +1288 1936 1771 +1289 96 3 +1770 1290 1291 +104 1292 1293 +1294 1295 1757 +1296 1297 1144 +1298 1276 1752 +1299 1278 1020 +1300 1301 1310 +1510 1302 1662 +1761 1303 1619 +182 1304 1014 +1305 1306 1439 +1794 1307 679 +1308 1309 51 +1391 1310 1402 +1311 1312 1712 +2099 1336 1313 +1905 1314 1315 +1316 1317 1476 +44 1686 1318 +1319 1320 1787 +1321 1438 1385 +1322 1263 1323 +1324 1325 1803 +1983 1326 1316 +116 1327 2028 +1328 1329 2017 +1330 1331 1568 +1791 1332 1345 +1333 2181 1326 +1334 1010 1723 +1335 1390 1336 +2184 1955 1337 +1338 1339 1412 +1340 1420 1358 +1341 1342 2068 +1343 1344 1417 +122 1345 1346 +453 1347 1762 +1348 1349 1243 +1833 1350 1348 +1416 1351 1356 +1346 1352 1630 +1353 1354 1436 +115 29 1355 +1356 2183 1340 +1380 1357 1358 +1359 1360 1837 +1361 2070 402 +1362 1363 753 +1290 1892 1364 +1365 1741 1208 +1366 1367 103 +1368 1369 1401 +1404 1370 410 +1371 1357 1372 +1373 1374 697 +1339 1375 1376 +1242 1377 1378 +1379 1380 2013 +1381 1382 1330 +1370 1400 1383 +1384 1397 1385 +1386 1387 1748 +1388 1389 1311 +1390 1415 1391 +2079 1392 1393 +1411 1394 1395 +432 1396 36 +2011 1397 2124 +2032 1398 1399 +1422 1399 1308 +1264 1400 2015 +6 1301 1401 +1402 1796 70 +1305 1403 1404 +1405 394 621 +1406 1407 1332 +1806 1408 1495 +1409 1410 1979 +1789 2237 1411 +1355 1412 1413 +1647 1414 1405 +1371 1415 1416 +1417 1418 2156 +2045 1419 1328 +2014 1420 1333 +1359 1951 1421 +1395 1422 143 +807 1423 1307 +1426 1424 1425 +2158 1926 1426 +1314 1427 1428 +1429 1430 1795 +1303 1431 1432 +1176 1433 1434 +1435 1436 1006 +1418 1437 1427 +1438 1439 1872 +1440 1441 1437 +80 1442 74 +1831 315 1443 +1443 1444 1824 +1445 1446 95 +1425 1447 1448 +1468 1449 1450 +1450 1451 1452 +131 1453 1454 +1454 1455 41 +1456 1457 1458 +1458 1807 139 +154 1459 1460 +403 1461 973 +1462 1463 1464 +1464 1782 2051 +406 1465 1466 +971 646 1467 +1467 1468 1469 +1470 1471 1472 +1472 46 1396 +1261 1473 1474 +1474 1475 284 +1376 1476 1804 +1447 1477 1470 +1956 1478 1479 +1479 1284 1285 +1350 2225 1480 +1480 1481 1708 +175 1482 1483 +1483 1484 2151 +1364 1485 1486 +239 1487 78 +287 1488 1489 +1489 1490 268 +710 1491 1492 +1492 456 1493 +1859 1494 688 +1329 1495 1008 +1512 339 1496 +1496 1497 1498 +1676 267 1499 +1499 1500 1501 +934 220 1502 +1502 1503 1504 +263 1505 1506 +1506 1507 1508 +1509 1510 2122 +968 108 1511 +1511 1512 1513 +84 874 1514 +1514 1515 1516 +1684 1122 1517 +1517 1518 1519 +1520 1521 1522 +1522 1523 258 +1524 1525 1526 +1526 1812 1457 +1531 1435 1527 +1527 1528 1529 +114 1353 1530 +1530 1531 1532 +1533 970 1309 +328 1453 1534 +1534 1535 1830 +20 1536 1537 +1538 1539 1050 +1537 1540 1541 +1542 1543 1235 +1544 1545 389 +280 1546 1547 +1547 1548 1549 +1550 1551 1552 +1552 1236 413 +442 1553 1554 +1948 1764 1555 +533 595 1556 +1556 1557 545 +391 1558 1559 +397 1560 1561 +1562 1563 1334 +408 1564 1565 +448 1566 1567 +1567 1568 1569 +1864 1570 1571 +1571 1572 1386 +590 1573 1574 +1574 555 1575 +1611 477 1576 +1576 1577 1578 +93 1579 1580 +1580 1581 585 +950 1582 1583 +1583 1564 380 +411 1584 1585 +1585 376 386 +1594 1586 1587 +1587 1588 1078 +481 1612 1589 +1589 1590 1591 +626 1592 1593 +1593 1594 1134 +751 1171 1595 +1595 1596 1597 +504 1598 608 +1599 1600 1995 +1596 1180 1601 +1601 1602 1603 +304 715 1604 +1604 1605 1606 +1057 1607 1608 +1608 1609 1674 +1003 550 1610 +1610 1611 1612 +190 1613 1614 +806 1615 1616 +1616 1542 494 +650 1617 1618 +1618 1550 1553 +2060 1619 1620 +1621 1622 1623 +1623 1624 694 +1036 1625 1639 +907 1626 1627 +1627 1628 1629 +149 1630 1631 +1631 1632 687 +1847 1633 1634 +1432 1635 1620 +217 1636 1637 +1637 1638 275 +747 1639 1640 +1640 1641 2146 +820 1642 1643 +1643 954 141 +310 1644 1645 +1645 1646 1647 +1648 1649 1650 +1650 1651 1654 +2072 1652 1653 +1653 1654 23 +1900 1291 1655 +1655 1656 1657 +425 1658 1659 +1659 1660 1919 +1578 91 1661 +1661 1662 1590 +1922 1663 1664 +1664 1665 1646 +1675 1666 2235 +848 1667 1668 +1668 1669 1607 +729 1670 1671 +1671 536 1672 +1656 1486 1673 +1673 1674 1675 +166 1676 1677 +1677 1678 233 +767 1493 1679 +1679 1680 1520 +1312 1681 1682 +1682 1683 951 +1388 1684 1685 +1685 1686 276 +1104 1719 1687 +1687 1688 1689 +138 1028 1689 +1690 1691 1692 +1692 1693 1663 +1041 1694 1695 +1695 1696 1000 +777 1697 1698 +1698 1699 1700 +2033 1431 1701 +1701 1702 891 +1638 1703 1704 +1704 1705 911 +1706 1707 97 +1708 1709 1714 +1538 1710 2232 +1711 908 1286 +1712 1713 1123 +1714 1715 683 +1029 1488 1716 +1716 1717 1718 +1030 1719 1720 +702 1721 1722 +1722 1723 60 +1724 1725 222 +1726 1727 1728 +1728 34 1110 +1729 1730 1731 +1731 1732 1211 +1733 1734 1023 +681 1735 1736 +1737 1738 1739 +1739 1740 1730 +1741 814 2092 +1742 81 137 +963 1743 1744 +1744 935 1158 +2077 1745 1746 +1746 1498 1100 +2030 1325 1747 +1748 1749 368 +2216 1750 1751 +1019 1752 2248 +1753 440 1271 +1268 1754 1755 +1755 1756 1378 +2147 1757 752 +2109 1758 1759 +738 1760 1761 +1762 1763 1939 +1945 1952 1764 +1765 1766 1240 +1767 1768 1252 +1383 1322 1769 +1917 1923 1770 +1771 2112 1772 +1772 1773 1774 +2223 1775 1776 +1776 1777 1778 +1779 211 198 +1848 1780 625 +684 1781 1782 +2153 1783 163 +1 2019 1784 +361 1785 1786 +1786 1787 1341 +2007 2003 1788 +1788 1789 1790 +1880 2140 1791 +1218 2230 1792 +1792 1793 1319 +1381 1566 1794 +2209 1795 1781 +153 1796 1797 +1870 1798 1753 +1799 1800 1327 +1935 1801 1802 +1802 1803 1800 +1804 1269 2087 +1805 1806 1419 +1807 1808 1809 +1809 1810 54 +5 1297 1811 +1808 1812 1813 +1813 1814 1815 +1977 1338 1816 +1816 1817 1409 +1854 1971 1818 +1818 1819 1820 +1821 1822 1823 +1823 1824 1429 +822 921 1825 +1825 1826 596 +45 1230 1827 +1827 1828 1829 +1830 1831 1832 +1832 1822 145 +2113 1833 1834 +2244 1835 1836 +1836 1837 1838 +430 1944 1839 +1839 1840 1841 +1293 1842 1843 +1843 1844 367 +888 1845 1846 +1846 1847 726 +2166 1848 1849 +1849 575 1256 +1545 1850 1851 +1851 1852 26 +1853 1854 1855 +1855 2137 111 +422 912 1856 +1856 1857 1858 +1866 1859 1860 +1860 1861 1873 +1861 1862 1863 +1863 1864 369 +1292 1494 1865 +1865 1866 1867 +119 2114 1868 +1868 1869 1870 +246 2036 1871 +1871 1872 560 +1599 1873 1874 +1874 1875 1876 +748 1877 1878 +1878 1879 1374 +2134 1880 1881 +1881 1882 1974 +1883 1884 1885 +1885 1876 1561 +972 1461 1886 +1886 1887 1888 +707 1889 1890 +1890 1891 221 +1602 1892 1893 +1893 1894 2129 +1903 946 1895 +1895 1896 1897 +1898 1918 1899 +1899 1900 1070 +424 1901 1902 +1902 1903 1491 +1904 1905 2136 +1906 1251 1907 +1563 1908 1909 +1909 1910 1011 +1911 1912 1913 +1913 1914 1012 +1129 1915 1916 +1916 1917 1918 +1919 1920 1921 +1921 1922 1182 +1894 1923 1924 +1924 1925 1926 +185 597 1927 +1927 1928 1929 +1930 1931 1932 +1932 1933 1967 +1934 1935 2088 +1775 1936 1937 +1937 1294 1296 +2021 444 1938 +1938 1939 1940 +1950 1941 1942 +1942 1943 1944 +1954 1945 1946 +1946 1947 1835 +1947 1948 1949 +1949 1950 1951 +2213 1952 1953 +1953 1954 1955 +1603 1956 1957 +1957 1767 2149 +1958 1959 1960 +1960 1961 1375 +1373 1888 1962 +1962 1963 1964 +1440 1965 1966 +1966 1967 1968 +1779 1765 1969 +1969 1970 1971 +1869 2163 1972 +1972 1973 285 +2206 1974 1975 +1975 1245 1398 +2008 1976 2138 +151 1977 1978 +1978 1979 2004 +1980 1981 1982 +1982 1983 1961 +1984 1985 1217 +2038 1986 1987 +1987 1988 2241 +1318 1519 1989 +1989 1990 1227 +1963 2073 1991 +1991 1992 1993 +27 1867 1994 +1994 1995 1996 +1790 1997 1998 +1998 58 1976 +2173 1999 2000 +2000 2001 2002 +2003 2004 2005 +2005 2006 2239 +2007 2008 2009 +2009 2010 152 +1981 2011 2012 +2012 2013 2014 +1959 2015 2016 +2016 1403 553 +1248 2017 2018 +2018 2019 2025 +2020 812 1361 +2021 2022 2023 +2023 2024 1387 +1323 2025 2026 +2026 2027 409 +2229 2028 2029 +2029 2030 1793 +2031 2032 1394 +2033 2034 2035 +2035 1624 1635 +1384 2036 2037 +2037 927 1369 +2187 2038 2039 +2039 2040 30 +2041 2042 2043 +2043 98 1304 +2044 2045 2046 +2046 2047 2010 +1446 2048 2049 +2049 2050 22 +324 2051 2052 +2052 1430 1444 +1726 124 2053 +2054 2055 2056 +2056 1115 632 +1096 2057 2058 +2058 581 2059 +2060 1622 2061 +2061 2062 739 +24 2063 2064 +2064 2065 2066 +2067 2068 2069 +2069 64 1632 +1887 2070 2071 +2071 2072 2073 +2074 787 2075 +2075 2076 878 +2055 2077 2078 +2078 2079 450 +2080 2081 2082 +2082 86 1720 +2083 2084 2085 +2085 1891 1711 +1799 1413 2086 +2086 2087 2088 +2089 2090 2091 +2091 39 2175 +2092 2093 2094 +2094 2195 1085 +2233 2095 2096 +2096 2097 1093 +52 1335 2098 +2098 2099 1324 +2100 2101 2102 +2102 2186 1257 +1481 1778 2103 +2103 2104 2105 +1250 2106 2107 +2107 2108 2222 +2109 2110 2111 +2111 2112 1724 +2226 2113 2114 +1758 2115 2116 +2116 2117 201 +1973 4 2118 +2118 2119 1281 +2120 1760 1365 +1287 2110 2121 +2121 2122 90 +1229 1379 2123 +2123 2124 1368 +2170 2125 2126 +2126 2127 695 +2128 2120 1046 +1478 2129 2130 +2130 2131 1267 +1986 2132 2133 +2133 2134 2204 +1820 1736 2135 +2135 2136 2137 +2044 2138 2139 +2139 57 1805 +2140 2132 2141 +2141 2142 1406 +2125 2143 2144 +2144 2145 2146 +2147 1597 2148 +2148 2149 1811 +2150 2151 2152 +2152 2153 1343 +1455 327 2154 +2154 2155 68 +2150 2156 2157 +2157 1904 176 +2131 2158 2159 +2159 294 325 +2160 2161 2162 +2162 2163 1834 +1774 2164 2165 +2165 2166 1280 +2167 2168 2169 +2169 2170 884 +1907 2171 2172 +2172 2173 1718 +2174 2175 2176 +2176 1985 2093 +1754 2177 2178 +2178 2179 1970 +2180 2181 2182 +2182 2183 1934 +2215 2184 2185 +2185 2186 1222 +2187 2188 2227 +2203 2189 2190 +2190 2243 1988 +2212 2191 2192 +2192 375 1555 +1984 1094 2193 +2193 2194 2195 +2020 885 2196 +2196 2197 2198 +2202 2040 2199 +1410 2200 2201 +2201 2202 2006 +2203 2204 2205 +2205 2206 2031 +358 1484 2207 +2207 2208 2209 +2053 2210 2211 +2211 1162 1710 +2212 2213 2214 +2214 2215 1298 +2216 2220 2217 +2217 2218 1253 +2219 2220 2221 +2221 2222 1223 +1725 2223 2224 +2224 2225 2226 +2142 2227 2228 +2228 2229 2230 +2231 2232 1911 +2252 2233 2234 +1657 2235 2236 +2236 1702 2128 +2189 2237 2238 +2238 2239 2240 +2199 2241 2242 +2242 2243 2240 +2244 2245 2246 +2246 2247 1337 +2248 2249 2250 +2250 2219 1299 +2251 2252 2253 +2253 1107 2231 diff --git a/SkeinPyPy/models/box.obj b/SkeinPyPy/models/box.obj new file mode 100644 index 0000000..1e1c785 --- /dev/null +++ b/SkeinPyPy/models/box.obj @@ -0,0 +1,79 @@ +# ----------------- +# Start of obj file +g Box01 +mtllib box.mat +usemtl box +v -62.0579 -41.4791 0.0 +v 58.8424 -41.4791 0.0 +v -62.0579 22.1865 0.0 +v 58.8424 22.1865 0.0 +v -62.0579 -41.4791 39.8714 +v 58.8424 -41.4791 39.8714 +v -62.0579 22.1865 39.8714 +v 58.8424 22.1865 39.8714 + +vt 0.843206 0.405444 0.000499517 +vt 0.482802 0.71377 0.9995 +vt 0.478066 0.404023 0.000499636 +vt 0.482802 0.716612 0.9995 +vt 0.841627 0.688332 0.000499517 +vt 0.482013 0.981029 0.9995 +vt 0.480434 0.688332 0.000499636 +vt 0.485959 0.978188 0.9995 +vt 0.450102 0.00618343 0.000499547 +vt 0.45247 0.509304 0.000499547 +vt 0.000499517 0.512146 0.000499547 +vt 0.000499517 0.512146 0.000499547 +vt -0.0010791 0.00618302 0.000499547 +vt 0.450102 0.00618343 0.000499547 +vt 0.000499517 0.512009 0.9995 +vt 0.450891 0.510588 0.9995 +vt 0.45247 0.995237 0.9995 +vt 0.45247 0.996658 0.9995 +vt 0.000499636 0.9995 0.9995 +vt 0.000499517 0.51343 0.9995 +vt 0.478855 0.405444 0.000500023 +vt 0.841627 0.408286 0.000499576 +vt 0.83847 0.688332 0.000499576 +vt 0.83847 0.688332 0.000499576 +vt 0.477276 0.694016 0.000500023 +vt 0.478855 0.405444 0.000500023 +vt 0.482802 0.71377 0.9995 +vt 0.845574 0.71377 0.999501 +vt 0.844784 0.976767 0.999501 +vt 0.844784 0.976767 0.999501 +vt 0.482802 0.716612 0.9995 +vt 0.842417 0.710929 0.9995 +vt 0.843995 0.975346 0.9995 +vt 0.843995 0.975346 0.9995 +vt 0.478066 0.404023 0.000499636 +vt 0.841627 0.688332 0.000499517 + +vn 0.0 0.0 -1.0 +vn 0.0 0.0 -1.0 +vn 0.0 0.0 1.0 +vn 0.0 0.0 1.0 +vn 0.0 -1.0 0.0 +vn 0.0 -1.0 0.0 +vn 1.0 0.0 0.0 +vn 1.0 0.0 0.0 +vn 0.0 1.0 0.0 +vn 0.0 1.0 0.0 +vn -1.0 0.0 0.0 +vn -1.0 0.0 0.0 + +f 1/9/1 3/10/1 4/11/1 +f 4/12/2 2/13/2 1/14/2 +f 5/15/3 6/16/3 8/17/3 +f 8/18/4 7/19/4 5/20/4 +f 1/21/5 2/22/5 6/23/5 +f 6/24/6 5/25/6 1/26/6 +f 2/27/7 4/28/7 8/29/7 +f 8/30/8 6/6/8 2/2/8 +f 4/31/9 3/32/9 7/33/9 +f 7/34/10 8/8/10 4/4/10 +f 3/35/11 1/1/11 5/36/11 +f 5/5/12 7/7/12 3/3/12 + +# end of obj file +# --------------- diff --git a/SkeinPyPy/models/inkscape_star.svg b/SkeinPyPy/models/inkscape_star.svg new file mode 100644 index 0000000..8d99449 --- /dev/null +++ b/SkeinPyPy/models/inkscape_star.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/art_of_illusion/art_of_illusion_boolean.xml b/SkeinPyPy/models/xml_models/art_of_illusion/art_of_illusion_boolean.xml new file mode 100644 index 0000000..b426a64 --- /dev/null +++ b/SkeinPyPy/models/xml_models/art_of_illusion/art_of_illusion_boolean.xml @@ -0,0 +1,516 @@ + + + + + + + + + + + + + + + + Default Texture + + + + + + + + + + + + + + + + + + + + + + + + + + Camera 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + Light 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cube 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cube 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Boolean1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/art_of_illusion/art_of_illusion_cylinder.xml b/SkeinPyPy/models/xml_models/art_of_illusion/art_of_illusion_cylinder.xml new file mode 100644 index 0000000..a5c1198 --- /dev/null +++ b/SkeinPyPy/models/xml_models/art_of_illusion/art_of_illusion_cylinder.xml @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + Default Texture + + + + + + + + + + + + + + + + + + + + + + + + + + Camera 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + Light 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cylinder 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/art_of_illusion/art_of_illusion_group.xml b/SkeinPyPy/models/xml_models/art_of_illusion/art_of_illusion_group.xml new file mode 100644 index 0000000..5352bce --- /dev/null +++ b/SkeinPyPy/models/xml_models/art_of_illusion/art_of_illusion_group.xml @@ -0,0 +1,537 @@ + + + + + + + + + + + + + + + + Default Texture + + + + + + + + + + + + + + + + + + + + + + + + + + Camera 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + Light 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + Array of Cylinder 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cylinder 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cylinder 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + Cylinder 2 + + + + + + + + + + + Cylinder 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/art_of_illusion/art_of_illusion_sphere.xml b/SkeinPyPy/models/xml_models/art_of_illusion/art_of_illusion_sphere.xml new file mode 100644 index 0000000..81514fd --- /dev/null +++ b/SkeinPyPy/models/xml_models/art_of_illusion/art_of_illusion_sphere.xml @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + Default Texture + + + + + + + + + + + + + + + + + + + + + + + + + + Camera 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + Light 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sphere1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/art_of_illusion/art_of_illusion_tetrahedron.xml b/SkeinPyPy/models/xml_models/art_of_illusion/art_of_illusion_tetrahedron.xml new file mode 100644 index 0000000..5007bfd --- /dev/null +++ b/SkeinPyPy/models/xml_models/art_of_illusion/art_of_illusion_tetrahedron.xml @@ -0,0 +1,389 @@ + + + + + + + + + + + + + + + + Default Texture + + + + + + + + + + + + + + + + + + + + + + + + + + Camera 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + Light 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + tetrahedron + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/combined.xml b/SkeinPyPy/models/xml_models/combined.xml new file mode 100644 index 0000000..f00fa61 --- /dev/null +++ b/SkeinPyPy/models/xml_models/combined.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/creation/circle.xml b/SkeinPyPy/models/xml_models/creation/circle.xml new file mode 100644 index 0000000..8682a90 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/circle.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/creation/concatenate.xml b/SkeinPyPy/models/xml_models/creation/concatenate.xml new file mode 100644 index 0000000..0a6223f --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/concatenate.xml @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/creation/drill.xml b/SkeinPyPy/models/xml_models/creation/drill.xml new file mode 100644 index 0000000..787befa --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/drill.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/SkeinPyPy/models/xml_models/creation/extrude.xml b/SkeinPyPy/models/xml_models/creation/extrude.xml new file mode 100644 index 0000000..0040fb3 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/extrude.xml @@ -0,0 +1,46 @@ + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/bevel.svg b/SkeinPyPy/models/xml_models/creation/gear/bevel.svg new file mode 100644 index 0000000..a3d348f --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/bevel.svg @@ -0,0 +1,705 @@ + + + + + + + + bevel.xml - Slice Layers + + + + + + Layer 0, z:0.2 + + + + + Layer 1, z:0.6 + + + + + Layer 2, z:1.0 + + + + + Layer 3, z:1.4 + + + + + Layer 4, z:1.8 + + + + + Layer 5, z:2.2 + + + + + Layer 6, z:2.6 + + + + + Layer 7, z:3.0 + + + + + Layer 8, z:3.4 + + + + + Layer 9, z:3.8 + + + + + Layer 10, z:4.2 + + + + + Layer 11, z:4.6 + + + + + Layer 12, z:5.0 + + + + + Layer 13, z:5.4 + + + + + Layer 14, z:5.8 + + + + + Layer 15, z:6.2 + + + + + Layer 16, z:6.6 + + + + + Layer 17, z:7.0 + + + + + Layer 18, z:7.4 + + + + + Layer 19, z:7.8 + + + + + Layer 20, z:8.2 + + + + + Layer 21, z:8.6 + + + + + Layer 22, z:9.0 + + + + + Layer 23, z:9.4 + + + + + Layer 24, z:9.8 + + + + + Layer 25, z:10.2 + + + + + Layer 26, z:10.6 + + + + + Layer 27, z:11.0 + + + + + Layer 28, z:11.4 + + + + + Layer 29, z:11.8 + + + + + Layer 30, z:12.2 + + + + + Layer 31, z:12.6 + + + + + Layer 32, z:13.0 + + + + + Layer 33, z:13.4 + + + + + Layer 34, z:13.8 + + + + + Layer 35, z:14.2 + + + + + Layer 36, z:14.6 + + + + + Layer 37, z:15.0 + + + + + Layer 38, z:15.4 + + + + + + + + + + + + + Latitude + < + > + Longitude + < + > + Scale + 1 + < + > + + Min + X: -49.2741 mm + Y: -148.9752 mm + Z: 0.0 mm + + + Max + X: 49.2741 mm + Y: 21.6669 mm + Z: 15.6 mm + + + Dimension + X: 98.5482 mm + Y: 170.6421 mm + Z: 15.6 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 39 + Volume: 262.5277 cm3 + + + + + + + + Y + X + 0 + + + 1 + Layer + < + > + Scale + 1 + < + > + + Min + X: -49.2741 mm + Y: -148.9752 mm + Z: 0.0 mm + + + Max + X: 49.2741 mm + Y: 21.6669 mm + Z: 15.6 mm + + + Dimension + X: 98.5482 mm + Y: 170.6421 mm + Z: 15.6 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 39 + Volume: 262.5277 cm3 + + + + + + + Y + X + Scale + : 1 + < + > + + Min + X: -49.2741 mm + Y: -148.9752 mm + Z: 0.0 mm + + + Max + X: 49.2741 mm + Y: 21.6669 mm + Z: 15.6 mm + + + Dimension + X: 98.5482 mm + Y: 170.6421 mm + Z: 15.6 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 39 + Volume: 262.5277 cm3 + + + [Iso View] + Iso View + [Layer View] + Layer View + [Scroll View] + Scroll View + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/bevel.xml b/SkeinPyPy/models/xml_models/creation/gear/bevel.xml new file mode 100644 index 0000000..e93586a --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/bevel.xml @@ -0,0 +1,4 @@ + + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/collar.svg b/SkeinPyPy/models/xml_models/creation/gear/collar.svg new file mode 100644 index 0000000..1af847c --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/collar.svg @@ -0,0 +1,760 @@ + + + + + + + + collar.xml - Slice Layers + + + + + + Layer 0, z:0.2 + + + + + Layer 1, z:0.6 + + + + + Layer 2, z:1.0 + + + + + Layer 3, z:1.4 + + + + + Layer 4, z:1.8 + + + + + Layer 5, z:2.2 + + + + + Layer 6, z:2.6 + + + + + Layer 7, z:3.0 + + + + + Layer 8, z:3.4 + + + + + Layer 9, z:3.8 + + + + + Layer 10, z:4.2 + + + + + Layer 11, z:4.6 + + + + + Layer 12, z:5.0 + + + + + Layer 13, z:5.4 + + + + + Layer 14, z:5.8 + + + + + Layer 15, z:6.2 + + + + + Layer 16, z:6.6 + + + + + Layer 17, z:7.0 + + + + + Layer 18, z:7.4 + + + + + Layer 19, z:7.8 + + + + + Layer 20, z:8.2 + + + + + Layer 21, z:8.6 + + + + + Layer 22, z:9.0 + + + + + Layer 23, z:9.4 + + + + + Layer 24, z:9.8 + + + + + Layer 25, z:10.2 + + + + + Layer 26, z:10.6 + + + + + Layer 27, z:11.0 + + + + + Layer 28, z:11.4 + + + + + Layer 29, z:11.8 + + + + + Layer 30, z:12.2 + + + + + Layer 31, z:12.6 + + + + + Layer 32, z:13.0 + + + + + Layer 33, z:13.4 + + + + + Layer 34, z:13.8 + + + + + Layer 35, z:14.2 + + + + + Layer 36, z:14.6 + + + + + Layer 37, z:15.0 + + + + + Layer 38, z:15.4 + + + + + Layer 39, z:15.8 + + + + + Layer 40, z:16.2 + + + + + Layer 41, z:16.6 + + + + + Layer 42, z:17.0 + + + + + Layer 43, z:17.4 + + + + + Layer 44, z:17.8 + + + + + Layer 45, z:18.2 + + + + + Layer 46, z:18.6 + + + + + Layer 47, z:19.0 + + + + + Layer 48, z:19.4 + + + + + Layer 49, z:19.8 + + + + + + + + + + + + + Latitude + < + > + Longitude + < + > + Scale + 1 + < + > + + Min + X: -50.4319 mm + Y: -150.126 mm + Z: -0.0 mm + + + Max + X: 50.4319 mm + Y: 21.8248 mm + Z: 20.0 mm + + + Dimension + X: 100.8638 mm + Y: 171.9508 mm + Z: 20.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 50 + Volume: 164.3011 cm3 + + + + + + + + Y + X + 0 + + + 1 + Layer + < + > + Scale + 1 + < + > + + Min + X: -50.4319 mm + Y: -150.126 mm + Z: -0.0 mm + + + Max + X: 50.4319 mm + Y: 21.8248 mm + Z: 20.0 mm + + + Dimension + X: 100.8638 mm + Y: 171.9508 mm + Z: 20.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 50 + Volume: 164.3011 cm3 + + + + + + + Y + X + Scale + : 1 + < + > + + Min + X: -50.4319 mm + Y: -150.126 mm + Z: -0.0 mm + + + Max + X: 50.4319 mm + Y: 21.8248 mm + Z: 20.0 mm + + + Dimension + X: 100.8638 mm + Y: 171.9508 mm + Z: 20.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 50 + Volume: 164.3011 cm3 + + + [Iso View] + Iso View + [Layer View] + Layer View + [Scroll View] + Scroll View + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/collar.xml b/SkeinPyPy/models/xml_models/creation/gear/collar.xml new file mode 100644 index 0000000..c775fc3 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/collar.xml @@ -0,0 +1,4 @@ + + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/gear.svg b/SkeinPyPy/models/xml_models/creation/gear/gear.svg new file mode 100644 index 0000000..7c39e00 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/gear.svg @@ -0,0 +1,635 @@ + + + + + + + + gear.xml - Slice Layers + + + + + + Layer 0, z:0.2 + + + + + Layer 1, z:0.6 + + + + + Layer 2, z:1.0 + + + + + Layer 3, z:1.4 + + + + + Layer 4, z:1.8 + + + + + Layer 5, z:2.2 + + + + + Layer 6, z:2.6 + + + + + Layer 7, z:3.0 + + + + + Layer 8, z:3.4 + + + + + Layer 9, z:3.8 + + + + + Layer 10, z:4.2 + + + + + Layer 11, z:4.6 + + + + + Layer 12, z:5.0 + + + + + Layer 13, z:5.4 + + + + + Layer 14, z:5.8 + + + + + Layer 15, z:6.2 + + + + + Layer 16, z:6.6 + + + + + Layer 17, z:7.0 + + + + + Layer 18, z:7.4 + + + + + Layer 19, z:7.8 + + + + + Layer 20, z:8.2 + + + + + Layer 21, z:8.6 + + + + + Layer 22, z:9.0 + + + + + Layer 23, z:9.4 + + + + + Layer 24, z:9.8 + + + + + + + + + + + + + Latitude + < + > + Longitude + < + > + Scale + 1 + < + > + + Min + X: -50.4319 mm + Y: -150.126 mm + Z: -0.0 mm + + + Max + X: 50.4319 mm + Y: 21.8248 mm + Z: 10.0 mm + + + Dimension + X: 100.8638 mm + Y: 171.9508 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 151.0364 cm3 + + + + + + + + Y + X + 0 + + + 1 + Layer + < + > + Scale + 1 + < + > + + Min + X: -50.4319 mm + Y: -150.126 mm + Z: -0.0 mm + + + Max + X: 50.4319 mm + Y: 21.8248 mm + Z: 10.0 mm + + + Dimension + X: 100.8638 mm + Y: 171.9508 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 151.0364 cm3 + + + + + + + Y + X + Scale + : 1 + < + > + + Min + X: -50.4319 mm + Y: -150.126 mm + Z: -0.0 mm + + + Max + X: 50.4319 mm + Y: 21.8248 mm + Z: 10.0 mm + + + Dimension + X: 100.8638 mm + Y: 171.9508 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 151.0364 cm3 + + + [Iso View] + Iso View + [Layer View] + Layer View + [Scroll View] + Scroll View + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/gear.xml b/SkeinPyPy/models/xml_models/creation/gear/gear.xml new file mode 100644 index 0000000..0843e61 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/gear.xml @@ -0,0 +1,4 @@ + + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/keyway.svg b/SkeinPyPy/models/xml_models/creation/gear/keyway.svg new file mode 100644 index 0000000..3210cf2 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/keyway.svg @@ -0,0 +1,760 @@ + + + + + + + + keyway.xml - Slice Layers + + + + + + Layer 0, z:0.2 + + + + + Layer 1, z:0.6 + + + + + Layer 2, z:1.0 + + + + + Layer 3, z:1.4 + + + + + Layer 4, z:1.8 + + + + + Layer 5, z:2.2 + + + + + Layer 6, z:2.6 + + + + + Layer 7, z:3.0 + + + + + Layer 8, z:3.4 + + + + + Layer 9, z:3.8 + + + + + Layer 10, z:4.2 + + + + + Layer 11, z:4.6 + + + + + Layer 12, z:5.0 + + + + + Layer 13, z:5.4 + + + + + Layer 14, z:5.8 + + + + + Layer 15, z:6.2 + + + + + Layer 16, z:6.6 + + + + + Layer 17, z:7.0 + + + + + Layer 18, z:7.4 + + + + + Layer 19, z:7.8 + + + + + Layer 20, z:8.2 + + + + + Layer 21, z:8.6 + + + + + Layer 22, z:9.0 + + + + + Layer 23, z:9.4 + + + + + Layer 24, z:9.8 + + + + + Layer 25, z:10.2 + + + + + Layer 26, z:10.6 + + + + + Layer 27, z:11.0 + + + + + Layer 28, z:11.4 + + + + + Layer 29, z:11.8 + + + + + Layer 30, z:12.2 + + + + + Layer 31, z:12.6 + + + + + Layer 32, z:13.0 + + + + + Layer 33, z:13.4 + + + + + Layer 34, z:13.8 + + + + + Layer 35, z:14.2 + + + + + Layer 36, z:14.6 + + + + + Layer 37, z:15.0 + + + + + Layer 38, z:15.4 + + + + + Layer 39, z:15.8 + + + + + Layer 40, z:16.2 + + + + + Layer 41, z:16.6 + + + + + Layer 42, z:17.0 + + + + + Layer 43, z:17.4 + + + + + Layer 44, z:17.8 + + + + + Layer 45, z:18.2 + + + + + Layer 46, z:18.6 + + + + + Layer 47, z:19.0 + + + + + Layer 48, z:19.4 + + + + + Layer 49, z:19.8 + + + + + + + + + + + + + Latitude + < + > + Longitude + < + > + Scale + 1 + < + > + + Min + X: -50.4319 mm + Y: -150.126 mm + Z: -0.0 mm + + + Max + X: 50.4319 mm + Y: 21.8248 mm + Z: 20.0 mm + + + Dimension + X: 100.8638 mm + Y: 171.9508 mm + Z: 20.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 50 + Volume: 164.533 cm3 + + + + + + + + Y + X + 0 + + + 1 + Layer + < + > + Scale + 1 + < + > + + Min + X: -50.4319 mm + Y: -150.126 mm + Z: -0.0 mm + + + Max + X: 50.4319 mm + Y: 21.8248 mm + Z: 20.0 mm + + + Dimension + X: 100.8638 mm + Y: 171.9508 mm + Z: 20.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 50 + Volume: 164.533 cm3 + + + + + + + Y + X + Scale + : 1 + < + > + + Min + X: -50.4319 mm + Y: -150.126 mm + Z: -0.0 mm + + + Max + X: 50.4319 mm + Y: 21.8248 mm + Z: 20.0 mm + + + Dimension + X: 100.8638 mm + Y: 171.9508 mm + Z: 20.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 50 + Volume: 164.533 cm3 + + + [Iso View] + Iso View + [Layer View] + Layer View + [Scroll View] + Scroll View + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/keyway.xml b/SkeinPyPy/models/xml_models/creation/gear/keyway.xml new file mode 100644 index 0000000..0a780a8 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/keyway.xml @@ -0,0 +1,4 @@ + + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/rack.svg b/SkeinPyPy/models/xml_models/creation/gear/rack.svg new file mode 100644 index 0000000..5cd250c --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/rack.svg @@ -0,0 +1,635 @@ + + + + + + + + rack.xml - Slice Layers + + + + + + Layer 0, z:0.2 + + + + + Layer 1, z:0.6 + + + + + Layer 2, z:1.0 + + + + + Layer 3, z:1.4 + + + + + Layer 4, z:1.8 + + + + + Layer 5, z:2.2 + + + + + Layer 6, z:2.6 + + + + + Layer 7, z:3.0 + + + + + Layer 8, z:3.4 + + + + + Layer 9, z:3.8 + + + + + Layer 10, z:4.2 + + + + + Layer 11, z:4.6 + + + + + Layer 12, z:5.0 + + + + + Layer 13, z:5.4 + + + + + Layer 14, z:5.8 + + + + + Layer 15, z:6.2 + + + + + Layer 16, z:6.6 + + + + + Layer 17, z:7.0 + + + + + Layer 18, z:7.4 + + + + + Layer 19, z:7.8 + + + + + Layer 20, z:8.2 + + + + + Layer 21, z:8.6 + + + + + Layer 22, z:9.0 + + + + + Layer 23, z:9.4 + + + + + Layer 24, z:9.8 + + + + + + + + + + + + + Latitude + < + > + Longitude + < + > + Scale + 1 + < + > + + Min + X: -62.8319 mm + Y: -54.0 mm + Z: -0.0 mm + + + Max + X: 62.8319 mm + Y: 21.8248 mm + Z: 10.0 mm + + + Dimension + X: 125.6637 mm + Y: 75.8248 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 55.6493 cm3 + + + + + + + + Y + X + 0 + + + 1 + Layer + < + > + Scale + 1 + < + > + + Min + X: -62.8319 mm + Y: -54.0 mm + Z: -0.0 mm + + + Max + X: 62.8319 mm + Y: 21.8248 mm + Z: 10.0 mm + + + Dimension + X: 125.6637 mm + Y: 75.8248 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 55.6493 cm3 + + + + + + + Y + X + Scale + : 1 + < + > + + Min + X: -62.8319 mm + Y: -54.0 mm + Z: -0.0 mm + + + Max + X: 62.8319 mm + Y: 21.8248 mm + Z: 10.0 mm + + + Dimension + X: 125.6637 mm + Y: 75.8248 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 55.6493 cm3 + + + [Iso View] + Iso View + [Layer View] + Layer View + [Scroll View] + Scroll View + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/rack.xml b/SkeinPyPy/models/xml_models/creation/gear/rack.xml new file mode 100644 index 0000000..2f01a1d --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/rack.xml @@ -0,0 +1,4 @@ + + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/rack_hole.svg b/SkeinPyPy/models/xml_models/creation/gear/rack_hole.svg new file mode 100644 index 0000000..022f523 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/rack_hole.svg @@ -0,0 +1,635 @@ + + + + + + + + rack_hole.xml - Slice Layers + + + + + + Layer 0, z:0.2 + + + + + Layer 1, z:0.6 + + + + + Layer 2, z:1.0 + + + + + Layer 3, z:1.4 + + + + + Layer 4, z:1.8 + + + + + Layer 5, z:2.2 + + + + + Layer 6, z:2.6 + + + + + Layer 7, z:3.0 + + + + + Layer 8, z:3.4 + + + + + Layer 9, z:3.8 + + + + + Layer 10, z:4.2 + + + + + Layer 11, z:4.6 + + + + + Layer 12, z:5.0 + + + + + Layer 13, z:5.4 + + + + + Layer 14, z:5.8 + + + + + Layer 15, z:6.2 + + + + + Layer 16, z:6.6 + + + + + Layer 17, z:7.0 + + + + + Layer 18, z:7.4 + + + + + Layer 19, z:7.8 + + + + + Layer 20, z:8.2 + + + + + Layer 21, z:8.6 + + + + + Layer 22, z:9.0 + + + + + Layer 23, z:9.4 + + + + + Layer 24, z:9.8 + + + + + + + + + + + + + Latitude + < + > + Longitude + < + > + Scale + 1 + < + > + + Min + X: -62.8319 mm + Y: -64.0 mm + Z: -0.0 mm + + + Max + X: 62.8319 mm + Y: 21.8248 mm + Z: 10.0 mm + + + Dimension + X: 125.6637 mm + Y: 85.8248 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 81.0238 cm3 + + + + + + + + Y + X + 0 + + + 1 + Layer + < + > + Scale + 1 + < + > + + Min + X: -62.8319 mm + Y: -64.0 mm + Z: -0.0 mm + + + Max + X: 62.8319 mm + Y: 21.8248 mm + Z: 10.0 mm + + + Dimension + X: 125.6637 mm + Y: 85.8248 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 81.0238 cm3 + + + + + + + Y + X + Scale + : 1 + < + > + + Min + X: -62.8319 mm + Y: -64.0 mm + Z: -0.0 mm + + + Max + X: 62.8319 mm + Y: 21.8248 mm + Z: 10.0 mm + + + Dimension + X: 125.6637 mm + Y: 85.8248 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 81.0238 cm3 + + + [Iso View] + Iso View + [Layer View] + Layer View + [Scroll View] + Scroll View + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/rack_hole.xml b/SkeinPyPy/models/xml_models/creation/gear/rack_hole.xml new file mode 100644 index 0000000..43f719e --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/rack_hole.xml @@ -0,0 +1,4 @@ + + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/ring.svg b/SkeinPyPy/models/xml_models/creation/gear/ring.svg new file mode 100644 index 0000000..6930208 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/ring.svg @@ -0,0 +1,700 @@ + + + + + + + + ring.xml - Slice Layers + + + + + + Layer 0, z:0.2 + + + + + Layer 1, z:0.6 + + + + + Layer 2, z:1.0 + + + + + Layer 3, z:1.4 + + + + + Layer 4, z:1.8 + + + + + Layer 5, z:2.2 + + + + + Layer 6, z:2.6 + + + + + Layer 7, z:3.0 + + + + + Layer 8, z:3.4 + + + + + Layer 9, z:3.8 + + + + + Layer 10, z:4.2 + + + + + Layer 11, z:4.6 + + + + + Layer 12, z:5.0 + + + + + Layer 13, z:5.4 + + + + + Layer 14, z:5.8 + + + + + Layer 15, z:6.2 + + + + + Layer 16, z:6.6 + + + + + Layer 17, z:7.0 + + + + + Layer 18, z:7.4 + + + + + Layer 19, z:7.8 + + + + + Layer 20, z:8.2 + + + + + Layer 21, z:8.6 + + + + + Layer 22, z:9.0 + + + + + Layer 23, z:9.4 + + + + + Layer 24, z:9.8 + + + + + Layer 25, z:10.2 + + + + + Layer 26, z:10.6 + + + + + Layer 27, z:11.0 + + + + + Layer 28, z:11.4 + + + + + Layer 29, z:11.8 + + + + + Layer 30, z:12.2 + + + + + Layer 31, z:12.6 + + + + + Layer 32, z:13.0 + + + + + Layer 33, z:13.4 + + + + + Layer 34, z:13.8 + + + + + Layer 35, z:14.2 + + + + + Layer 36, z:14.6 + + + + + Layer 37, z:15.0 + + + + + + + + + + + + + Latitude + < + > + Longitude + < + > + Scale + 1 + < + > + + Min + X: -73.4936 mm + Y: -191.9792 mm + Z: -0.0 mm + + + Max + X: 73.4936 mm + Y: 21.8248 mm + Z: 15.2 mm + + + Dimension + X: 146.9872 mm + Y: 213.804 mm + Z: 15.2 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 38 + Volume: 230.2172 cm3 + + + + + + + + Y + X + 0 + + + 1 + Layer + < + > + Scale + 1 + < + > + + Min + X: -73.4936 mm + Y: -191.9792 mm + Z: -0.0 mm + + + Max + X: 73.4936 mm + Y: 21.8248 mm + Z: 15.2 mm + + + Dimension + X: 146.9872 mm + Y: 213.804 mm + Z: 15.2 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 38 + Volume: 230.2172 cm3 + + + + + + + Y + X + Scale + : 1 + < + > + + Min + X: -73.4936 mm + Y: -191.9792 mm + Z: -0.0 mm + + + Max + X: 73.4936 mm + Y: 21.8248 mm + Z: 15.2 mm + + + Dimension + X: 146.9872 mm + Y: 213.804 mm + Z: 15.2 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 38 + Volume: 230.2172 cm3 + + + [Iso View] + Iso View + [Layer View] + Layer View + [Scroll View] + Scroll View + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/ring.xml b/SkeinPyPy/models/xml_models/creation/gear/ring.xml new file mode 100644 index 0000000..1fb56af --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/ring.xml @@ -0,0 +1,4 @@ + + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/shaft.svg b/SkeinPyPy/models/xml_models/creation/gear/shaft.svg new file mode 100644 index 0000000..fe01684 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/shaft.svg @@ -0,0 +1,635 @@ + + + + + + + + shaft.xml - Slice Layers + + + + + + Layer 0, z:0.2 + + + + + Layer 1, z:0.6 + + + + + Layer 2, z:1.0 + + + + + Layer 3, z:1.4 + + + + + Layer 4, z:1.8 + + + + + Layer 5, z:2.2 + + + + + Layer 6, z:2.6 + + + + + Layer 7, z:3.0 + + + + + Layer 8, z:3.4 + + + + + Layer 9, z:3.8 + + + + + Layer 10, z:4.2 + + + + + Layer 11, z:4.6 + + + + + Layer 12, z:5.0 + + + + + Layer 13, z:5.4 + + + + + Layer 14, z:5.8 + + + + + Layer 15, z:6.2 + + + + + Layer 16, z:6.6 + + + + + Layer 17, z:7.0 + + + + + Layer 18, z:7.4 + + + + + Layer 19, z:7.8 + + + + + Layer 20, z:8.2 + + + + + Layer 21, z:8.6 + + + + + Layer 22, z:9.0 + + + + + Layer 23, z:9.4 + + + + + Layer 24, z:9.8 + + + + + + + + + + + + + Latitude + < + > + Longitude + < + > + Scale + 1 + < + > + + Min + X: -50.4319 mm + Y: -150.126 mm + Z: -0.0 mm + + + Max + X: 50.4319 mm + Y: 21.8248 mm + Z: 10.0 mm + + + Dimension + X: 100.8638 mm + Y: 171.9508 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 151.8979 cm3 + + + + + + + + Y + X + 0 + + + 1 + Layer + < + > + Scale + 1 + < + > + + Min + X: -50.4319 mm + Y: -150.126 mm + Z: -0.0 mm + + + Max + X: 50.4319 mm + Y: 21.8248 mm + Z: 10.0 mm + + + Dimension + X: 100.8638 mm + Y: 171.9508 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 151.8979 cm3 + + + + + + + Y + X + Scale + : 1 + < + > + + Min + X: -50.4319 mm + Y: -150.126 mm + Z: -0.0 mm + + + Max + X: 50.4319 mm + Y: 21.8248 mm + Z: 10.0 mm + + + Dimension + X: 100.8638 mm + Y: 171.9508 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 151.8979 cm3 + + + [Iso View] + Iso View + [Layer View] + Layer View + [Scroll View] + Scroll View + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/shaft.xml b/SkeinPyPy/models/xml_models/creation/gear/shaft.xml new file mode 100644 index 0000000..fbaa52e --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/shaft.xml @@ -0,0 +1,4 @@ + + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/shaft_top.svg b/SkeinPyPy/models/xml_models/creation/gear/shaft_top.svg new file mode 100644 index 0000000..203642f --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/shaft_top.svg @@ -0,0 +1,635 @@ + + + + + + + + shaft_top.xml - Slice Layers + + + + + + Layer 0, z:0.2 + + + + + Layer 1, z:0.6 + + + + + Layer 2, z:1.0 + + + + + Layer 3, z:1.4 + + + + + Layer 4, z:1.8 + + + + + Layer 5, z:2.2 + + + + + Layer 6, z:2.6 + + + + + Layer 7, z:3.0 + + + + + Layer 8, z:3.4 + + + + + Layer 9, z:3.8 + + + + + Layer 10, z:4.2 + + + + + Layer 11, z:4.6 + + + + + Layer 12, z:5.0 + + + + + Layer 13, z:5.4 + + + + + Layer 14, z:5.8 + + + + + Layer 15, z:6.2 + + + + + Layer 16, z:6.6 + + + + + Layer 17, z:7.0 + + + + + Layer 18, z:7.4 + + + + + Layer 19, z:7.8 + + + + + Layer 20, z:8.2 + + + + + Layer 21, z:8.6 + + + + + Layer 22, z:9.0 + + + + + Layer 23, z:9.4 + + + + + Layer 24, z:9.8 + + + + + + + + + + + + + Latitude + < + > + Longitude + < + > + Scale + 1 + < + > + + Min + X: -50.4319 mm + Y: -150.126 mm + Z: -0.0 mm + + + Max + X: 50.4319 mm + Y: 21.8248 mm + Z: 10.0 mm + + + Dimension + X: 100.8638 mm + Y: 171.9508 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 151.1382 cm3 + + + + + + + + Y + X + 0 + + + 1 + Layer + < + > + Scale + 1 + < + > + + Min + X: -50.4319 mm + Y: -150.126 mm + Z: -0.0 mm + + + Max + X: 50.4319 mm + Y: 21.8248 mm + Z: 10.0 mm + + + Dimension + X: 100.8638 mm + Y: 171.9508 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 151.1382 cm3 + + + + + + + Y + X + Scale + : 1 + < + > + + Min + X: -50.4319 mm + Y: -150.126 mm + Z: -0.0 mm + + + Max + X: 50.4319 mm + Y: 21.8248 mm + Z: 10.0 mm + + + Dimension + X: 100.8638 mm + Y: 171.9508 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 151.1382 cm3 + + + [Iso View] + Iso View + [Layer View] + Layer View + [Scroll View] + Scroll View + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/shaft_top.xml b/SkeinPyPy/models/xml_models/creation/gear/shaft_top.xml new file mode 100644 index 0000000..e5568e7 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/shaft_top.xml @@ -0,0 +1,4 @@ + + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/spur_helix.svg b/SkeinPyPy/models/xml_models/creation/gear/spur_helix.svg new file mode 100644 index 0000000..6cd0d8b --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/spur_helix.svg @@ -0,0 +1,635 @@ + + + + + + + + spur_helix.xml - Slice Layers + + + + + + Layer 0, z:0.2 + + + + + Layer 1, z:0.6 + + + + + Layer 2, z:1.0 + + + + + Layer 3, z:1.4 + + + + + Layer 4, z:1.8 + + + + + Layer 5, z:2.2 + + + + + Layer 6, z:2.6 + + + + + Layer 7, z:3.0 + + + + + Layer 8, z:3.4 + + + + + Layer 9, z:3.8 + + + + + Layer 10, z:4.2 + + + + + Layer 11, z:4.6 + + + + + Layer 12, z:5.0 + + + + + Layer 13, z:5.4 + + + + + Layer 14, z:5.8 + + + + + Layer 15, z:6.2 + + + + + Layer 16, z:6.6 + + + + + Layer 17, z:7.0 + + + + + Layer 18, z:7.4 + + + + + Layer 19, z:7.8 + + + + + Layer 20, z:8.2 + + + + + Layer 21, z:8.6 + + + + + Layer 22, z:9.0 + + + + + Layer 23, z:9.4 + + + + + Layer 24, z:9.8 + + + + + + + + + + + + + Latitude + < + > + Longitude + < + > + Scale + 1 + < + > + + Min + X: -50.4346 mm + Y: -150.4247 mm + Z: -0.0 mm + + + Max + X: 50.4288 mm + Y: 22.1224 mm + Z: 10.0 mm + + + Dimension + X: 100.8635 mm + Y: 172.5471 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 150.9144 cm3 + + + + + + + + Y + X + 0 + + + 1 + Layer + < + > + Scale + 1 + < + > + + Min + X: -50.4346 mm + Y: -150.4247 mm + Z: -0.0 mm + + + Max + X: 50.4288 mm + Y: 22.1224 mm + Z: 10.0 mm + + + Dimension + X: 100.8635 mm + Y: 172.5471 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 150.9144 cm3 + + + + + + + Y + X + Scale + : 1 + < + > + + Min + X: -50.4346 mm + Y: -150.4247 mm + Z: -0.0 mm + + + Max + X: 50.4288 mm + Y: 22.1224 mm + Z: 10.0 mm + + + Dimension + X: 100.8635 mm + Y: 172.5471 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 150.9144 cm3 + + + [Iso View] + Iso View + [Layer View] + Layer View + [Scroll View] + Scroll View + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/spur_helix.xml b/SkeinPyPy/models/xml_models/creation/gear/spur_helix.xml new file mode 100644 index 0000000..a0bc6d8 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/spur_helix.xml @@ -0,0 +1,4 @@ + + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/spur_herringbone.svg b/SkeinPyPy/models/xml_models/creation/gear/spur_herringbone.svg new file mode 100644 index 0000000..d4bd6df --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/spur_herringbone.svg @@ -0,0 +1,635 @@ + + + + + + + + spur_herringbone.xml - Slice Layers + + + + + + Layer 0, z:0.2 + + + + + Layer 1, z:0.6 + + + + + Layer 2, z:1.0 + + + + + Layer 3, z:1.4 + + + + + Layer 4, z:1.8 + + + + + Layer 5, z:2.2 + + + + + Layer 6, z:2.6 + + + + + Layer 7, z:3.0 + + + + + Layer 8, z:3.4 + + + + + Layer 9, z:3.8 + + + + + Layer 10, z:4.2 + + + + + Layer 11, z:4.6 + + + + + Layer 12, z:5.0 + + + + + Layer 13, z:5.4 + + + + + Layer 14, z:5.8 + + + + + Layer 15, z:6.2 + + + + + Layer 16, z:6.6 + + + + + Layer 17, z:7.0 + + + + + Layer 18, z:7.4 + + + + + Layer 19, z:7.8 + + + + + Layer 20, z:8.2 + + + + + Layer 21, z:8.6 + + + + + Layer 22, z:9.0 + + + + + Layer 23, z:9.4 + + + + + Layer 24, z:9.8 + + + + + + + + + + + + + Latitude + < + > + Longitude + < + > + Scale + 1 + < + > + + Min + X: -50.4225 mm + Y: -150.4395 mm + Z: -0.0 mm + + + Max + X: 50.4306 mm + Y: 22.1224 mm + Z: 10.0 mm + + + Dimension + X: 100.8531 mm + Y: 172.5619 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 150.9346 cm3 + + + + + + + + Y + X + 0 + + + 1 + Layer + < + > + Scale + 1 + < + > + + Min + X: -50.4225 mm + Y: -150.4395 mm + Z: -0.0 mm + + + Max + X: 50.4306 mm + Y: 22.1224 mm + Z: 10.0 mm + + + Dimension + X: 100.8531 mm + Y: 172.5619 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 150.9346 cm3 + + + + + + + Y + X + Scale + : 1 + < + > + + Min + X: -50.4225 mm + Y: -150.4395 mm + Z: -0.0 mm + + + Max + X: 50.4306 mm + Y: 22.1224 mm + Z: 10.0 mm + + + Dimension + X: 100.8531 mm + Y: 172.5619 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 150.9346 cm3 + + + [Iso View] + Iso View + [Layer View] + Layer View + [Scroll View] + Scroll View + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/spur_herringbone.xml b/SkeinPyPy/models/xml_models/creation/gear/spur_herringbone.xml new file mode 100644 index 0000000..ef8b8de --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/spur_herringbone.xml @@ -0,0 +1,4 @@ + + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/spur_parabolic.svg b/SkeinPyPy/models/xml_models/creation/gear/spur_parabolic.svg new file mode 100644 index 0000000..de90249 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/spur_parabolic.svg @@ -0,0 +1,635 @@ + + + + + + + + spur_parabolic.xml - Slice Layers + + + + + + Layer 0, z:0.2 + + + + + Layer 1, z:0.6 + + + + + Layer 2, z:1.0 + + + + + Layer 3, z:1.4 + + + + + Layer 4, z:1.8 + + + + + Layer 5, z:2.2 + + + + + Layer 6, z:2.6 + + + + + Layer 7, z:3.0 + + + + + Layer 8, z:3.4 + + + + + Layer 9, z:3.8 + + + + + Layer 10, z:4.2 + + + + + Layer 11, z:4.6 + + + + + Layer 12, z:5.0 + + + + + Layer 13, z:5.4 + + + + + Layer 14, z:5.8 + + + + + Layer 15, z:6.2 + + + + + Layer 16, z:6.6 + + + + + Layer 17, z:7.0 + + + + + Layer 18, z:7.4 + + + + + Layer 19, z:7.8 + + + + + Layer 20, z:8.2 + + + + + Layer 21, z:8.6 + + + + + Layer 22, z:9.0 + + + + + Layer 23, z:9.4 + + + + + Layer 24, z:9.8 + + + + + + + + + + + + + Latitude + < + > + Longitude + < + > + Scale + 1 + < + > + + Min + X: -50.4272 mm + Y: -150.3492 mm + Z: -0.0 mm + + + Max + X: 50.4412 mm + Y: 22.1077 mm + Z: 10.0 mm + + + Dimension + X: 100.8684 mm + Y: 172.4569 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 151.0364 cm3 + + + + + + + + Y + X + 0 + + + 1 + Layer + < + > + Scale + 1 + < + > + + Min + X: -50.4272 mm + Y: -150.3492 mm + Z: -0.0 mm + + + Max + X: 50.4412 mm + Y: 22.1077 mm + Z: 10.0 mm + + + Dimension + X: 100.8684 mm + Y: 172.4569 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 151.0364 cm3 + + + + + + + Y + X + Scale + : 1 + < + > + + Min + X: -50.4272 mm + Y: -150.3492 mm + Z: -0.0 mm + + + Max + X: 50.4412 mm + Y: 22.1077 mm + Z: 10.0 mm + + + Dimension + X: 100.8684 mm + Y: 172.4569 mm + Z: 10.0 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 25 + Volume: 151.0364 cm3 + + + [Iso View] + Iso View + [Layer View] + Layer View + [Scroll View] + Scroll View + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/spur_parabolic.xml b/SkeinPyPy/models/xml_models/creation/gear/spur_parabolic.xml new file mode 100644 index 0000000..a0e8a4f --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/spur_parabolic.xml @@ -0,0 +1,4 @@ + + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/spur_profile.svg b/SkeinPyPy/models/xml_models/creation/gear/spur_profile.svg new file mode 100644 index 0000000..45ad13b --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/spur_profile.svg @@ -0,0 +1,521 @@ + + + + + + + + spur_profile.xml - Slice Layers + + + + + + Layer 0, z:0.2 + + + + + Layer 1, z:0.6 + + + + + + + + + + + + + Latitude + < + > + Longitude + < + > + Scale + 1 + < + > + + Min + X: -50.4319 mm + Y: -150.126 mm + Z: 0.0 mm + + + Max + X: 50.4319 mm + Y: 21.8248 mm + Z: 0.8 mm + + + Dimension + X: 100.8638 mm + Y: 171.9508 mm + Z: 0.8 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 2 + Volume: 16.6701 cm3 + + + + + + + + Y + X + 0 + + + 1 + Layer + < + > + Scale + 1 + < + > + + Min + X: -50.4319 mm + Y: -150.126 mm + Z: 0.0 mm + + + Max + X: 50.4319 mm + Y: 21.8248 mm + Z: 0.8 mm + + + Dimension + X: 100.8638 mm + Y: 171.9508 mm + Z: 0.8 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 2 + Volume: 16.6701 cm3 + + + + + + + Y + X + Scale + : 1 + < + > + + Min + X: -50.4319 mm + Y: -150.126 mm + Z: 0.0 mm + + + Max + X: 50.4319 mm + Y: 21.8248 mm + Z: 0.8 mm + + + Dimension + X: 100.8638 mm + Y: 171.9508 mm + Z: 0.8 mm + + + Statistics + Layer Thickness: 0.4 mm + Number of Layers: 2 + Volume: 16.6701 cm3 + + + [Iso View] + Iso View + [Layer View] + Layer View + [Scroll View] + Scroll View + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/spur_profile.xml b/SkeinPyPy/models/xml_models/creation/gear/spur_profile.xml new file mode 100644 index 0000000..f616bf5 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/spur_profile.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/creation/gear/test_gear.xml b/SkeinPyPy/models/xml_models/creation/gear/test_gear.xml new file mode 100644 index 0000000..579bbd7 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/gear/test_gear.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/creation/grid.xml b/SkeinPyPy/models/xml_models/creation/grid.xml new file mode 100644 index 0000000..c9333bd --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/grid.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/SkeinPyPy/models/xml_models/creation/heightmap.xml b/SkeinPyPy/models/xml_models/creation/heightmap.xml new file mode 100644 index 0000000..bc982d2 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/heightmap.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/creation/heightmap_1.pgm b/SkeinPyPy/models/xml_models/creation/heightmap_1.pgm new file mode 100644 index 0000000..c8e23b4 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/heightmap_1.pgm @@ -0,0 +1,9 @@ +P1 +# CREATOR: GIMP PNM Filter Version 1.1 +3 2 +1 +0 +1 +1 +0 +1 \ No newline at end of file diff --git a/SkeinPyPy/models/xml_models/creation/heightmap_255.pgm b/SkeinPyPy/models/xml_models/creation/heightmap_255.pgm new file mode 100644 index 0000000..2425437 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/heightmap_255.pgm @@ -0,0 +1,201 @@ +P2 +# CREATOR: GIMP PNM Filter Version 1.1 +20 10 +255 +255 +255 +255 +255 +229 120 19 0 +0 +0 +0 +0 +0 +19 +120 +229 +255 +255 +255 +255 +255 +255 +255 +255 +197 +70 +0 +0 +0 +0 +0 +0 +0 +0 +70 +197 +255 +255 +255 +255 +255 +255 +255 +255 +140 +12 +0 +0 +0 +0 +0 +0 +0 +0 +12 +140 +255 +255 +255 +255 +255 +255 +255 +255 +128 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +128 +255 +255 +255 +255 +255 +255 +255 +255 +128 +0 +0 +0 +0 +0 +0 +0 +0 +0 +0 +128 +255 +255 +255 +255 +255 +255 +255 +255 +179 +51 +0 +0 +0 +0 +0 +0 +0 +0 +51 +179 +255 +255 +255 +255 +255 +255 +255 +255 +217 +89 +0 +0 +0 +0 +0 +0 +0 +0 +89 +217 +255 +255 +255 +255 +255 +255 +255 +255 +249 +198 +77 +0 +0 +0 +0 +0 +0 +77 +198 +249 +255 +255 +255 +255 +255 +255 +255 +255 +255 +249 +198 +128 +51 +0 +0 +51 +128 +198 +249 +255 +255 +255 +255 +255 +255 +255 +255 +255 +255 +255 +249 +236 +218 +205 +205 +218 +236 +249 +255 +255 +255 +255 +255 +255 diff --git a/SkeinPyPy/models/xml_models/creation/lathe.xml b/SkeinPyPy/models/xml_models/creation/lathe.xml new file mode 100644 index 0000000..8f1e5a2 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/lathe.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/creation/line.xml b/SkeinPyPy/models/xml_models/creation/line.xml new file mode 100644 index 0000000..3c94c1e --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/line.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/creation/linear_bearing_cage.xml b/SkeinPyPy/models/xml_models/creation/linear_bearing_cage.xml new file mode 100644 index 0000000..14b9ed9 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/linear_bearing_cage.xml @@ -0,0 +1,4 @@ + + + + diff --git a/SkeinPyPy/models/xml_models/creation/lineation.xml b/SkeinPyPy/models/xml_models/creation/lineation.xml new file mode 100644 index 0000000..9028433 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/lineation.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/creation/mechaslab.xml b/SkeinPyPy/models/xml_models/creation/mechaslab.xml new file mode 100644 index 0000000..e98cc66 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/mechaslab.xml @@ -0,0 +1,4 @@ + + + + diff --git a/SkeinPyPy/models/xml_models/creation/peg.xml b/SkeinPyPy/models/xml_models/creation/peg.xml new file mode 100644 index 0000000..8a6d296 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/peg.xml @@ -0,0 +1,4 @@ + + + + diff --git a/SkeinPyPy/models/xml_models/creation/polygon.xml b/SkeinPyPy/models/xml_models/creation/polygon.xml new file mode 100644 index 0000000..ac8bc0f --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/polygon.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/creation/shaft.xml b/SkeinPyPy/models/xml_models/creation/shaft.xml new file mode 100644 index 0000000..443a122 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/shaft.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/creation/solid.xml b/SkeinPyPy/models/xml_models/creation/solid.xml new file mode 100644 index 0000000..2613f3b --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/solid.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/creation/sponge_slice.xml b/SkeinPyPy/models/xml_models/creation/sponge_slice.xml new file mode 100644 index 0000000..bf5b607 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/sponge_slice.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/creation/square.xml b/SkeinPyPy/models/xml_models/creation/square.xml new file mode 100644 index 0000000..a322f41 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/square.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/creation/svg.xml b/SkeinPyPy/models/xml_models/creation/svg.xml new file mode 100644 index 0000000..45f9961 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/svg.xml @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/creation/teardrop.xml b/SkeinPyPy/models/xml_models/creation/teardrop.xml new file mode 100644 index 0000000..a3a2636 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/teardrop.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/creation/text.xml b/SkeinPyPy/models/xml_models/creation/text.xml new file mode 100644 index 0000000..642bf89 --- /dev/null +++ b/SkeinPyPy/models/xml_models/creation/text.xml @@ -0,0 +1,5 @@ + + + hi + + diff --git a/SkeinPyPy/models/xml_models/geometry.csv b/SkeinPyPy/models/xml_models/geometry.csv new file mode 100644 index 0000000..5d6b1a0 --- /dev/null +++ b/SkeinPyPy/models/xml_models/geometry.csv @@ -0,0 +1,25 @@ +Format is tab separated boolean geometry. +Name Value +_object booleangeometry +version 2010-03-29 +_object trianglemesh +id tetrahedron + _object matrix4x4 + m11 0.0 + m13 1.0 + m21 -1.0 + m22 0.0 + m32 -1.0 + m33 0.0 + _table vertex + x y z + -5.0 2.89 + -5.77 + 5.0 2.89 + 8.66 + _table face + vertex0 vertex1 vertex2 + 3 0 1 + 3 1 2 + 3 2 0 + 0 2 1 diff --git a/SkeinPyPy/models/xml_models/geometry_tools/path_elements/arc.xml b/SkeinPyPy/models/xml_models/geometry_tools/path_elements/arc.xml new file mode 100644 index 0000000..2315b3d --- /dev/null +++ b/SkeinPyPy/models/xml_models/geometry_tools/path_elements/arc.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/geometry_tools/path_elements/cubic.xml b/SkeinPyPy/models/xml_models/geometry_tools/path_elements/cubic.xml new file mode 100644 index 0000000..bb38791 --- /dev/null +++ b/SkeinPyPy/models/xml_models/geometry_tools/path_elements/cubic.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/geometry_tools/path_elements/quadratic.xml b/SkeinPyPy/models/xml_models/geometry_tools/path_elements/quadratic.xml new file mode 100644 index 0000000..7364d9f --- /dev/null +++ b/SkeinPyPy/models/xml_models/geometry_tools/path_elements/quadratic.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/geometry_utilities/evaluate.xml b/SkeinPyPy/models/xml_models/geometry_utilities/evaluate.xml new file mode 100644 index 0000000..cc0c948 --- /dev/null +++ b/SkeinPyPy/models/xml_models/geometry_utilities/evaluate.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_elements/creation.xml b/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_elements/creation.xml new file mode 100644 index 0000000..3f8806c --- /dev/null +++ b/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_elements/creation.xml @@ -0,0 +1,4 @@ + + + + diff --git a/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_elements/document.xml b/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_elements/document.xml new file mode 100644 index 0000000..2f2f299 --- /dev/null +++ b/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_elements/document.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_elements/setting.xml b/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_elements/setting.xml new file mode 100644 index 0000000..180dcbb --- /dev/null +++ b/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_elements/setting.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_enumerables/dictionary.xml b/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_enumerables/dictionary.xml new file mode 100644 index 0000000..8b072b0 --- /dev/null +++ b/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_enumerables/dictionary.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_enumerables/list.xml b/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_enumerables/list.xml new file mode 100644 index 0000000..7efb1d3 --- /dev/null +++ b/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_enumerables/list.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_enumerables/string.xml b/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_enumerables/string.xml new file mode 100644 index 0000000..18da3be --- /dev/null +++ b/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_enumerables/string.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_fundamentals/euclid.xml b/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_fundamentals/euclid.xml new file mode 100644 index 0000000..a1ba2b0 --- /dev/null +++ b/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_fundamentals/euclid.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_fundamentals/math.xml b/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_fundamentals/math.xml new file mode 100644 index 0000000..1540842 --- /dev/null +++ b/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_fundamentals/math.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_fundamentals/measure.xml b/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_fundamentals/measure.xml new file mode 100644 index 0000000..bc35c0d --- /dev/null +++ b/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_fundamentals/measure.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_fundamentals/print.xml b/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_fundamentals/print.xml new file mode 100644 index 0000000..be2727c --- /dev/null +++ b/SkeinPyPy/models/xml_models/geometry_utilities/evaluate_fundamentals/print.xml @@ -0,0 +1,10 @@ + + + + + hello + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_matrix/rotate.xml b/SkeinPyPy/models/xml_models/manipulation_matrix/rotate.xml new file mode 100644 index 0000000..4a5fe62 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_matrix/rotate.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_matrix/scale.xml b/SkeinPyPy/models/xml_models/manipulation_matrix/scale.xml new file mode 100644 index 0000000..6ac9867 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_matrix/scale.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_matrix/transform.xml b/SkeinPyPy/models/xml_models/manipulation_matrix/transform.xml new file mode 100644 index 0000000..23f5011 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_matrix/transform.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_matrix/translate.xml b/SkeinPyPy/models/xml_models/manipulation_matrix/translate.xml new file mode 100644 index 0000000..f45587a --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_matrix/translate.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_meta/array.xml b/SkeinPyPy/models/xml_models/manipulation_meta/array.xml new file mode 100644 index 0000000..2a08f45 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_meta/array.xml @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_meta/carve.xml b/SkeinPyPy/models/xml_models/manipulation_meta/carve.xml new file mode 100644 index 0000000..a8fc654 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_meta/carve.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_meta/copy.xml b/SkeinPyPy/models/xml_models/manipulation_meta/copy.xml new file mode 100644 index 0000000..b78dfab --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_meta/copy.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_meta/disjoin.xml b/SkeinPyPy/models/xml_models/manipulation_meta/disjoin.xml new file mode 100644 index 0000000..550a7fc --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_meta/disjoin.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_meta/import.xml b/SkeinPyPy/models/xml_models/manipulation_meta/import.xml new file mode 100644 index 0000000..d197421 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_meta/import.xml @@ -0,0 +1,17 @@ + + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_meta/write.xml b/SkeinPyPy/models/xml_models/manipulation_meta/write.xml new file mode 100644 index 0000000..42bc509 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_meta/write.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_meta/writeTest.xml b/SkeinPyPy/models/xml_models/manipulation_meta/writeTest.xml new file mode 100644 index 0000000..db708bf --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_meta/writeTest.xml @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_meta/writeTest_2.xml b/SkeinPyPy/models/xml_models/manipulation_meta/writeTest_2.xml new file mode 100644 index 0000000..db708bf --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_meta/writeTest_2.xml @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_paths/bevel/bevel.xml b/SkeinPyPy/models/xml_models/manipulation_paths/bevel/bevel.xml new file mode 100644 index 0000000..68ce115 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_paths/bevel/bevel.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_paths/bevel/bevel_element.xml b/SkeinPyPy/models/xml_models/manipulation_paths/bevel/bevel_element.xml new file mode 100644 index 0000000..baa6e2c --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_paths/bevel/bevel_element.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_paths/convex.xml b/SkeinPyPy/models/xml_models/manipulation_paths/convex.xml new file mode 100644 index 0000000..185885c --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_paths/convex.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_paths/outline.xml b/SkeinPyPy/models/xml_models/manipulation_paths/outline.xml new file mode 100644 index 0000000..0c73ec1 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_paths/outline.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_paths/overhang.xml b/SkeinPyPy/models/xml_models/manipulation_paths/overhang.xml new file mode 100644 index 0000000..17d9782 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_paths/overhang.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_paths/round.xml b/SkeinPyPy/models/xml_models/manipulation_paths/round.xml new file mode 100644 index 0000000..b2dffd3 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_paths/round.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_paths/segment.xml b/SkeinPyPy/models/xml_models/manipulation_paths/segment.xml new file mode 100644 index 0000000..8c431f9 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_paths/segment.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_paths/wedge.xml b/SkeinPyPy/models/xml_models/manipulation_paths/wedge.xml new file mode 100644 index 0000000..577082a --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_paths/wedge.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_shapes/bottom.xml b/SkeinPyPy/models/xml_models/manipulation_shapes/bottom.xml new file mode 100644 index 0000000..85936f2 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_shapes/bottom.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_shapes/equation.xml b/SkeinPyPy/models/xml_models/manipulation_shapes/equation.xml new file mode 100644 index 0000000..7c5e763 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_shapes/equation.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_shapes/flip/flip_element.xml b/SkeinPyPy/models/xml_models/manipulation_shapes/flip/flip_element.xml new file mode 100644 index 0000000..92c3d29 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_shapes/flip/flip_element.xml @@ -0,0 +1,6 @@ + + + hi + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_shapes/flip/flip_path.xml b/SkeinPyPy/models/xml_models/manipulation_shapes/flip/flip_path.xml new file mode 100644 index 0000000..f733855 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_shapes/flip/flip_path.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_shapes/flip/flip_solid.xml b/SkeinPyPy/models/xml_models/manipulation_shapes/flip/flip_solid.xml new file mode 100644 index 0000000..e3b9a25 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_shapes/flip/flip_solid.xml @@ -0,0 +1,4 @@ + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_shapes/inset/inset_element.xml b/SkeinPyPy/models/xml_models/manipulation_shapes/inset/inset_element.xml new file mode 100644 index 0000000..3ea7b38 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_shapes/inset/inset_element.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_shapes/inset/inset_path.xml b/SkeinPyPy/models/xml_models/manipulation_shapes/inset/inset_path.xml new file mode 100644 index 0000000..e75b16d --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_shapes/inset/inset_path.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_shapes/inset/inset_solid.xml b/SkeinPyPy/models/xml_models/manipulation_shapes/inset/inset_solid.xml new file mode 100644 index 0000000..48002a9 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_shapes/inset/inset_solid.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_shapes/mirror/mirror_element.xml b/SkeinPyPy/models/xml_models/manipulation_shapes/mirror/mirror_element.xml new file mode 100644 index 0000000..73373e2 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_shapes/mirror/mirror_element.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_shapes/mirror/mirror_path.xml b/SkeinPyPy/models/xml_models/manipulation_shapes/mirror/mirror_path.xml new file mode 100644 index 0000000..f41bf70 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_shapes/mirror/mirror_path.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_shapes/mirror/mirror_solid.xml b/SkeinPyPy/models/xml_models/manipulation_shapes/mirror/mirror_solid.xml new file mode 100644 index 0000000..debaef4 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_shapes/mirror/mirror_solid.xml @@ -0,0 +1,4 @@ + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_shapes/outset/outset_element.xml b/SkeinPyPy/models/xml_models/manipulation_shapes/outset/outset_element.xml new file mode 100644 index 0000000..d3eb289 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_shapes/outset/outset_element.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_shapes/outset/outset_path.xml b/SkeinPyPy/models/xml_models/manipulation_shapes/outset/outset_path.xml new file mode 100644 index 0000000..c074079 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_shapes/outset/outset_path.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/manipulation_shapes/outset/outset_solid.xml b/SkeinPyPy/models/xml_models/manipulation_shapes/outset/outset_solid.xml new file mode 100644 index 0000000..b17c966 --- /dev/null +++ b/SkeinPyPy/models/xml_models/manipulation_shapes/outset/outset_solid.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/SkeinPyPy/models/xml_models/slab.xml b/SkeinPyPy/models/xml_models/slab.xml new file mode 100644 index 0000000..e30dec7 --- /dev/null +++ b/SkeinPyPy/models/xml_models/slab.xml @@ -0,0 +1,4 @@ + + + + diff --git a/SkeinPyPy/models/xml_models/solids/cube.xml b/SkeinPyPy/models/xml_models/solids/cube.xml new file mode 100644 index 0000000..2f0e283 --- /dev/null +++ b/SkeinPyPy/models/xml_models/solids/cube.xml @@ -0,0 +1,4 @@ + + + + diff --git a/SkeinPyPy/models/xml_models/solids/cylinder.xml b/SkeinPyPy/models/xml_models/solids/cylinder.xml new file mode 100644 index 0000000..60bc34c --- /dev/null +++ b/SkeinPyPy/models/xml_models/solids/cylinder.xml @@ -0,0 +1,4 @@ + + + + diff --git a/SkeinPyPy/models/xml_models/solids/difference.xml b/SkeinPyPy/models/xml_models/solids/difference.xml new file mode 100644 index 0000000..0731d67 --- /dev/null +++ b/SkeinPyPy/models/xml_models/solids/difference.xml @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/solids/group.xml b/SkeinPyPy/models/xml_models/solids/group.xml new file mode 100644 index 0000000..17a1298 --- /dev/null +++ b/SkeinPyPy/models/xml_models/solids/group.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/solids/intersection.xml b/SkeinPyPy/models/xml_models/solids/intersection.xml new file mode 100644 index 0000000..f723a8e --- /dev/null +++ b/SkeinPyPy/models/xml_models/solids/intersection.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/SkeinPyPy/models/xml_models/solids/sphere.xml b/SkeinPyPy/models/xml_models/solids/sphere.xml new file mode 100644 index 0000000..51d6911 --- /dev/null +++ b/SkeinPyPy/models/xml_models/solids/sphere.xml @@ -0,0 +1,4 @@ + + + + diff --git a/SkeinPyPy/models/xml_models/solids/triangle_mesh.xml b/SkeinPyPy/models/xml_models/solids/triangle_mesh.xml new file mode 100644 index 0000000..74a9927 --- /dev/null +++ b/SkeinPyPy/models/xml_models/solids/triangle_mesh.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/solids/union.xml b/SkeinPyPy/models/xml_models/solids/union.xml new file mode 100644 index 0000000..7ca2f79 --- /dev/null +++ b/SkeinPyPy/models/xml_models/solids/union.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/statements/class.xml b/SkeinPyPy/models/xml_models/statements/class.xml new file mode 100644 index 0000000..857720f --- /dev/null +++ b/SkeinPyPy/models/xml_models/statements/class.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/statements/elif.xml b/SkeinPyPy/models/xml_models/statements/elif.xml new file mode 100644 index 0000000..2d20e7a --- /dev/null +++ b/SkeinPyPy/models/xml_models/statements/elif.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/statements/else.xml b/SkeinPyPy/models/xml_models/statements/else.xml new file mode 100644 index 0000000..4075942 --- /dev/null +++ b/SkeinPyPy/models/xml_models/statements/else.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/statements/for.xml b/SkeinPyPy/models/xml_models/statements/for.xml new file mode 100644 index 0000000..b379827 --- /dev/null +++ b/SkeinPyPy/models/xml_models/statements/for.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/statements/function.xml b/SkeinPyPy/models/xml_models/statements/function.xml new file mode 100644 index 0000000..2c71034 --- /dev/null +++ b/SkeinPyPy/models/xml_models/statements/function.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/SkeinPyPy/models/xml_models/statements/if.xml b/SkeinPyPy/models/xml_models/statements/if.xml new file mode 100644 index 0000000..f338a6f --- /dev/null +++ b/SkeinPyPy/models/xml_models/statements/if.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/statements/print.xml b/SkeinPyPy/models/xml_models/statements/print.xml new file mode 100644 index 0000000..6ce4bff --- /dev/null +++ b/SkeinPyPy/models/xml_models/statements/print.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/statements/return.xml b/SkeinPyPy/models/xml_models/statements/return.xml new file mode 100644 index 0000000..243a0d2 --- /dev/null +++ b/SkeinPyPy/models/xml_models/statements/return.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/SkeinPyPy/models/xml_models/statements/statement.xml b/SkeinPyPy/models/xml_models/statements/statement.xml new file mode 100644 index 0000000..413ccf3 --- /dev/null +++ b/SkeinPyPy/models/xml_models/statements/statement.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/statements/while.xml b/SkeinPyPy/models/xml_models/statements/while.xml new file mode 100644 index 0000000..810edad --- /dev/null +++ b/SkeinPyPy/models/xml_models/statements/while.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/support_test_box.xml b/SkeinPyPy/models/xml_models/support_test_box.xml new file mode 100644 index 0000000..600c7d1 --- /dev/null +++ b/SkeinPyPy/models/xml_models/support_test_box.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/SkeinPyPy/models/xml_models/tetra.xml b/SkeinPyPy/models/xml_models/tetra.xml new file mode 100644 index 0000000..1c07c14 --- /dev/null +++ b/SkeinPyPy/models/xml_models/tetra.xml @@ -0,0 +1,1330 @@ + + + + + + + + + + + + + + + + Default Texture + + + + + + + + + + + + + + + + + + + + + + + + + + Camera 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + Light 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + tetrax20 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + tetray40 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + tetraz30 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + tetrax20y40 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + tetrax20z30 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + tetray40z30 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + tetrax20y40z30 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/models/xml_models/tetrawedge.xml b/SkeinPyPy/models/xml_models/tetrawedge.xml new file mode 100644 index 0000000..db79680 --- /dev/null +++ b/SkeinPyPy/models/xml_models/tetrawedge.xml @@ -0,0 +1,388 @@ + + + + + + + + + + + + + + + + Default Texture + + + + + + + + + + + + + + + + + + + + + + + + + + Camera 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + Light 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + tetrawedge + + + + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Position + + + + + + + + + + + + + + + + + + + + + + + + Weight + + Rotation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SkeinPyPy/skeinforge_application/__init__.py b/SkeinPyPy/skeinforge_application/__init__.py new file mode 100644 index 0000000..bdac25d --- /dev/null +++ b/SkeinPyPy/skeinforge_application/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 1 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/skeinforge_application/alterations/end.gcode b/SkeinPyPy/skeinforge_application/alterations/end.gcode new file mode 100644 index 0000000..a981292 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/alterations/end.gcode @@ -0,0 +1,10 @@ +(start of end.gcode) +M104 S0 (extruder heat off) +M106 (fan on) +G91 (relative positioning) +G1 Z+10 E-5 F400 (move Z up a bit and retract filament by 5mm) +G1 X-20 Y-20 F1500 (move X and Y over a bit) +M84 (steppers off) +G90 (absolute positioning) +(end of end.gcode) + diff --git a/SkeinPyPy/skeinforge_application/alterations/example_cool_end.gcode b/SkeinPyPy/skeinforge_application/alterations/example_cool_end.gcode new file mode 100644 index 0000000..730c606 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/alterations/example_cool_end.gcode @@ -0,0 +1,2 @@ +(this is a sample cool end file, it must be renamed cool_end.gcode for skeinforge to recognize it) +M107 diff --git a/SkeinPyPy/skeinforge_application/alterations/example_cool_start.gcode b/SkeinPyPy/skeinforge_application/alterations/example_cool_start.gcode new file mode 100644 index 0000000..90285c7 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/alterations/example_cool_start.gcode @@ -0,0 +1,2 @@ +(this is a sample cool start file, it must be renamed cool_start.gcode for skeinforge to recognize it) +M106 diff --git a/SkeinPyPy/skeinforge_application/alterations/example_end.gcode b/SkeinPyPy/skeinforge_application/alterations/example_end.gcode new file mode 100644 index 0000000..86dc43b --- /dev/null +++ b/SkeinPyPy/skeinforge_application/alterations/example_end.gcode @@ -0,0 +1,2 @@ +(this is a sample gcode end file, it must be renamed end.gcode for skeinforge to recognize it) +M2 diff --git a/SkeinPyPy/skeinforge_application/alterations/example_home.gcode b/SkeinPyPy/skeinforge_application/alterations/example_home.gcode new file mode 100644 index 0000000..a089a31 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/alterations/example_home.gcode @@ -0,0 +1,5 @@ +(this is a sample gcode homing file, it must be renamed homing.gcode for skeinforge to recognize it) +G1 X-250.0 +G92 X0 ;set x 0 +G1 Y-250.0 +G92 Y0 ;set y 0 diff --git a/SkeinPyPy/skeinforge_application/alterations/example_replace.csv b/SkeinPyPy/skeinforge_application/alterations/example_replace.csv new file mode 100644 index 0000000..dbcbd1b --- /dev/null +++ b/SkeinPyPy/skeinforge_application/alterations/example_replace.csv @@ -0,0 +1,3 @@ +M101 +M103 + diff --git a/SkeinPyPy/skeinforge_application/alterations/example_replace_M108.csv b/SkeinPyPy/skeinforge_application/alterations/example_replace_M108.csv new file mode 100644 index 0000000..4cac815 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/alterations/example_replace_M108.csv @@ -0,0 +1,2 @@ +M108 S M108 P +M113 S M108 S diff --git a/SkeinPyPy/skeinforge_application/alterations/example_start.gcode b/SkeinPyPy/skeinforge_application/alterations/example_start.gcode new file mode 100644 index 0000000..eeafa85 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/alterations/example_start.gcode @@ -0,0 +1,3 @@ +(This is a sample gcode start file, it must be renamed start.gcode for skeinforge to recognize it. Also, to remove confusion this comment line should be deleted.) +G28 +M140 S diff --git a/SkeinPyPy/skeinforge_application/alterations/example_support_end.gcode b/SkeinPyPy/skeinforge_application/alterations/example_support_end.gcode new file mode 100644 index 0000000..63315e9 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/alterations/example_support_end.gcode @@ -0,0 +1,2 @@ +(this is a sample support end file, it must be renamed support_end.gcode for skeinforge to recognize it) + diff --git a/SkeinPyPy/skeinforge_application/alterations/example_support_start.gcode b/SkeinPyPy/skeinforge_application/alterations/example_support_start.gcode new file mode 100644 index 0000000..04392f0 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/alterations/example_support_start.gcode @@ -0,0 +1,2 @@ +(this is a sample support start file, it must be renamed support_start.gcode for skeinforge to recognize it) + diff --git a/SkeinPyPy/skeinforge_application/alterations/start.gcode b/SkeinPyPy/skeinforge_application/alterations/start.gcode new file mode 100644 index 0000000..0dca1c4 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/alterations/start.gcode @@ -0,0 +1,31 @@ +(start of start.txt) +M92 E926.5 (the number of extruder steps to take in 1mm of filament) +G21 (metric values) +G21 +G21 (all the extra G21 commands are comments - skeinforge eats lines without a gcode) +G21 +G90 (absolute positioning) +G21 +G28 X0 Y0 (move X/Y to min endstops) +G28 Z0 (move Z to min endstops) +G21 +G21 ( if your prints start too high, try changing the Z0.0 below ) +G21 ( to Z1.0 - the number after the Z is the actual, physical ) +G21 ( height of the nozzle in mm. This can take some messing around ) +G21 ( with to get just right... ) +G21 +G92 X0 Y0 Z0 E0 (reset software position to front/left/z=0.0) +G21 +G1 Z15.0 F400 (move the platform down 15mm) +G92 E0 (zero the extruded length) +G21 +G1 F75 E5 (extrude 5mm of feed stock) +G1 F75 E3.5 (reverse feed stock by 1.5mm) +G92 E0 (zero the extruded length again) +G21 +M1 (Clean the nozzle then press YES to continue...) +G21 +G1 X100 Y100 F3500 (go to the middle of the platform) +G1 Z0.0 F400 (back to Z=0 and start the print!) +(end of start.txt) + diff --git a/SkeinPyPy/skeinforge_application/profiles/cutting/End_Mill/chop.csv b/SkeinPyPy/skeinforge_application/profiles/cutting/End_Mill/chop.csv new file mode 100644 index 0000000..ace928d --- /dev/null +++ b/SkeinPyPy/skeinforge_application/profiles/cutting/End_Mill/chop.csv @@ -0,0 +1,13 @@ +Format is tab separated chop preferences. +Name Value +Add Extra Top Layer if Necessary True +Open File to be Chopped +Import Coarseness (ratio): 1.0 +Correct Mesh True +Unproven Mesh False +Layer Thickness (mm): 0.4 +Layer Thickness over Precision (ratio): 10.0 +Layers From (index): 0 +Layers To (index): 999999999 +Perimeter Width (mm): 2.0 +windowPositionChop Preferences 600+0 diff --git a/SkeinPyPy/skeinforge_application/profiles/cutting/End_Mill/lift.csv b/SkeinPyPy/skeinforge_application/profiles/cutting/End_Mill/lift.csv new file mode 100644 index 0000000..a2cf543 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/profiles/cutting/End_Mill/lift.csv @@ -0,0 +1,7 @@ +Format is tab separated lift preferences. +Name Value +Activate Lift: True +Cutting Lift over Layer Step (ratio): -0.5 +Open File to be Lifted /home/enrique/Desktop/backup/babbleold/script/reprap/pyRepRap/skeinforge_tools/craft_plugins/Screw Holder Bottom.stl +Clearance above Top (mm): 5.0 +windowPositionLift Preferences 440+53 diff --git a/SkeinPyPy/skeinforge_application/profiles/cutting/Laser/chop.csv b/SkeinPyPy/skeinforge_application/profiles/cutting/Laser/chop.csv new file mode 100644 index 0000000..b54d561 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/profiles/cutting/Laser/chop.csv @@ -0,0 +1,13 @@ +Format is tab separated chop preferences. +Name Value +Add Extra Top Layer if Necessary True +Open File to be Chopped +Import Coarseness (ratio): 1.0 +Correct Mesh True +Unproven Mesh False +Layer Thickness (mm): 0.4 +Layer Thickness over Precision (ratio): 10.0 +Layers From (index): 0 +Layers To (index): 999999999 +Perimeter Width (mm): 0.2 +windowPositionChop Preferences 600+0 diff --git a/SkeinPyPy/skeinforge_application/profiles/cutting/Laser/lift.csv b/SkeinPyPy/skeinforge_application/profiles/cutting/Laser/lift.csv new file mode 100644 index 0000000..9818ca2 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/profiles/cutting/Laser/lift.csv @@ -0,0 +1,7 @@ +Format is tab separated lift preferences. +Name Value +Activate Lift: True +Cutting Lift over Layer Step (ratio): 0.0 +Open File to be Lifted /home/enrique/Desktop/backup/babbleold/script/reprap/pyRepRap/skeinforge_tools/craft_plugins/Screw Holder Bottom.stl +Clearance above Top (mm): 5.0 +windowPositionLift Preferences 440+53 diff --git a/SkeinPyPy/skeinforge_application/profiles/extrusion/PLA/temperature.csv b/SkeinPyPy/skeinforge_application/profiles/extrusion/PLA/temperature.csv new file mode 100644 index 0000000..e9373f2 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/profiles/extrusion/PLA/temperature.csv @@ -0,0 +1,16 @@ +Format is tab separated temperature settings. +Name Value +WindowPosition 600+0 +WindowVisibilities +Open File for Temperature +Activate Temperature: False +Cooling Rate (Celcius/second): 3.0 +Heating Rate (Celcius/second): 10.0 +Temperature at Beginning (Celcius): 20.0 +Temperature of Base (Celcius): 180.0 +Temperature of Interface (Celcius): 180.0 +Temperature of Object First Layer Infill (Celcius): 170.0 +Temperature of Object First Layer Perimeter (Celcius): 180.0 +Temperature of Object Next Layers (Celcius): 160.0 +Temperature of Support Layers (Celcius): 170.0 +Temperature of Supported Layers (Celcius): 190.0 diff --git a/SkeinPyPy/skeinforge_application/profiles/milling/End_Mill/chop.csv b/SkeinPyPy/skeinforge_application/profiles/milling/End_Mill/chop.csv new file mode 100644 index 0000000..ace928d --- /dev/null +++ b/SkeinPyPy/skeinforge_application/profiles/milling/End_Mill/chop.csv @@ -0,0 +1,13 @@ +Format is tab separated chop preferences. +Name Value +Add Extra Top Layer if Necessary True +Open File to be Chopped +Import Coarseness (ratio): 1.0 +Correct Mesh True +Unproven Mesh False +Layer Thickness (mm): 0.4 +Layer Thickness over Precision (ratio): 10.0 +Layers From (index): 0 +Layers To (index): 999999999 +Perimeter Width (mm): 2.0 +windowPositionChop Preferences 600+0 diff --git a/SkeinPyPy/skeinforge_application/profiles/milling/End_Mill/lift.csv b/SkeinPyPy/skeinforge_application/profiles/milling/End_Mill/lift.csv new file mode 100644 index 0000000..a2cf543 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/profiles/milling/End_Mill/lift.csv @@ -0,0 +1,7 @@ +Format is tab separated lift preferences. +Name Value +Activate Lift: True +Cutting Lift over Layer Step (ratio): -0.5 +Open File to be Lifted /home/enrique/Desktop/backup/babbleold/script/reprap/pyRepRap/skeinforge_tools/craft_plugins/Screw Holder Bottom.stl +Clearance above Top (mm): 5.0 +windowPositionLift Preferences 440+53 diff --git a/SkeinPyPy/skeinforge_application/profiles/milling/Laser/chop.csv b/SkeinPyPy/skeinforge_application/profiles/milling/Laser/chop.csv new file mode 100644 index 0000000..b54d561 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/profiles/milling/Laser/chop.csv @@ -0,0 +1,13 @@ +Format is tab separated chop preferences. +Name Value +Add Extra Top Layer if Necessary True +Open File to be Chopped +Import Coarseness (ratio): 1.0 +Correct Mesh True +Unproven Mesh False +Layer Thickness (mm): 0.4 +Layer Thickness over Precision (ratio): 10.0 +Layers From (index): 0 +Layers To (index): 999999999 +Perimeter Width (mm): 0.2 +windowPositionChop Preferences 600+0 diff --git a/SkeinPyPy/skeinforge_application/profiles/milling/Laser/lift.csv b/SkeinPyPy/skeinforge_application/profiles/milling/Laser/lift.csv new file mode 100644 index 0000000..9818ca2 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/profiles/milling/Laser/lift.csv @@ -0,0 +1,7 @@ +Format is tab separated lift preferences. +Name Value +Activate Lift: True +Cutting Lift over Layer Step (ratio): 0.0 +Open File to be Lifted /home/enrique/Desktop/backup/babbleold/script/reprap/pyRepRap/skeinforge_tools/craft_plugins/Screw Holder Bottom.stl +Clearance above Top (mm): 5.0 +windowPositionLift Preferences 440+53 diff --git a/SkeinPyPy/skeinforge_application/runskeinforge.sh b/SkeinPyPy/skeinforge_application/runskeinforge.sh new file mode 100755 index 0000000..6c165a1 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/runskeinforge.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +# +# Utility script for keeping track of which preference settings a file was processed +# with, by copying the current preferences to a date-tagged directory together +# with the output files. +# +# Usage: runskeinforge.sh +# + +dir=`dirname $1` +file=`basename $1` + +for s in .gts .GTS .stl .STL; do + if [ ! `basename $file $s` = $file ]; then suffix=$s; fi +done + +if [ -n $suffix ]; then + filename=`basename $file $suffix` + newdir=$filename-`date +%m%d%H%M` + mkdir -p $newdir/skeinforge-prefs + cp $1 $newdir + cp ~/.skeinforge/*.csv $newdir/skeinforge-prefs + python skeinforge.py $newdir/$filename$suffix + echo $PWD/$newdir/${filename}_export.gcode +fi diff --git a/SkeinPyPy/skeinforge_application/show_skeinforge.sh b/SkeinPyPy/skeinforge_application/show_skeinforge.sh new file mode 100755 index 0000000..0548ca1 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/show_skeinforge.sh @@ -0,0 +1,8 @@ +#!/usr/bin/python +# +# Script to show the skeinforge dialog. +# +# Usage: set the executable property to true if it isn't already. Then double click the file. +# +import skeinforge +skeinforge.main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge.py b/SkeinPyPy/skeinforge_application/skeinforge.py new file mode 100755 index 0000000..590fd25 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge.py @@ -0,0 +1,656 @@ +#!/usr/bin/python +""" +This page is in the table of contents. +==Overview== +===Introduction=== +Skeinforge is a GPL tool chain to forge a gcode skein for a model. + +The tool chain starts with carve, which carves the model into layers, then the layers are modified by other tools in turn like fill, comb, tower, raft, stretch, hop, wipe, fillet & export. Each tool automatically gets the gcode from the previous tool. So if you want a carved & filled gcode, call the fill tool and it will call carve, then it will fill and output the gcode. If you want to use all the tools, call export and it will call in turn all the other tools down the chain to produce the gcode file. + +If you do not want a tool after preface to modify the output, deselect the Activate checkbox for that tool. When the Activate checkbox is off, the tool will just hand off the gcode to the next tool without modifying it. + +The skeinforge module provides a single place to call up all the setting dialogs. When the 'Skeinforge' button is clicked, skeinforge calls export, since that is the end of the chain. + +The plugin buttons which are commonly used are bolded and the ones which are rarely used have normal font weight. + +There are also tools which handle settings for the chain, like polyfile. + +The analyze tool calls plugins in the analyze_plugins folder, which will analyze the gcode in some way when it is generated if their Activate checkbox is selected. + +The interpret tool accesses and displays the import plugins. + +The default settings are similar to those on Nophead's machine. A setting which is often different is the 'Layer Height' in carve. + +===Command Line Interface=== +To bring up the skeinforge dialog without a file name, type: +python skeinforge_application/skeinforge.py + +Slicing a file from skeinforge_utilities/skeinforge_craft.py, for example: +python skeinforge_application/skeinforge_utilities/skeinforge_craft.py test.stl + +will slice the file and exit. This is the correct option for programs which use skeinforge to only generate a gcode file. + +Slicing a file from skeinforge.py, for example: +python skeinforge_application/skeinforge.py test.stl + +will slice the file and bring up the skeinforge window and the analyze windows and then skeinforge will wait for user input. + +Slicing a file from skeinforge_plugins/craft.py, for example: +python skeinforge_application/skeinforge_plugins/craft.py test.stl + +will slice the file and bring up the analyze windows only and then skeinforge will wait for user input. + +===Contribute=== +You can contribute by helping develop the manual at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge + +There is also a forum thread about how to contribute to skeinforge development at: +http://dev.forums.reprap.org/read.php?12,27562 + +I will only reply to emails from contributors or to complete bug reports. + +===Documentation=== +There is a manual at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge + +There is also documentation is in the documentation folder, in the doc strings for each module and it can be called from the '?' button or the menu or by clicking F1 in each setting dialog. + +A list of other tutorials is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge#Tutorials + +Skeinforge tagged pages on thingiverse can be searched for at: +http://www.thingiverse.com/search?cx=015525747728168968820%3Arqnsgx1xxcw&cof=FORID%3A9&ie=UTF-8&q=skeinforge&sa=Search&siteurl=www.thingiverse.com%2F#944 + +===Fabrication=== +To fabricate a model with gcode and the Arduino you can use the send.py in the fabricate folder. The documentation for it is in the folder as send.html and at: +http://reprap.org/bin/view/Main/ArduinoSend + +Another way is to use an EMC2 or similar computer controlled milling machine, as described in the "ECM2 based repstrap" forum thread at: +http://forums.reprap.org/read.php?1,12143 + +using the M-Apps package, which is at: +http://forums.reprap.org/file.php?1,file=772 + +Another way is to use Zach's ReplicatorG at: +http://replicat.org/ + +There is also an older Processing script at: +http://reprap.svn.sourceforge.net/viewvc/reprap/trunk/users/hoeken/arduino/GCode_Host/ + +Yet another way is to use the reprap host, written in Java, to load and print gcode: +http://dev.www.reprap.org/bin/view/Main/DriverSoftware#Load_GCode + +For jogging, the Metalab group wrote their own exerciser, also in Processing: +http://reprap.svn.sourceforge.net/viewvc/reprap/trunk/users/metalab/processing/GCode_Exerciser/ + +The Metalab group has descriptions of skeinforge in action and their adventures are described at: +http://reprap.soup.io/ + +There is a board about printing issues at: +http://www.bitsfrombytes.com/fora/user/index.php?board=5.0 + +You can buy the Rapman (an improved Darwin) from Bits from Bytes at: +http://www.bitsfrombytes.com/ + +You can buy the Makerbot from Makerbot Industries at: +http://www.makerbot.com/ + +===File Formats=== +An explanation of the gcodes is at: +http://reprap.org/bin/view/Main/Arduino_GCode_Interpreter + +and at: +http://reprap.org/bin/view/Main/MCodeReference + +A gode example is at: +http://forums.reprap.org/file.php?12,file=565 + +The settings are saved as tab separated .csv files in the .skeinforge folder in your home directory. The settings can be set in the tool dialogs. The .csv files can also be edited with a text editor or a spreadsheet program set to separate tabs. + +The Scalable Vector Graphics file produced by vectorwrite can be opened by an SVG viewer or an SVG capable browser like Mozilla: +http://www.mozilla.com/firefox/ + +A good triangle surface format is the GNU Triangulated Surface format, which is supported by Mesh Viewer and described at: +http://gts.sourceforge.net/reference/gts-surfaces.html#GTS-SURFACE-WRITE + +You can export GTS files from Art of Illusion with the Export GNU Triangulated Surface.bsh script in the Art of Illusion Scripts folder. + +STL is an inferior triangle surface format, described at: +http://en.wikipedia.org/wiki/STL_(file_format) + +If you're using an STL file and you can't even carve it, try converting it to a GNU Triangulated Surface file in Art of Illusion. If it still doesn't carve, then follow the advice in the troubleshooting section. + +===Getting Skeinforge=== +The latest version is at: +http://members.axion.net/~enrique/reprap_python_beanshell.zip + +a sometimes out of date version is in the last reprap_python_beanshell.zip attachment in the last post of the Fabmetheus blog at: +http://fabmetheus.blogspot.com/ + +another sometimes out of date version is at: +https://reprap.svn.sourceforge.net/svnroot/reprap/trunk/reprap/miscellaneous/python-beanshell-scripts/ + +===Getting Started=== +For skeinforge to run, install python 2.x on your machine, which is available from: +http://www.python.org/download/ + +To use the settings dialog you'll also need Tkinter, which probably came with the python installation. If it did not, look for it at: +http://www.tcl.tk/software/tcltk/ + +If you want python and Tkinter together on MacOS, you can try: +http://www.astro.washington.edu/users/rowen/ROPackage/Overview.html + +If you want python and Tkinter together on all platforms and don't mind filling out forms, you can try the ActivePython package from Active State at: +http://www.activestate.com/Products/activepython/feature_list.mhtml + +The computation intensive python modules will use psyco if it is available and run about twice as fast. Psyco is described at: +http://psyco.sourceforge.net/index.html + +The psyco download page is: +http://psyco.sourceforge.net/download.html + +Skeinforge imports Stereolithography (.stl) files or GNU Triangulated Surface (.gts) files. If importing an STL file directly doesn't work, an indirect way to import an STL file is by turning it into a GTS file is by using the Export GNU Triangulated Surface script at: +http://members.axion.net/~enrique/Export%20GNU%20Triangulated%20Surface.bsh + +The Export GNU Triangulated Surface script is also in the Art of Illusion folder, which is in the same folder as skeinforge.py. To bring the script into Art of Illusion, drop it into the folder ArtOfIllusion/Scripts/Tools/. Then import the STL file using the STL import plugin in the import submenu of the Art of Illusion file menu. Then from the Scripts submenu in the Tools menu, choose 'Export GNU Triangulated Surface' and select the imported STL shape. Click the 'Export Selected' checkbox and click OK. Once you've created the GTS file, you can turn it into gcode by typing in a shell in the same folder as skeinforge: +> python skeinforge.py + +When the skeinforge dialog pops up, click 'Skeinforge', choose the file which you exported in 'Export GNU Triangulated Surface' and the gcode file will be saved with the suffix '_export.gcode'. + +Or you can turn files into gcode by adding the file name, for example: +> python skeinforge.py Screw Holder Bottom.stl + +===License=== +GNU Affero General Public License +http://www.gnu.org/licenses/agpl.html + +===Motto=== +I may be slow, but I get there in the end. + +===Troubleshooting=== +If there's a bug, try downloading the very latest version because skeinforge is often updated without an announcement. The very latest version is at: +http://members.axion.net/~enrique/reprap_python_beanshell.zip + +If there is still a bug, then first prepare the following files: + +1. stl file +2. pictures explaining the problem +3. your settings (pack the whole .skeinforge directory with all your settings) +4. alterations folder, if you have any active alterations files + +Then zip all the files. + +Second, write a description of the error, send the description and the archive to the developer, enrique ( perez_enrique AT yahoo.com.removethispart ). After a bug fix is released, test the new version and report the results to enrique, whether the fix was successful or not. + +If the dialog window is too big for the screen, on most Linux window managers you can move a window by holding down the Alt key and then drag the window with the left mouse button to get to the off screen widgets. + +If you can't use the graphical interface, you can change the settings for skeinforge by using a text editor or spreadsheet to change the settings in the profiles folder in the .skeinforge folder in your home directory. + +Comments and suggestions are welcome, however, I won't reply unless you are a contributor. Likewise, I will only answer your questions if you contribute to skeinforge in some way. Some ways of contributing to skeinforge are in the contributions thread at: +http://dev.forums.reprap.org/read.php?12,27562 + +You could also contribute articles to demozendium on any topic: +http://fabmetheus.crsndoo.com/wiki/index.php/Main_Page + +If you contribute in a significant way to another open source project, I will consider that also. + +When I answered everyone's questions, eventually I received more questions than I had time to answer, so now I only answer questions from contributors. + +I reserve the right to make any correspondence public. Do not send me any correspondence marked confidential. If you do I will delete it. + + +==Examples== +The following examples forge the STL file Screw Holder.stl. The examples are run in a terminal in the folder which contains Screw Holder.gts and skeinforge.py. + +> python skeinforge.py +This brings up the dialog, after clicking 'Skeinforge', the following is printed: +The exported file is saved as Screw Holder_export.gcode + +> python skeinforge.py Screw Holder.stl +The exported file is saved as Screw Holder_export.gcode + +To run only fill for example, type in the craft_plugins folder which fill is in: +> python fill.py + +""" + +from __future__ import absolute_import +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from optparse import OptionParser +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import os +import sys +import platform +import subprocess + + +# document raft, stretch, then carve, comb, fill, inset, oozebane, splodge, temperature, speed once they are updated +# wiki document help, description, polyfile +# subplugins like export static, maybe later mill cut and coil plugins, maybe later still export plugins & change file extension to output file extension http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge +# +# backup demozendium links +# replace replace baseLayerThickness.. with baseLayerHeightMultiplier +# announce layer thickness with layer height +# +# unimportant +# minor outline problem when an end path goes through a path, like in the letter A +# view profile 1 mm thickness +# analyze doesn't save skeinlayer settings, remember xy in skeiniso +# +# +# +# question, should 'Infill Odd Layer Extra Rotation' be dropped +# consolidate Object First Layer Flow +# +# retraction step leave +# melt _extrusion +# think about http://code.google.com/p/skeinarchiver/ and/or undo +# add volume fraction to fill +# getStrokeRadius default to edgeWidth +# look at loop end removed bug in upper loop of layer 8 of Screw_Holder_alteration +# fix tower edge line start problem +# check globalExecutionOrder, ensure that bottom order is really high +# set temperature in temperature +# maybe rename geometry_plugins xml +# maybe add carve preview, opening it up in browser +# dwindle or dawdle or taper +# voronoi average location intersection looped inset intercircles +# skin layers without something over the infill +# check for last existing then remove unneeded fill code (getLastExistingFillLoops) from euclidean, add fill in penultimate loops, if there is no fill it should not use edge - skin should work +# delete commented addInfillPerimeter +# unpause slow flow rate instead of speeding feed rate +# maybe in svgReader if loop intersection with previous union else add +# add links download manual svg_writer, add left right arrow keys to layer +# delete location from wipe, in other words Arrival X instead of Location Arrival X, also convert Location Arrival to Arrival Location +# command +# manipulation derivations +# cutting ahmet +# +# When opening a file for craft I wondered if there is an option to set the file type to .stl as it currently defaults to .xml +# then add Retraction Scaling Exponent +# check inset loop for intersection with loopLayer.loops +# maybe make vectorwrite prominent, not skeiniso, probably not because it doesn't work on Mac +# close, getPillarByLoopLists, addConcave, polymorph original graph section, loop, add step object, add continuous object +# chamber: heated bed off at a layer http://blog.makerbot.com/2011/03/17/if-you-cant-stand-the-heat/ +# profile copy / rename / delete, maybe move craft type to profile +# think about rectangular getVector3RemoveByPre.. +# del previous, add begin & end if far get actual path +# bridge infill modifiers only in the bridge infill loop +# linearbearingexample 15 x 1 x 2, linearbearingcage +# polling +# connectionfrom, to, connect, xaxis +# move replace from export to alterations +# lathe, transform normal in getRemaining, getConnection +# add overview link to crnsdoo index and svg page +# getConnection of some kind like getConnectionVertexes, getConnection +# incorporate actual thickness from feed rate and flow rate in statistics for dimension +# update stretch pictures By design, distance between parallel sides in hexagonal hole are 13mm, 7mm, 6.5mm, round hole diameter's are 8mm, 4mm and 3mm. http://fabmetheus.crsndoo.com/wiki/images/Stretch.png http://fabmetheus.crsndoo.com/wiki/images/thumb/NormalHole.png/180px-NormalHole.png http://fabmetheus.crsndoo.com/wiki/images/thumb/StretchDeformedHole.png/180px-StretchDeformedHole.png +# xml_creation +# 'fileName, text, repository' commandLineInterface +# delete: text = text.replace(('\nName %sValue\n' % globalSpreadsheetSeparator), ('\n_Name %sValue\n' % globalSpreadsheetSeparator)) +# comment search from home panel when there is an input field +# +# +# multiply to table + boundary bedBound bedWidth bedHeight bedFile.csv +# getNormal, getIsFlat? +# info statistics, procedures, xml if any +# test solid arguments +# combine xmlelement with csvelement using example.csv & geometry.csv, csv _format, _column, _row, _text +# pixel, voxel, surfaxel/boxel, lattice, mesh +# probably not replace getOverlapRatio with getOverlap if getOverlapRatio is never small, always 0.0 +# mesh. for cube, then cylinder, then sphere after lathe +# dimension extrude diameter, density +# superformula http://www.thingiverse.com/thing:12419 +# maybe get rid of testLoops once they are no longer needed +# thermistor lookup table +# stretch maybe add back addAlong +# import, write, copy examples +# maybe remove default warnings from scale, rotate, translate, transform +# easy helix +# write tool; maybe write one deep +# +# +# tube +# rotor +# coin +# demozendium privacy policy, maybe thumbnail logo +# pymethe +# test translate +# full lathe +# pyramid +# round extrusion ?, fillet +# make html statistics, move statistics to folder +# manipulate solid, maybe manipulate around elements +# boolean loop corner outset +# mechaslab advanced drainage, shingles +# dovetail +# maybe not getNewObject, getNew, addToBoolean +# work out close and radius +# maybe have add function as well as append for list and string +# maybe move and give geometryOutput to cube, cylinder, sphere +# +# maybe move widen before bottom +# maybe add 1 to max layer input to iso in layer_template.svg +# maybe save all generated_files option +# table to dictionary +# remove cool set at end of layer +# add fan on when hot in chamber +# maybe measuring rod +# getLayerHeight from xml +# maybe center for xy plane +# remove comments from clip, bend +# winding into coiling, coil into wind & weave +# later, precision +# documentation +# http://wiki.makerbot.com/configuring-skeinforge +# +# +# remove index from CircleIntersection remove ahead or behind from CircleIntersection _speed +# probably not speed up CircleIntersection by performing isWithinCircles before creation _speed +# don't remove brackets in early craft tools _speed +# check bounding box when subtracting or intersecting boolean geometry +# get arounds in inset, the inside become extrude loops and the outside below loops _speed +# +# +# add hook _extrusion +# integral thin width _extrusion +# layer color, for multilayer start http://reprap.org/pub/Main/MultipleMaterialsFiles/legend.xml _extrusion +# maybe raft triple layer base, middle interface with hot loop or ties +# somehow, add pattern to outside, http://blog.makerbot.com/2010/09/03/lampshades/ +# implement acceleration & collinear removal in penultimate viewers _extrusion +# +# rename skeinforge_profile.addListsToCraftTypeRepository to skeinforge_profile.addToCraftTypeRepository after skirt +# basic basedit tool +# arch, ceiling +# meta setting, rename setting _setting +# add polish, has edge, has cut first layer (False) +# probably not set addedLocation in distanceFeedRate after arc move +# maybe horizontal bridging and/or check to see if the ends are standing on anything +# thin self? check when removing intersecting paths in inset +# save all analyze viewers of the same name except itself, update help menu self.wikiManualPrimary.setUpdateFunction +# check alterations folder first, if there is something copy it to the home directory, if not check the home directory +# add links to demozendium in help +# maybe add hop only if long option +# +# +# +# help primary menu item refresh +# add plugin help menu, add craft below menu +# give option of saving when switching profiles +# xml & svg more forgiving, svg make defaults for layerHeight +# option of surrounding lines in display +# maybe add connecting line in display line +# maybe check inset loops to see if they are smaller, but this would be slow +# maybe status bar +# maybe measurement ruler mouse tool +# search rss from blogs, add search links for common materials, combine created on or progress bar with searchable help +# boundaries, center radius z bottom top, alterations file, circular or rectangular, polygon, put cool minimum radius orbits within boundaries, bound.. +# move & rotate model +# possible jitter bug http://cpwebste.blogspot.com/2010/04/hydras-first-print.html +# trial, meta in a grid settings +# maybe interpret svg_convex_mesh +#laminate tool head +#maybe use 5x5 radius search in circle node +#maybe add layer updates in behold, skeinlayer and maybe others +#lathe winding, extrusion and cutting; synonym for rotation or turning, loop angle +# maybe split into source code and documentation sections +# transform plugins, start with sarrus http://www.thingiverse.com/thing:1425 +# maybe make setting backups +# move skeinforge_utilities to fabmetheus_utilities +# maybe lathe cutting +# maybe lathe extrusion +# maybe lathe milling +# maybe lathe winding & weaving +# +# +# +# pick and place +# search items, search links, choice entry field +# svg triangle mesh, svg polygon mesh +# simulate +#transform +# juricator +# probably not run along sparse infill to avoid stops +#custom inclined plane, inclined plane from model, screw, fillet travel as well maybe +# probably not stretch single isLoop +#maybe much afterwards make congajure multistep view +#maybe stripe although model colors alone can handle it +#stretch fiber around shape, maybe modify winding for asymmetric shapes +#multiple heads around edge +#maybe add rarely used tool option +#angle shape for overhang extrusions +#maybe m111? countdown +#first time tool tip +#individual tool tip to place in text +# maybe try to simplify raft layer start +# maybe make temp directory +# maybe carve aoi xml testing and check xml gcode +# maybe cross hatch support polishing??? +# maybe print svg view from current layer or zero layer in single view +# maybe check if tower is picking the closest island +# maybe combine skein classes in fillet +# maybe isometric svg option + +#Manual +#10,990 +#11,1776,786 +#12,3304,1528 +#1,4960,1656 +#2, 7077,2117 +#3, 9598,2521 +#4 12014,2305 +#5 14319,2536 +#6 16855,3226 +#7 20081, 2189 +#8 22270, 2625 +#9 24895, 2967, 98 +#10 27862, 3433, 110 +#11 31295, 3327 +#12 34622 +#85 jan7, 86jan11, 87 jan13, 88 jan15, 91 jan21, 92 jan23, 95 jan30, 98 feb6 +#make one piece electromagnet spool +#stepper rotor with ceramic disk magnet in middle, electromagnet with long thin spool line? +#stepper motor +#make plastic coated thread in vat with pulley +#tensile stuart platform +#kayak +#gear vacuum pump +#gear turbine +#heat engine +#solar power +#sailboat +#yacht +#house +#condo with reflected gardens in between buildings +#medical equipment +#cell counter, etc.. +#pipe clamp lathe +# square tube driller & cutter + +# archihedrongagglevoteindexium +# outline images +# look from top of intersection circle plane to look for next, add a node; tree out until all are stepped on then connect, when more than three intersections are close +# when loading a file, we should have a preview of the part and orientation in space +# second (and most important in my opinion) would be the ability to rotate the part on X/Y/Z axis to chose it's orientation +# third, a routine to detect the largest face and orient the part accordingly. Mat http://reprap.kumy.net/ +# concept, three perpendicular slices to get display spheres +# extend lines around short segment after cross hatched boolean +# concept, donation, postponement, rotate ad network, cached search options +# concept, local ad server, every time the program runs it changes the iamge which all the documentation points to from a pool of ads +# concept, join cross slices, go from vertex to two orthogonal edges, then from edges to each other, if not to a common point, then simplify polygons by removing points which do not change the area much +# concept, each node is fourfold, use sorted intersectionindexes to find close, connect each double sided edge, don't overlap more than two triangles on an edge +# concept, diamond cross section loops +# concept, in file, store polygon mesh and centers +# concept, display spheres or polygons would have original triangle for work plane +# .. then again no point with slices +# concept, filled slices, about 2 mm thick +# concept, rgb color triangle switch to get inside color, color golden ratio on 5:11 slope with a modulo 3 face +# concept, interlaced bricks at corners ( length proportional to corner angle ) +# concept, new links to archi, import links to archi and adds skeinforge tool menu item, back on skeinforge named execute tool is added +# concept, trnsnt +# concept, indexium expand condense remove, single text, pymetheus +# concept, inscribed key silencer +# concept, spreadsheet to python and/or javascript +# concept, range voting for posters, informative, complainer, funny, insightful, rude, spammer, literacy, troll? +# concept, intermittent cloud with multiple hash functions + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = """ +Adrian Bowyer +Brendan Erwin +Greenarrow +Ian England +John Gilmore +Jonwise +Kyle Corbitt +Michael Duffin +Marius Kintel +Nophead +PJR +Reece.Arnott +Wade +Xsainnz +Zach Hoeken + +Organizations: +Art of Illusion """ +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def addToProfileMenu(profileSelection, profileType, repository): + 'Add a profile menu.' + pluginFileNames = skeinforge_profile.getPluginFileNames() + craftTypeName = skeinforge_profile.getCraftTypeName() + pluginModule = skeinforge_profile.getCraftTypePluginModule() + profilePluginSettings = settings.getReadRepository(pluginModule.getNewRepository()) + for pluginFileName in pluginFileNames: + skeinforge_profile.ProfileTypeMenuRadio().getFromMenuButtonDisplay(profileType, pluginFileName, repository, craftTypeName == pluginFileName) + for profileName in profilePluginSettings.profileList.value: + skeinforge_profile.ProfileSelectionMenuRadio().getFromMenuButtonDisplay(profileSelection, profileName, repository, profileName == profilePluginSettings.profileListbox.value) + +def getNewRepository(): + 'Get new repository.' + return SkeinforgeRepository() + +def getPluginFileNames(): + 'Get skeinforge plugin fileNames.' + return archive.getPluginFileNamesFromDirectoryPath(archive.getSkeinforgePluginsPath()) + +def getRadioPluginsAddPluginGroupFrame(directoryPath, importantFileNames, names, repository): + 'Get the radio plugins and add the plugin frame.' + repository.pluginGroupFrame = settings.PluginGroupFrame() + radioPlugins = [] + for name in names: + radioPlugin = settings.RadioPlugin().getFromRadio(name in importantFileNames, repository.pluginGroupFrame.latentStringVar, name, repository, name == importantFileNames[0]) + radioPlugin.updateFunction = repository.pluginGroupFrame.update + radioPlugins.append( radioPlugin ) + defaultRadioButton = settings.getSelectedRadioPlugin(importantFileNames + [radioPlugins[0].name], radioPlugins) + repository.pluginGroupFrame.getFromPath(defaultRadioButton, directoryPath, repository) + return radioPlugins + +def writeOutput(fileName): + 'Craft a file, display dialog.' + repository = getNewRepository() + repository.fileNameInput.value = fileName + repository.execute() + + +class SkeinforgeRepository: + 'A class to handle the skeinforge settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge.html', self) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Skeinforge', self, '') + self.profileType = settings.MenuButtonDisplay().getFromName('Profile Type: ', self ) + self.profileType.columnspan = 6 + self.profileSelection = settings.MenuButtonDisplay().getFromName('Profile Selection: ', self) + self.profileSelection.columnspan = 6 + addToProfileMenu( self.profileSelection, self.profileType, self ) + settings.LabelDisplay().getFromName('', self) + importantFileNames = ['craft', 'profile'] + getRadioPluginsAddPluginGroupFrame(archive.getSkeinforgePluginsPath(), importantFileNames, getPluginFileNames(), self) + self.executeTitle = 'Skeinforge a file...' + + def getPyPyExe(self): + if platform.system() == "Windows": + pypyExe = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../pypy/pypy.exe")); + else: + pypyExe = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../pypy/bin/pypy")); + if os.path.exists(pypyExe): + return pypyExe + pypyExe = "/bin/pypy"; + if os.path.exists(pypyExe): + return pypyExe + pypyExe = "/usr/bin/pypy"; + if os.path.exists(pypyExe): + return pypyExe + pypyExe = "/usr/local/bin/pypy"; + if os.path.exists(pypyExe): + return pypyExe + return False + + def execute(self): + 'Skeinforge button has been clicked.' + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + pypyExe = self.getPyPyExe() + for fileName in fileNames: + if platform.python_implementation() == "PyPy": + skeinforge_craft.writeOutput(fileName) + elif pypyExe == False: + print "************************************************" + print "* Failed to find pypy, so slicing with python! *" + print "************************************************" + skeinforge_craft.writeOutput(fileName) + print "************************************************" + print "* Failed to find pypy, so sliced with python! *" + print "************************************************" + else: + subprocess.call([pypyExe, __file__, fileName]) + + def save(self): + 'Profile has been saved and profile menu should be updated.' + self.profileType.removeMenus() + self.profileSelection.removeMenus() + addToProfileMenu(self.profileSelection, self.profileType, self) + self.profileType.addRadiosToDialog(self.repositoryDialog) + self.profileSelection.addRadiosToDialog(self.repositoryDialog) + + +def main(): + 'Display the skeinforge dialog.' + parser = OptionParser() + parser.add_option( + '-p', '--prefdir', help='set path to preference directory', action='store', type='string', dest='preferencesDirectory') + parser.add_option( + '-s', '--start', help='set start file to use', action='store', type='string', dest='startFile') + parser.add_option( + '-e', '--end', help='set end file to use', action='store', type='string', dest='endFile') + parser.add_option( + '-o', '--option', help='set an individual option in the format "module:preference=value"', + action='append', type='string', dest='preferences') + (options, args) = parser.parse_args() + if options.preferencesDirectory: + archive.globalTemporarySettingsPath = options.preferencesDirectory + if options.preferences: + for prefSpec in options.preferences: + (moduleName, prefSpec) = prefSpec.split(':', 1) + (prefName, valueName) = prefSpec.split('=', 1) + settings.addPreferenceOverride(moduleName, prefName, valueName) + sys.argv = [sys.argv[0]] + args + if len( args ) > 0: + writeOutput( ' '.join(args) ) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/__init__.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/__init__.py new file mode 100644 index 0000000..2dc8ddc --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 2 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze.py new file mode 100644 index 0000000..99604b2 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze.py @@ -0,0 +1,60 @@ +""" +This page is in the table of contents. +Analyze is a script to access the plugins which analyze a gcode file. + +The plugin buttons which are commonly used are bolded and the ones which are rarely used have normal font weight. + +==Gcodes== +An explanation of the gcodes is at: +http://reprap.org/bin/view/Main/Arduino_GCode_Interpreter + +and at: +http://reprap.org/bin/view/Main/MCodeReference + +A gode example is at: +http://forums.reprap.org/file.php?12,file=565 + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_analyze +import sys + + +__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 addToMenu(master, menu, repository, window): + "Add a tool plugin menu." + analyzeFilePath = archive.getSkeinforgePluginsPath('analyze.py') + pluginsDirectoryPath = skeinforge_analyze.getPluginsDirectoryPath() + settings.addPluginsParentToMenu(pluginsDirectoryPath, menu, analyzeFilePath, skeinforge_analyze.getPluginFileNames()) + +def getNewRepository(): + 'Get new repository.' + return skeinforge_analyze.AnalyzeRepository() + +def writeOutput(fileName): + "Analyze a gcode file." + repository = getNewRepository() + repository.fileNameInput.value = fileName + repository.execute() + settings.startMainLoopFromConstructor(repository) + + +def main(): + "Display the analyze dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/__init__.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/__init__.py new file mode 100644 index 0000000..1121e8a --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 3 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/__init__.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/__init__.py new file mode 100644 index 0000000..58ec332 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 4 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/display_line.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/display_line.py new file mode 100644 index 0000000..5a8c79c --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/display_line.py @@ -0,0 +1,124 @@ +""" +This page is in the table of contents. +Display line is a mouse tool to select and display information about the line. + +When a line is clicked, the line will be selected and information about the line will be displayed. If a gcode line is clicked, the information will be file line count of the line clicked, counting from one, and the line itself. + +When the display line tool is chosen and the canvas has the focus, display line will listen to the arrow keys. Clicking in the canvas gives the canvas the focus, and when the canvas has the focus a thick black border is drawn around the canvas. When the right arrow key is pressed, display line will increase the line index of the layer by one, and change the selection accordingly. If the line index of the layer goes over the index of the last line, the layer index will be increased by one and the new line index will be zero. When the left arrow key is pressed, the index will be decreased. If the line index goes below the index of the first line, the layer index will be decreased by one and the new line index will be at the last line. The up arrow key increases the layer index by one and the down arow key decreases the line index. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base import MouseToolBase +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings + +__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 getNewMouseTool(): + "Get a new mouse tool." + return DisplayLine() + + +class DisplayLine( MouseToolBase ): + "Display the line when it is clicked." + def button1( self, event, shift = False ): + "Print line text and connection line." + self.destroyEverythingGetFocus() + x = self.canvas.canvasx(event.x) + y = self.canvas.canvasy(event.y) + tags = self.getTagsGivenXY(x, y) + if tags == '': + return + if tags.startswith('colored_line_index:'): + splitLine = tags.split() + coloredLineIndex = int(splitLine[1]) + self.repository.line.value = coloredLineIndex + tags = self.getSelectedColoredLine().displayString + self.drawLineText( complex( float(x), float(y) ), tags ) + + def destroyEverything(self): + "Destroy items." + self.canvas.delete('mouse_item') + self.canvas.delete('selection_line') + + def drawLineText( self, location, tags ): + "Draw the line text." + self.window.getDrawnLineText( location, 'mouse_item', tags ) + + def drawSelectedColoredLineText(self): + "Draw the selected line and text." + selectedColoredLine = self.getSelectedColoredLine() + if len( self.canvas.find_withtag('selection_line') ) < 1 or selectedColoredLine == None: + return + tags = selectedColoredLine.displayString + lineCoordinates = self.canvas.coords( self.canvas.find_withtag('selection_line')[-1] ) + begin = complex( lineCoordinates[0], lineCoordinates[1] ) + end = complex( lineCoordinates[2], lineCoordinates[3] ) + segment = end - begin + segmentLength = abs(segment) + if segmentLength <= 0.0: + return + towardEnd = 0.75 * segment + segmentClockwise = 20.0 * complex( segment.imag, - segment.real ) / segmentLength + location = begin + towardEnd + segmentClockwise + self.drawLineText( location, tags ) + + def getSelectedColoredLine(self): + "Draw the selected line, add it to the items and return the colored line." + self.window.cancelTimerResetButtons() + coloredLines = self.window.getColoredLines() + self.repository.line.value = max(0, self.repository.line.value) + if len(coloredLines) < 1: + return None + self.repository.line.value = min(len(coloredLines) - 1, self.repository.line.value) + self.window.setLineButtonsState() + coloredLine = coloredLines[self.repository.line.value] + lineCoordinates = self.canvas.coords(self.window.getDrawnSelectedColoredLine(coloredLine)) + end = complex(lineCoordinates[2], lineCoordinates[3]) + radiusComplex = complex(16.0, 16.0) + upperLeft = end - radiusComplex + lowerRight = end + radiusComplex + self.canvas.create_oval (int(upperLeft.real), int(upperLeft.imag), int(lowerRight.real), int(lowerRight.imag), tags = 'mouse_item') + return coloredLine + + def isSelectionTool(self): + "Return if this mouse tool is a selection tool." + return True + + def keyPressDown(self, event): + "The down arrow was pressed." + self.destroyEverything() + self.window.setLayerIndex( self.repository.layer.value - 1 ) + + def keyPressLeft(self, event): + "The left arrow was pressed." + self.destroyEverything() + self.repository.line.value -= 1 + if self.window.isLineBelowZeroSetLayer(): + return + self.drawSelectedColoredLineText() + + def keyPressRight(self, event): + "The right arrow was pressed." + self.destroyEverything() + self.repository.line.value += 1 + if self.window.isLineBeyondListSetLayer(): + return + self.drawSelectedColoredLineText() + + def keyPressUp(self, event): + "The up arrow was pressed." + self.destroyEverything() + self.window.setLayerIndex( self.repository.layer.value + 1 ) + + def update(self): + "Update the mouse tool." + self.destroyEverything() + self.drawSelectedColoredLineText() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/mouse_tool_base.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/mouse_tool_base.py new file mode 100644 index 0000000..3ca8457 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/mouse_tool_base.py @@ -0,0 +1,85 @@ +""" +Display line is a mouse tool to display the line index of the line clicked, counting from one, and the line itself. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +class MouseToolBase: + "The mouse tool base class, which does nothing." + def button1(self, event): + "The left button was clicked, function." + pass + + def buttonRelease1(self, event): + "The left button was released, function." + pass + + def destroyEverything(self): + "Destroy items." + self.canvas.delete('mouse_item') + + def destroyEverythingGetFocus(self): + "Destroy items and get the focus for the canvas." + self.destroyEverything() + self.canvas.focus_set() + + def getReset( self, window ): + "Reset the mouse tool to default." + self.setWindowItems( window ) + self.destroyEverything() + return self + + def getTagsGivenXY( self, x, y ): + "Get the tag for the x and y." + tags = self.canvas.itemcget( self.canvas.find_closest(x, y), 'tags') + currentEnd = ' current' + if tags.find( currentEnd ) != - 1: + return tags[ : - len( currentEnd ) ] + return tags + + def isSelectionTool(self): + "Return if this mouse tool is a selection tool." + return False + + def keyPressDown(self, event): + "The down arrow was pressed." + pass + + def keyPressLeft(self, event): + "The left arrow was pressed." + pass + + def keyPressReturn(self, event): + "The return key was pressed." + pass + + def keyPressRight(self, event): + "The right arrow was pressed." + pass + + def keyPressUp(self, event): + "The up arrow was pressed." + pass + + def motion( self, event, shift = False ): + "The mouse moved, function." + pass + + def setWindowItems( self, window ): + "Set the canvas and items." + self.canvas = window.canvas + self.repository = window.repository + self.window = window + + def update(self): + "Update the mouse tool." + pass diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/tableau.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/tableau.py new file mode 100644 index 0000000..c282d95 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/tableau.py @@ -0,0 +1,777 @@ +""" +Tableau has a couple of base classes for analyze viewers. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities import zoom_in +from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities import zoom_out +import math +import os + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getGeometricDifference( first, second ): + 'Get the geometric difference of the two numbers.' + return max( first, second ) / min( first, second ) + +def getGridHorizontalFrame(gridPosition): + 'Get the grid horizontal object with a frame from the grid position.' + gridHorizontal = settings.GridHorizontal( 0, 0 ) + gridHorizontal.master = settings.Tkinter.Frame( gridPosition.master, borderwidth = 1, padx = 3, relief = 'raised') + gridHorizontal.master.grid( row = gridPosition.row, column = gridPosition.column, sticky = settings.Tkinter.E ) + return gridHorizontal + +def getIsLayerStart(firstWord, skein, splitLine): + 'Determine if the line is the start of a layer.' + if skein.isThereALayerStartWord: + return firstWord == '(' + if firstWord != 'G1' and firstWord != 'G2' and firstWord != 'G3': + return False + location = gcodec.getLocationFromSplitLine(skein.oldLocation, splitLine) + if location.z - skein.oldZ > 0.1: + skein.oldZ = location.z + return True + return False + +def getLengthMinusOneMinimumOne( elementList ): + 'Get the length of the length minus one, minimum one.' + return max( 1, len( elementList ) - 1 ) + +def getPluginsDirectoryPath(): + 'Get the plugins directory path.' + return archive.getAnalyzePluginsDirectoryPath('export_canvas_plugins') + +def getScrollbarCanvasPortion( scrollbar ): + 'Get the canvas portion of the scrollbar.' + scrollbarBeginEnd = scrollbar.get() + return scrollbarBeginEnd[1] - scrollbarBeginEnd[0] + +def setStateNormalDisabled( active, widget ): + 'Set the state of the widget to normal if active and disabled if inactive.' + if active: + widget.config( state = settings.Tkinter.NORMAL ) + else: + widget.config( state = settings.Tkinter.DISABLED ) + + +class ColoredLine: + 'A colored index line.' + def __init__( self, begin, colorName, displayString, end, tagString ): + 'Set the color name and corners.' + self.begin = begin + self.colorName = colorName + self.displayString = displayString + self.end = end + self.tagString = tagString + + def __repr__(self): + 'Get the string representation of this colored index line.' + return '%s, %s, %s, %s' % ( self.colorName, self.begin, self.end, self.tagString ) + + +class ExportCanvasDialog: + 'A class to display the export canvas repository dialog.' + def addPluginToMenu( self, canvas, fileName, menu, name, suffix ): + 'Add the display command to the menu.' + self.canvas = canvas + self.fileName = fileName + self.name = name + self.suffix = suffix + menu.add_command( label = settings.getEachWordCapitalized( self.name ), command = self.display ) + + def display(self): + 'Display the export canvas repository dialog.' + for repositoryDialog in settings.globalRepositoryDialogListTable: + if repositoryDialog.repository.lowerName == self.name: + repositoryDialog.setCanvasFileNameSuffix(self.canvas, self.skein.fileName, self.suffix) + settings.liftRepositoryDialogs(settings.globalRepositoryDialogListTable[repositoryDialog]) + return + pluginModule = archive.getModuleWithDirectoryPath(getPluginsDirectoryPath(), self.name) + if pluginModule == None: + return None + pluginRepository = pluginModule.getNewRepository() + pluginRepository.setCanvasFileNameSuffix(self.canvas, self.fileName, self.suffix) + settings.getDisplayedDialogFromConstructor(pluginRepository) + + +class TableauRepository: + 'The viewer base repository class.' + def addAnimation(self): + 'Add the animation settings.' + self.frameList = settings.FrameList().getFromValue('Frame List', self, [] ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Animation -', self ) + self.animationLineQuickening = settings.FloatSpinUpdate().getFromValue( 0.5, 'Animation Line Quickening (ratio):', self, 4.5, 1.0 ) + self.animationSlideShowRate = settings.FloatSpinUpdate().getFromValue( 1.0, 'Animation Slide Show Rate (layers/second):', self, 5.0, 2.0 ) + settings.LabelSeparator().getFromRepository(self) + + def addScaleScreenSlide(self): + 'Add the scale, screen and slide show settings.' + self.scale = settings.FloatSpinNotOnMenu().getFromValue( 10.0, 'Scale (pixels per millimeter):', self, 50.0, 15.0 ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Screen Inset -', self ) + self.screenHorizontalInset = settings.IntSpin().getFromValue( 80, 'Screen Horizontal Inset (pixels):', self, 1000, 100 ) + self.screenVerticalInset = settings.IntSpin().getFromValue( 120, 'Screen Vertical Inset (pixels):', self, 1000, 220 ) + settings.LabelSeparator().getFromRepository(self) + self.showGcode = settings.BooleanSetting().getFromValue('Show Gcode', self, True) + + def setToDisplaySave(self, event=None): + 'Set the setting values to the display, save the new values.' + for menuEntity in self.menuEntities: + if menuEntity in self.preferences: + menuEntity.setToDisplay() + settings.writeSettings(self) + + +class TableauWindow: + def activateMouseModeTool(self): + 'Activate the mouse mode tool.' + self.repository.setToDisplaySave() + self.canvas.focus_set() + self.createMouseModeTool() + self.mouseTool.update() + + def addCanvasMenuRootScrollSkein(self, repository, skein, suffix, title): + 'Add the canvas, menu bar, scroll bar, skein panes, tableau repository, root and skein.' + self.imagesDirectoryPath = archive.getFabmetheusUtilitiesPath('images') + self.movementTextID = None + self.mouseInstantButtons = [] + self.photoImages = {} + self.repository = repository + self.root = settings.Tkinter.Tk() + self.gridPosition = settings.GridVertical(0, 1) + self.gridPosition.master = self.root + self.highlightThickness = 3 + self.root.title(os.path.basename(skein.fileName) + ' - ' + title) + self.rulingExtent = 24 + self.rulingTargetSeparation = 150.0 + self.screenSize = skein.screenSize + self.skein = skein + self.skeinPanes = skein.skeinPanes + self.suffix = suffix + self.timerID = None + repository.animationSlideShowRate.value = max(repository.animationSlideShowRate.value, 0.01) + repository.animationSlideShowRate.value = min(repository.animationSlideShowRate.value, 85.0) + repository.drawArrows.setUpdateFunction(self.setWindowToDisplaySaveUpdate) + repository.goAroundExtruderOffTravel.setUpdateFunction(self.setWindowToDisplaySavePhoenixUpdate) + repository.layerExtraSpan.setUpdateFunction(self.setWindowToDisplaySaveUpdate) + repository.showGcode.setUpdateFunction(self.setWindowToDisplaySaveUpdate) + repository.widthOfSelectionThread.setUpdateFunction(self.setWindowToDisplaySaveUpdate) + repository.widthOfTravelThread.setUpdateFunction(self.setWindowToDisplaySaveUpdate) + repository.window = self + for menuRadio in repository.mouseMode.menuRadios: + fileName = menuRadio.name.lower() + fileName = fileName.replace(' ', '_') + '.ppm' + menuRadio.mouseButton = self.getPhotoButtonGridIncrement(menuRadio.invoke, fileName, self.gridPosition) + self.gridPosition = settings.GridHorizontal(2, 99) + self.gridPosition.master = self.root + self.gcodeStringVar = settings.Tkinter.StringVar(self.root) + self.gcodeLabel = settings.Tkinter.Label(self.root, anchor = settings.Tkinter.W, textvariable = self.gcodeStringVar) + self.gcodeLabel.grid(row = 0, column = 5, columnspan = 93, sticky = settings.Tkinter.W) + from fabmetheus_utilities.hidden_scrollbar import HiddenScrollbar + self.xScrollbar = HiddenScrollbar(self.root, orient = settings.Tkinter.HORIZONTAL) + self.xScrollbar.grid(row = 98, column = 2, columnspan = 96, sticky = settings.Tkinter.E + settings.Tkinter.W) + self.yScrollbar = HiddenScrollbar(self.root) + self.yScrollbar.grid(row = 2, rowspan = 96, column = 99, sticky = settings.Tkinter.N + settings.Tkinter.S) + self.canvasHeight = self.root.winfo_screenheight() - repository.screenVerticalInset.value + self.canvasWidth = self.root.winfo_screenwidth() - repository.screenHorizontalInset.value + scrollRegionBoundingBox = (0, 0, int(skein.screenSize.real), int(skein.screenSize.imag)) + self.canvas = settings.Tkinter.Canvas(self.root, highlightthickness = self.highlightThickness, width = self.canvasWidth, height = self.canvasHeight, scrollregion = scrollRegionBoundingBox) + self.canvas.grid(row = 2, rowspan = 96, column = 2, columnspan = 96, sticky = settings.Tkinter.E + settings.Tkinter.W + settings.Tkinter.N + settings.Tkinter.S) + self.fileHelpMenuBar = settings.FileHelpMenuBar(self.root) + self.exportMenu = settings.Tkinter.Menu(self.fileHelpMenuBar.fileMenu, tearoff = 0) + self.fileHelpMenuBar.fileMenu.add_cascade(label = 'Export', menu = self.exportMenu, underline = 0) + exportCanvasPluginFileNames = archive.getPluginFileNamesFromDirectoryPath(getPluginsDirectoryPath()) + for exportCanvasPluginFileName in exportCanvasPluginFileNames: + ExportCanvasDialog().addPluginToMenu(self.canvas, skein.fileName, self.exportMenu, exportCanvasPluginFileName, suffix) + self.fileHelpMenuBar.fileMenu.add_separator() + self.fileHelpMenuBar.completeMenu(self.close, repository, self.save, self) + + def addLayer( self, gridPosition ): + 'Add the layer frame items.' + self.diveButton = self.getPhotoButtonGridIncrement( self.dive, 'dive.ppm', gridPosition ) + self.soarButton = self.getPhotoButtonGridIncrement( self.soar, 'soar.ppm', gridPosition ) + gridPosition.increment() + settings.Tkinter.Label( gridPosition.master, text = 'Layer:').grid( row = gridPosition.row, column = gridPosition.column, sticky = settings.Tkinter.W ) + gridPosition.increment() + self.limitIndex() + self.layerEntry = settings.Tkinter.Spinbox( gridPosition.master, command = self.layerEntryReturnPressed, from_ = 0, increment = 1, to = getLengthMinusOneMinimumOne( self.skeinPanes ) ) + self.layerEntry.bind('', self.layerEntryReturnPressed ) + self.layerEntry.grid( row = gridPosition.row, column = gridPosition.column, sticky = settings.Tkinter.W ) + + def addLine( self, gridPosition ): + 'Add the line frame items.' + self.lineDiveButton = self.getPhotoButtonGridIncrement( self.lineDive, 'dive.ppm', gridPosition ) + self.lineSoarButton = self.getPhotoButtonGridIncrement( self.lineSoar, 'soar.ppm', gridPosition ) + gridPosition.increment() + settings.Tkinter.Label( gridPosition.master, text = 'Line:').grid( row = gridPosition.row, column = gridPosition.column, sticky = settings.Tkinter.W ) + gridPosition.increment() + self.lineEntry = settings.Tkinter.Spinbox( gridPosition.master, command = self.lineEntryReturnPressed, from_ = 0, increment = 1, to = getLengthMinusOneMinimumOne( self.getColoredLines() ) ) + self.lineEntry.bind('', self.lineEntryReturnPressed ) + self.lineEntry.grid( row = gridPosition.row, column = gridPosition.column, sticky = settings.Tkinter.W ) + + def addMouseInstantTool( self, fileName, gridPosition, mouseInstantTool ): + 'Add the mouse instant tool and derived photo button.' + mouseInstantTool.getReset(self) + photoButton = self.getPhotoButtonGridIncrement( mouseInstantTool.click, fileName, gridPosition ) + mouseInstantTool.mouseButton = photoButton + self.mouseInstantButtons.append( photoButton ) + + def addMouseToolsBind(self): + 'Add the mouse tool and bind button one clicked, button one released and motion.' + self.xScrollbar.config( command = self.relayXview ) + self.yScrollbar.config( command = self.relayYview ) + self.canvas['xscrollcommand'] = self.xScrollbar.set + self.canvas['yscrollcommand'] = self.yScrollbar.set + settings.CloseListener( self, self.destroyAllDialogWindows ).listenToWidget( self.canvas ) + self.canvasScreenCenter = 0.5 * complex( float( self.canvasWidth ) / float( self.screenSize.real ), float( self.canvasHeight ) / float( self.screenSize.imag ) ) + self.addPhotoImage('stop.ppm', self.gridPosition ) + self.gridPosition.increment() + self.addLayer( getGridHorizontalFrame( self.gridPosition ) ) + self.gridPosition.increment() + self.addLine( getGridHorizontalFrame( self.gridPosition ) ) + self.gridPosition.increment() + self.addScale( getGridHorizontalFrame( self.gridPosition ) ) + self.gridPosition = settings.GridVertical( self.gridPosition.columnStart + 1, self.gridPosition.row ) + self.gridPosition.master = self.root + for name in self.repository.frameList.value: + entity = self.getEntityFromName( name ) + if entity != None: + self.gridPosition.incrementGivenNumberOfColumns(3) + entity.addToDialog( getGridHorizontalFrame( self.gridPosition ) ) + for menuRadio in self.repository.mouseMode.menuRadios: + menuRadio.mouseTool = menuRadio.getNewMouseToolFunction().getReset(self) + self.mouseTool = menuRadio.mouseTool + self.createMouseModeTool() + self.canvas.bind('', self.button1) + self.canvas.bind('', self.buttonRelease1) + self.canvas.bind('', self.setInsetToCanvas) + self.canvas.bind('', self.keyPressDown) + self.canvas.bind('', self.keyPressLeft) + self.canvas.bind('', self.keyPressRight) + self.canvas.bind('', self.keyPressUp) + self.canvas.bind('', self.motion) + self.canvas.bind('', self.keyPressReturn) + self.canvas.bind('', self.shiftButtonRelease1) + self.canvas.bind('', self.shiftMotion) + self.layerEntry.bind('', self.cancelTimer) + self.root.grid_columnconfigure(44, weight = 1) + self.root.grid_rowconfigure(44, weight = 1) + self.resetPeriodicButtonsText() + self.repository.animationLineQuickening.setUpdateFunction( self.repository.setToDisplaySave ) + self.repository.animationSlideShowRate.setUpdateFunction( self.repository.setToDisplaySave ) + self.repository.screenHorizontalInset.setUpdateFunction( self.redisplayWindowUpdate ) + self.repository.screenVerticalInset.setUpdateFunction( self.redisplayWindowUpdate ) + rankZeroSeperation = self.getRulingSeparationWidthPixels( 0 ) + zoom = self.rulingTargetSeparation / rankZeroSeperation + self.rank = euclidean.getRank( zoom ) + rankTop = self.rank + 1 + seperationBottom = self.getRulingSeparationWidthPixels( self.rank ) + seperationTop = self.getRulingSeparationWidthPixels( rankTop ) + bottomDifference = getGeometricDifference( self.rulingTargetSeparation, seperationBottom ) + topDifference = getGeometricDifference( self.rulingTargetSeparation, seperationTop ) + if topDifference < bottomDifference: + self.rank = rankTop + self.rulingSeparationWidthMillimeters = euclidean.getIncrementFromRank( self.rank ) + self.canvas.focus_set() + + def addPhotoImage( self, fileName, gridPosition ): + 'Get a PhotoImage button, grid the button and increment the grid position.' + photoImage = None + try: + photoImage = settings.Tkinter.PhotoImage( file = os.path.join( self.imagesDirectoryPath, fileName ), master = gridPosition.master ) + except: + print('Image %s was not found in the images directory, so a text button will be substituted.' % fileName ) + untilDotFileName = archive.getUntilDot(fileName) + self.photoImages[ untilDotFileName ] = photoImage + return untilDotFileName + + def addScale( self, gridPosition ): + 'Add the line frame items.' + self.addMouseInstantTool('zoom_out.ppm', gridPosition, zoom_out.getNewMouseTool() ) + self.addMouseInstantTool('zoom_in.ppm', gridPosition, zoom_in.getNewMouseTool() ) + gridPosition.increment() + settings.Tkinter.Label( gridPosition.master, text = 'Scale:').grid( row = gridPosition.row, column = gridPosition.column, sticky = settings.Tkinter.W ) + gridPosition.increment() + self.scaleEntry = settings.Tkinter.Spinbox( gridPosition.master, command = self.scaleEntryReturnPressed, from_ = 10.0, increment = 5.0, to = 100.0 ) + self.scaleEntry.bind('', self.scaleEntryReturnPressed ) + self.scaleEntry.grid( row = gridPosition.row, column = gridPosition.column, sticky = settings.Tkinter.W ) + + def addSettingsMenuSetWindowGeometry( self, center ): + 'Add the settings menu, center the scroll region, update, and set the window geometry.' + self.settingsMenu = settings.Tkinter.Menu( self.fileHelpMenuBar.menuBar, tearoff = 0 ) + self.fileHelpMenuBar.addMenuToMenuBar( 'Settings', self.settingsMenu ) + settings.addMenuEntitiesToMenuFrameable( self.settingsMenu, self.repository.menuEntities ) + self.relayXview( settings.Tkinter.MOVETO, center.real - self.canvasScreenCenter.real ) + self.relayYview( settings.Tkinter.MOVETO, center.imag - self.canvasScreenCenter.imag ) + self.root.withdraw() + self.root.update_idletasks() + movedGeometryString = '%sx%s+%s' % ( self.root.winfo_reqwidth(), self.root.winfo_reqheight(), '0+0') + self.root.geometry( movedGeometryString ) + + def button1(self, event): + 'The button was clicked.' + self.mouseTool.button1(event) + + def buttonRelease1(self, event): + 'The button was released.' + self.mouseTool.buttonRelease1(event) + + def cancel(self, event=None): + 'Set all entities to their saved state.' + settings.cancelRepository(self.repository) + + def cancelTimer(self, event=None): + 'Cancel the timer and set it to none.' + if self.timerID != None: + self.canvas.after_cancel(self.timerID) + self.timerID = None + + def cancelTimerResetButtons(self): + 'Cancel the timer and set it to none.' + self.cancelTimer() + self.resetPeriodicButtonsText() + + def close(self, event=None): + 'The dialog was closed.' + try: + self.root.after( 1, self.root.destroy ) # to get around 'Font Helvetica -12 still in cache.' segmentation bug, instead of simply calling self.root.destroy() + except: + pass + + def createMouseModeTool(self): + 'Create the mouse mode tool.' + self.destroyMouseToolRaiseMouseButtons() + for menuRadio in self.repository.mouseMode.menuRadios: + if menuRadio.value: + self.mouseTool = menuRadio.mouseTool + menuRadio.mouseButton['relief'] = settings.Tkinter.SUNKEN + + def destroyAllDialogWindows(self): + 'Destroy all the dialog windows.' + settings.writeSettings(self.repository) + return + for menuEntity in self.repository.menuEntities: + lowerName = menuEntity.name.lower() + if lowerName in settings.globalRepositoryDialogListTable: + globalRepositoryDialogValues = settings.globalRepositoryDialogListTable[ lowerName ] + for globalRepositoryDialogValue in globalRepositoryDialogValues: + settings.quitWindow( globalRepositoryDialogValue.root ) + + def destroyMouseToolRaiseMouseButtons(self): + 'Destroy the mouse tool and raise the mouse buttons.' + self.mouseTool.destroyEverything() + for menuRadio in self.repository.mouseMode.menuRadios: + menuRadio.mouseButton['relief'] = settings.Tkinter.RAISED + for mouseInstantButton in self.mouseInstantButtons: + mouseInstantButton['relief'] = settings.Tkinter.RAISED + + def dive(self): + 'Dive, go down periodically.' + oldDiveButtonText = self.diveButton['text'] + self.cancelTimerResetButtons() + if oldDiveButtonText == 'stop': + return + self.diveCycle() + + def diveCycle(self): + 'Start the dive cycle.' + self.setLayerIndex(self.repository.layer.value - 1) + if self.repository.layer.value < 1: + self.resetPeriodicButtonsText() + return + self.setButtonImageText( self.diveButton, 'stop') + self.timerID = self.canvas.after( self.getSlideShowDelay(), self.diveCycle ) + + def getAnimationLineDelay( self, coloredLine ): + 'Get the animation line delay in milliseconds.' +# maybe later, add animation along line +# nextLayerIndex = self.repository.layer.value +# nextLineIndex = self.repository.line.value + 1 +# coloredLinesLength = len( self.getColoredLines() ) +# self.skein.feedRateMinute +# if nextLineIndex >= coloredLinesLength: +# if nextLayerIndex + 1 < len( self.skeinPanes ): +# nextLayerIndex += 1 +# nextLineIndex = 0 +# else: +# nextLineIndex = self.repository.line.value + splitLine = gcodec.getSplitLineBeforeBracketSemicolon( coloredLine.displayString ) + self.skein.feedRateMinute = gcodec.getFeedRateMinute( self.skein.feedRateMinute, splitLine ) + feedRateSecond = self.skein.feedRateMinute / 60.0 + coloredLineLength = abs( coloredLine.end - coloredLine.begin ) / self.repository.scale.value + duration = coloredLineLength / feedRateSecond + animationLineDelay = int( round( 1000.0 * duration / self.repository.animationLineQuickening.value ) ) + return max( animationLineDelay, 1 ) + + def getDrawnLineText( self, location, tags, text ): + 'Get the line text drawn on the canvas.' + if not self.repository.showGcode.value: + return + anchorTowardCenter = settings.Tkinter.N + if location.imag > float( self.canvasHeight ) * 0.1: + anchorTowardCenter = settings.Tkinter.S + if location.real > float( self.canvasWidth ) * 0.7: + anchorTowardCenter += settings.Tkinter.E + else: + anchorTowardCenter += settings.Tkinter.W + return self.canvas.create_text( int( location.real ), int( location.imag ), anchor = anchorTowardCenter, tags = tags, text = text ) + + def getEntityFromName(self, name): + 'Get the entity of the given name.' + for entity in self.repository.displayEntities: + if entity.name == name: + return entity + return None + + def getPhotoButtonGridIncrement( self, commandFunction, fileName, gridPosition ): + 'Get a PhotoImage button, grid the button and increment the grid position.' + gridPosition.increment() + untilDotFileName = self.addPhotoImage( fileName, gridPosition ) + photoImage = self.photoImages[ untilDotFileName ] + photoButton = settings.Tkinter.Button( gridPosition.master, activebackground = 'black', activeforeground = 'white', command = commandFunction, text = untilDotFileName ) + if photoImage != None: + photoButton['image'] = photoImage + photoButton.grid( row = gridPosition.row, column = gridPosition.column, sticky = settings.Tkinter.W ) + return photoButton + + def getRoundedRulingText( self, extraDecimalPlaces, number ): + 'Get the rounded ruling text.' + rulingText = euclidean.getRoundedToPlacesString( extraDecimalPlaces - math.floor( math.log10( self.rulingSeparationWidthMillimeters ) ), number ) + if self.rulingSeparationWidthMillimeters < .99: + return rulingText + if rulingText[ - len('.0') : ] == '.0': + return rulingText[ : - len('.0') ] + return rulingText + + def getRulingSeparationWidthPixels( self, rank ): + 'Get the separation width in pixels.' + return euclidean.getIncrementFromRank( rank ) * self.skein.scale + + def getScrollPaneCenter(self): + 'Get the center of the scroll pane.' + return self.getScrollPaneFraction() + self.canvasScreenCenter + + def getScrollPaneFraction(self): + 'Get the scroll pane top left.' + return complex( self.xScrollbar.get()[0], self.yScrollbar.get()[0] ) + + def getSlideShowDelay(self): + 'Get the slide show delay in milliseconds.' + slideShowDelay = int( round( 1000.0 / self.repository.animationSlideShowRate.value ) ) + return max( slideShowDelay, 1 ) + + def getUpdateSkeinPanes(self): + 'Get the update skein panes.' + layerPlusExtraSpan = self.repository.layer.value + self.repository.layerExtraSpan.value + layersFrom = max( 0, min( self.repository.layer.value, layerPlusExtraSpan ) ) + layersTo = min( len( self.skeinPanes ), max( self.repository.layer.value, layerPlusExtraSpan ) + 1 ) + return self.skeinPanes[ layersFrom : layersTo ] + + def isLineBelowZeroSetLayer(self): + 'Determine if the line index is below zero, and if so set the layer index.' + if self.repository.line.value >= 0: + return False + self.repository.line.value = 0 + if self.repository.layer.value > 0: + self.setLayerIndex( self.repository.layer.value - 1 ) + return True + return False + + def isLineBeyondListSetLayer(self): + 'Determine if the line index is beyond the end of the list, and if so set the layer index.' + coloredLinesLength = len( self.getColoredLines() ) + if self.repository.line.value < coloredLinesLength: + return False + self.repository.line.value = coloredLinesLength - 1 + if self.repository.layer.value < len( self.skeinPanes ) - 1: + self.setLayerIndex( self.repository.layer.value + 1 ) + return True + return False + + def keyPressDown(self, event): + 'The down arrow was pressed.' + self.mouseTool.keyPressDown(event) + + def keyPressLeft(self, event): + 'The left arrow was pressed.' + self.mouseTool.keyPressLeft(event) + + def keyPressReturn(self, event): + 'The return key was pressed.' + self.mouseTool.keyPressReturn(event) + + def keyPressRight(self, event): + 'The right arrow was pressed.' + self.mouseTool.keyPressRight(event) + + def keyPressUp(self, event): + 'The up arrow was pressed.' + self.mouseTool.keyPressUp(event) + + def layerEntryReturnPressed(self, event=None): + 'The layer index entry return was pressed.' + self.setLayerIndex( int( self.layerEntry.get() ) ) + + def limitIndex(self): + 'Limit the index so it is not below zero or above the top.' + self.repository.layer.value = max( 0, self.repository.layer.value ) + self.repository.layer.value = min( len( self.skeinPanes ) - 1, self.repository.layer.value ) + + def limitIndexSetArrowMouseDeleteCanvas(self): + 'Limit the index, set the arrow type, and delete all the canvas items.' + self.limitIndex() + self.arrowType = None + if self.repository.drawArrows.value: + self.arrowType = 'last' + self.canvas.delete( settings.Tkinter.ALL ) + + def lineDive(self): + 'Line dive, go down periodically.' + oldLineDiveButtonText = self.lineDiveButton['text'] + self.cancelTimerResetButtons() + if oldLineDiveButtonText == 'stop': + return + self.lineDiveCycle() + + def lineDiveCycle(self): + 'Start the line dive cycle.' + self.cancelTimer() + self.repository.line.value -= 1 + if self.repository.line.value < 0: + self.repository.line.value = 0 + if self.repository.layer.value == 0: + self.resetPeriodicButtonsText() + self.setLineButtonsState() + return + self.setLayerIndex( self.repository.layer.value - 1 ) + else: + self.updateMouseToolIfSelection() + self.setLineButtonsState() + self.setButtonImageText( self.lineDiveButton, 'stop') + coloredLine = self.getColoredLines()[ self.repository.line.value ] + self.timerID = self.canvas.after( self.getAnimationLineDelay( coloredLine ), self.lineDiveCycle ) + + def lineEntryReturnPressed(self, event=None): + 'The line index entry return was pressed.' + self.repository.line.value = int( self.lineEntry.get() ) + if self.isLineBelowZeroSetLayer(): + return + if self.isLineBeyondListSetLayer(): + return + self.cancelTimerResetButtons() + self.updateMouseToolIfSelection() + self.setLineButtonsState() + + def lineSoar(self): + 'Line soar, go up periodically.' + oldLineSoarButtonText = self.lineSoarButton['text'] + self.cancelTimerResetButtons() + if oldLineSoarButtonText == 'stop': + return + self.lineSoarCycle() + + def lineSoarCycle(self): + 'Start the line soar cycle.' + self.cancelTimer() + self.repository.line.value += 1 + coloredLinesLength = len( self.getColoredLines() ) + if self.repository.line.value >= coloredLinesLength: + self.repository.line.value = coloredLinesLength - 1 + if self.repository.layer.value > len( self.skeinPanes ) - 2: + self.resetPeriodicButtonsText() + self.setLineButtonsState() + return + self.setLayerIndex( self.repository.layer.value + 1 ) + else: + self.updateMouseToolIfSelection() + self.setLineButtonsState() + self.setButtonImageText( self.lineSoarButton, 'stop') + coloredLine = self.getColoredLines()[ self.repository.line.value ] + self.timerID = self.canvas.after( self.getAnimationLineDelay( coloredLine ), self.lineSoarCycle ) + + def motion(self, event): + 'The mouse moved.' + self.mouseTool.motion(event) + + def phoenixUpdate(self): + 'Update the skein, and deiconify a new window and destroy the old.' + self.updateNewDestroyOld( self.getScrollPaneCenter() ) + + def redisplayWindowUpdate(self, event=None): + 'Deiconify a new window and destroy the old.' + self.repository.setToDisplaySave() + self.getCopy().updateDeiconify( self.getScrollPaneCenter() ) + self.root.after( 1, self.root.destroy ) # to get around 'Font Helvetica -12 still in cache.' segmentation bug, instead of simply calling self.root.destroy() + + def relayXview( self, *args ): + 'Relay xview changes.' + self.canvas.xview( *args ) + + def relayYview( self, *args ): + 'Relay yview changes.' + self.canvas.yview( *args ) + + def resetPeriodicButtonsText(self): + 'Reset the text of the periodic buttons.' + self.setButtonImageText( self.diveButton, 'dive') + self.setButtonImageText( self.soarButton, 'soar') + self.setButtonImageText( self.lineDiveButton, 'dive') + self.setButtonImageText( self.lineSoarButton, 'soar') + + def save(self): + 'Set the setting values to the display, save the new values.' + for menuEntity in self.repository.menuEntities: + if menuEntity in self.repository.preferences: + menuEntity.setToDisplay() + settings.writeSettings(self.repository) + + def scaleEntryReturnPressed(self, event=None): + 'The scale entry return was pressed.' + self.repository.scale.value = float( self.scaleEntry.get() ) + self.phoenixUpdate() + + def setButtonImageText( self, button, text ): + 'Set the text of the e periodic buttons.' + photoImage = self.photoImages[ text ] + if photoImage != None: + button['image'] = photoImage + button['text'] = text + + def setDisplayLayerIndex(self): + 'Set the display of the layer index entry field and buttons.' + coloredLines = self.getColoredLines() + isAboveFloor = self.repository.layer.value > 0 + isBelowCeiling = self.repository.layer.value < len( self.skeinPanes ) - 1 + setStateNormalDisabled( isAboveFloor, self.diveButton ) + setStateNormalDisabled( isBelowCeiling, self.soarButton ) + self.setLineButtonsState() + settings.setEntryText( self.layerEntry, self.repository.layer.value ) + settings.setEntryText( self.lineEntry, self.repository.line.value ) + settings.setEntryText( self.scaleEntry, self.repository.scale.value ) + self.mouseTool.update() + + def setInsetToCanvas(self, event=None): + 'Set the repository insets to the canvas.' + if self.root.state() != 'normal': + return + excessExtent = self.highlightThickness + self.highlightThickness + screenHorizontalInset = self.repository.screenHorizontalInset + screenVerticalInset = self.repository.screenVerticalInset + oldHorizontalValue = screenHorizontalInset.value + oldVerticalValue = screenVerticalInset.value + screenHorizontalInset.value = self.root.winfo_screenwidth() - self.canvas.winfo_width() + excessExtent + if not self.yScrollbar.visible: + screenHorizontalInset.value += self.yScrollbar.winfo_reqwidth() + screenHorizontalInset.setStateToValue() + screenVerticalInset.value = self.root.winfo_screenheight() - self.canvas.winfo_height() + excessExtent + if not self.xScrollbar.visible: + screenVerticalInset.value += self.xScrollbar.winfo_reqheight() + screenVerticalInset.setStateToValue() + if oldHorizontalValue != screenHorizontalInset.value or oldVerticalValue != screenVerticalInset.value: + self.repository.setToDisplaySave() + + def setLayerIndex( self, layerIndex ): + 'Set the layer index.' + self.cancelTimerResetButtons() + oldLayerIndex = self.repository.layer.value + self.repository.layer.value = layerIndex + self.limitIndex() + coloredLines = self.getColoredLines() + if self.repository.layer.value < oldLayerIndex: + self.repository.line.value = len( coloredLines ) - 1 + self.lineEntry['to'] = getLengthMinusOneMinimumOne( coloredLines ) + if self.repository.layer.value > oldLayerIndex: + self.repository.line.value = 0 + self.lineEntry['to'] = getLengthMinusOneMinimumOne( coloredLines ) + self.update() + + def setLineButtonsState(self): + 'Set the state of the line buttons.' + coloredLines = self.getColoredLines() + if len(coloredLines) < 1: + print('Warning, there are no coloredLines in setLineButtonsState in tableau for the layer:') + print(self.repository.layer.value) + return + isAboveFloor = self.repository.layer.value > 0 + isBelowCeiling = self.repository.layer.value < len( self.skeinPanes ) - 1 + setStateNormalDisabled( isAboveFloor or self.repository.line.value > 0, self.lineDiveButton ) + setStateNormalDisabled( isBelowCeiling or self.repository.line.value < len( coloredLines ) - 1, self.lineSoarButton ) + self.repository.line.value = max(self.repository.line.value, 0) + self.repository.line.value = min(self.repository.line.value, len(coloredLines) - 1) + gcodeString = '' + if self.repository.showGcode.value: + gcodeString = 'Gcode: ' + coloredLines[self.repository.line.value].displayString + self.gcodeStringVar.set(gcodeString) + self.canvas.delete('selection_line') + self.getDrawnSelectedColoredLine(coloredLines[self.repository.line.value]) + settings.setEntryText(self.lineEntry, self.repository.line.value) + + def setWindowNewMouseTool( self, getNewMouseToolFunction, mouseTool ): + 'Set the getNewMouseTool function and the update function.' + mouseTool.getNewMouseToolFunction = getNewMouseToolFunction + mouseTool.setUpdateFunction( self.activateMouseModeTool ) + + def setWindowToDisplaySavePhoenixUpdate(self, event=None): + 'Set the setting values to the display, save the new values, then call the update function.' + self.repository.setToDisplaySave() + self.phoenixUpdate() + + def setWindowToDisplaySaveUpdate(self, event=None): + 'Set the setting values to the display, save the new values, then call the update function.' + self.repository.setToDisplaySave() + self.update() + + def shiftButtonRelease1(self, event): + 'The button was released while the shift key was pressed.' + self.mouseTool.buttonRelease1( event, True ) + + def shiftMotion(self, event): + 'The mouse moved.' + self.mouseTool.motion( event, True ) + + def soar(self): + 'Soar, go up periodically.' + oldSoarButtonText = self.soarButton['text'] + self.cancelTimerResetButtons() + if oldSoarButtonText == 'stop': + return + self.soarCycle() + + def soarCycle(self): + 'Start the soar cycle.' + self.setLayerIndex(self.repository.layer.value + 1) + if self.repository.layer.value > len( self.skeinPanes ) - 2: + self.resetPeriodicButtonsText() + return + self.setButtonImageText( self.soarButton, 'stop') + self.timerID = self.canvas.after( self.getSlideShowDelay(), self.soarCycle ) + + def updateDeiconify( self, center = complex( 0.5, 0.5 ) ): + 'Update and deiconify the window.' + self.addSettingsMenuSetWindowGeometry( center ) + self.update() + self.root.deiconify() + + def updateMouseToolIfSelection(self): + 'Update the mouse tool if it is a selection tool.' + if self.mouseTool == None: + return + if self.mouseTool.isSelectionTool(): + self.mouseTool.update() + + def updateNewDestroyOld( self, scrollPaneCenter ): + 'Update and deiconify a window and destroy the old.' + self.getCopyWithNewSkein().updateDeiconify( scrollPaneCenter ) + self.root.after(1, self.root.destroy) # to get around 'Font Helvetica -12 still in cache.' segmentation bug, instead of simply calling self.root.destroy() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/view_move.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/view_move.py new file mode 100644 index 0000000..ca68350 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/view_move.py @@ -0,0 +1,83 @@ +""" +This page is in the table of contents. +Viewpoint move is a mouse tool to move the viewpoint in the xy plane. + +When the mouse is clicked and dragged on the canvas, viewpoint move will drag the scroll pane accordingly. If the shift key is also pressed, the scroll pane will be moved only in the x or y direction, whichever is largest. + +When the viewpoint move tool is chosen and the canvas has the focus, viewpoint move will listen to the arrow keys. Clicking in the canvas gives the canvas the focus, and when the canvas has the focus a thick black border is drawn around the canvas. When the right arrow key is pressed, viewpoint move will move the scroll pane to the right by a pixel. When the left arrow key is pressed, the scroll pane will be moved a pixel to the left. The up arrow key moves the scroll pane a pixel up and the down arow key moves the scroll pane a pixel down. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base import MouseToolBase +from fabmetheus_utilities import settings + +__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 getNewMouseTool(): + "Get a new mouse tool." + return ViewpointMove() + + +class ViewpointMove( MouseToolBase ): + "Display the line when it is clicked." + def button1( self, event, shift = False ): + "Print line text and connection line." + self.destroyEverythingGetFocus() + self.buttonOnePressedScreenCoordinate = complex( event.x, event.y ) + self.scrollPaneFraction = self.window.getScrollPaneFraction() + + def buttonRelease1( self, event, shift = False ): + "The left button was released, function." + self.destroyEverything() + + def destroyEverything(self): + "Destroy items." + self.buttonOnePressedScreenCoordinate = None + + def keyPressDown(self, event): + "The down arrow was pressed." + self.setScrollPaneMove( complex( 0.0, 1.0 ) ) + + def keyPressLeft(self, event): + "The left arrow was pressed." + self.setScrollPaneMove( complex( - 1.0, 0.0 ) ) + + def keyPressRight(self, event): + "The right arrow was pressed." + self.setScrollPaneMove( complex( 1.0, 0.0 ) ) + + def keyPressUp(self, event): + "The up arrow was pressed." + self.setScrollPaneMove( complex(0.0, -1.0) ) + + def motion( self, event, shift = False ): + "The mouse moved, function." + if self.buttonOnePressedScreenCoordinate == None: + return + motionCoordinate = complex( event.x, event.y ) + relativeMotion = motionCoordinate - self.buttonOnePressedScreenCoordinate + if shift: + if abs( relativeMotion.real ) > abs( relativeMotion.imag ): + relativeMotion = complex( relativeMotion.real, 0.0 ) + else: + relativeMotion = complex( 0.0, relativeMotion.imag ) + self.relativeMove( relativeMotion ) + + def relativeMove( self, relativeMotion ): + "Move the view given the relative motion." + relativeScreenMotion = complex( relativeMotion.real / float( self.window.screenSize.real ), relativeMotion.imag / float( self.window.screenSize.imag ) ) + moveTo = self.scrollPaneFraction - relativeScreenMotion + self.window.relayXview( settings.Tkinter.MOVETO, moveTo.real ) + self.window.relayYview( settings.Tkinter.MOVETO, moveTo.imag ) + + def setScrollPaneMove( self, relativeMotion ): + "The up arrow was pressed." + self.scrollPaneFraction = self.window.getScrollPaneFraction() + self.relativeMove( relativeMotion ) diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/view_rotate.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/view_rotate.py new file mode 100644 index 0000000..984bb0d --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/view_rotate.py @@ -0,0 +1,201 @@ +""" +This page is in the table of contents. +Viewpoint rotate is a mouse tool to rotate the viewpoint around the origin. + +When the mouse is clicked, dragged and released on the canvas, viewpoint rotate will rotate the longitude by the amount the mouse is dragged around the origin. If the mouse is moved towards the origin, the latitude will be increased, so the viewpoint will be closer to the top. If the mouse is moved away from the origin, the latitude will be decreased. If the shift key is also pressed, only the latitude or longitude will be changed, whichever is being changed the most. + +When the viewpoint rotate tool is chosen and the canvas has the focus, viewpoint rotate will listen to the arrow keys. Clicking in the canvas gives the canvas the focus, and when the canvas has the focus a thick black border is drawn around the canvas. When the right arrow key is pressed, viewpoint rotate will increase the preview longitude by one degree. When the left arrow key is pressed, the preview longitude will be decreased. The up arrow key increase the preview latitude by one degree and the down arow decreases the preview latitude. Pressing the key implements the preview. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base import MouseToolBase +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import settings +import math + +__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 getBoundedLatitude( latitude ): + "Get the bounded latitude.later get rounded" + return round( min( 179.9, max( 0.1, latitude ) ), 1 ) + +def getNewMouseTool(): + "Get a new mouse tool." + return ViewpointRotate() + + +class LatitudeLongitude: + "A latitude and longitude." + def __init__( self, buttonOnePressedCanvasCoordinate, newCoordinate, skeinWindow, shift ): + "Set the latitude and longitude." + buttonOnePressedCentered = skeinWindow.getCenteredScreened( buttonOnePressedCanvasCoordinate ) + buttonOnePressedRadius = abs( buttonOnePressedCentered ) + buttonOnePressedComplexMirror = complex( buttonOnePressedCentered.real, - buttonOnePressedCentered.imag ) + buttonOneReleasedCentered = skeinWindow.getCenteredScreened( newCoordinate ) + buttonOneReleasedRadius = abs( buttonOneReleasedCentered ) + pressedReleasedRotationComplex = buttonOneReleasedCentered * buttonOnePressedComplexMirror + self.deltaLatitude = math.degrees( buttonOneReleasedRadius - buttonOnePressedRadius ) + self.originalDeltaLongitude = math.degrees( math.atan2( pressedReleasedRotationComplex.imag, pressedReleasedRotationComplex.real ) ) + self.deltaLongitude = self.originalDeltaLongitude + if skeinWindow.repository.viewpointLatitude.value > 90.0: + self.deltaLongitude = - self.deltaLongitude + if shift: + if abs( self.deltaLatitude ) > abs( self.deltaLongitude ): + self.deltaLongitude = 0.0 + else: + self.deltaLatitude = 0.0 + self.latitude = getBoundedLatitude( skeinWindow.repository.viewpointLatitude.value + self.deltaLatitude ) + self.longitude = round( ( skeinWindow.repository.viewpointLongitude.value + self.deltaLongitude ) % 360.0, 1 ) + + +class ViewpointRotate( MouseToolBase ): + "Display the line when it is clicked." + def button1( self, event, shift = False ): + "Print line text and connection line." + self.destroyEverything() + x = self.canvas.canvasx(event.x) + y = self.canvas.canvasy(event.y) + self.buttonOnePressedCanvasCoordinate = complex(x, y) + + def buttonRelease1( self, event, shift = False ): + "The left button was released, function." + if self.buttonOnePressedCanvasCoordinate == None: + return + x = self.canvas.canvasx(event.x) + y = self.canvas.canvasy(event.y) + buttonOneReleasedCanvasCoordinate = complex(x, y) + self.moveViewpointGivenCoordinates( buttonOneReleasedCanvasCoordinate, shift, self.buttonOnePressedCanvasCoordinate ) + + def destroyEverything(self): + "Destroy items." + self.buttonOnePressedCanvasCoordinate = None + self.keyStartCanvasCoordinate = None + self.relativeLatitude = 0.0 + self.relativeLongitude = 0.5 * math.pi + self.canvas.delete('mouse_item') + + def getMoveCoordinate(self): + "Get the movement coordinate from the class relative latitude and longitude." + motionRadius = ( 0.75 + self.relativeLatitude ) * self.window.getCanvasRadius() + return self.window.getScreenComplex( motionRadius * euclidean.getWiddershinsUnitPolar( self.relativeLongitude ) ) + + def keyPressDown(self, event): + "The down arrow was pressed." + self.keyPressStart() + self.relativeLatitude -= math.radians(1.0) + self.keyPressMotion() + + def keyPressLeft(self, event): + "The left arrow was pressed." + self.keyPressStart() + self.relativeLongitude += math.radians(1.0) + self.keyPressMotion() + + def keyPressMotion(self): + "Move the motion viewpoint for the class key press coordinates." + self.motionGivenCoordinates( self.getMoveCoordinate(), False, self.keyStartCanvasCoordinate ) + + def keyPressReturn(self, event): + "The return key was pressed." + if self.keyStartCanvasCoordinate == None: + return + self.moveViewpointGivenCoordinates( self.getMoveCoordinate(), False, self.keyStartCanvasCoordinate ) + + def keyPressRight(self, event): + "The right arrow was pressed." + self.keyPressStart() + self.relativeLongitude -= math.radians(1.0) + self.keyPressMotion() + + def keyPressStart(self): + "If necessary, destroy everything and calculate the keyStartCanvasCoordinate." + if self.keyStartCanvasCoordinate == None: + self.destroyEverything() + self.keyStartCanvasCoordinate = self.window.getScreenComplex( complex( 0.0, 0.75 * self.window.getCanvasRadius() ) ) + + def keyPressUp(self, event): + "The up arrow was pressed." + self.keyPressStart() + self.relativeLatitude += math.radians(1.0) + self.keyPressMotion() + + def motion( self, event, shift = False ): + "Move the motion viewpoint if the mouse was moved." + if self.buttonOnePressedCanvasCoordinate == None: + return + x = self.canvas.canvasx(event.x) + y = self.canvas.canvasy(event.y) + motionCoordinate = complex(x, y) + self.motionGivenCoordinates( motionCoordinate, shift, self.buttonOnePressedCanvasCoordinate ) + + def motionGivenCoordinates( self, motionCoordinate, shift, startCoordinate ): + "Move the motion viewpoint given the motion coordinates." + latitudeLongitude = LatitudeLongitude( startCoordinate, motionCoordinate, self.window, shift ) + viewVectors = euclidean.ProjectiveSpace().getByLatitudeLongitude( latitudeLongitude.latitude, latitudeLongitude.longitude ) + motionCentered = self.window.getCentered( motionCoordinate ) + motionCenteredNormalized = motionCentered / abs( motionCentered ) + buttonOnePressedCentered = self.window.getCentered( startCoordinate ) + buttonOnePressedAngle = math.degrees( math.atan2( buttonOnePressedCentered.imag, buttonOnePressedCentered.real ) ) + buttonOnePressedLength = abs( buttonOnePressedCentered ) + buttonOnePressedCorner = complex( buttonOnePressedLength, buttonOnePressedLength ) + buttonOnePressedCornerBottomLeft = self.window.getScreenComplex( - buttonOnePressedCorner ) + buttonOnePressedCornerUpperRight = self.window.getScreenComplex( buttonOnePressedCorner ) + motionPressedStart = buttonOnePressedLength * motionCenteredNormalized + motionPressedScreen = self.window.getScreenComplex( motionPressedStart ) + motionColorName = '#4B0082' + motionWidth = 9 + self.canvas.delete('mouse_item') + if abs( latitudeLongitude.deltaLongitude ) > 0.0: + self.canvas.create_arc( + buttonOnePressedCornerBottomLeft.real, + buttonOnePressedCornerBottomLeft.imag, + buttonOnePressedCornerUpperRight.real, + buttonOnePressedCornerUpperRight.imag, + extent = latitudeLongitude.originalDeltaLongitude, + start = buttonOnePressedAngle, + outline = motionColorName, + outlinestipple = self.window.motionStippleName, + style = settings.Tkinter.ARC, + tags = 'mouse_item', + width = motionWidth ) + if abs( latitudeLongitude.deltaLatitude ) > 0.0: + self.canvas.create_line( + motionPressedScreen.real, + motionPressedScreen.imag, + motionCoordinate.real, + motionCoordinate.imag, + fill = motionColorName, + arrow = 'last', + arrowshape = self.window.arrowshape, + stipple = self.window.motionStippleName, + tags = 'mouse_item', + width = motionWidth ) + self.window.getDrawnLineText( motionCoordinate, 'mouse_item', 'Latitude: %s, Longitude: %s' % ( round( latitudeLongitude.latitude ), round( latitudeLongitude.longitude ) ) ) + if self.repository.widthOfAxisPositiveSide.value > 0: + self.window.getDrawnColoredLineMotion( self.window.positiveAxisLineX, viewVectors, self.repository.widthOfAxisPositiveSide.value ) + self.window.getDrawnColoredLineMotion( self.window.positiveAxisLineY, viewVectors, self.repository.widthOfAxisPositiveSide.value ) + self.window.getDrawnColoredLineMotion( self.window.positiveAxisLineZ, viewVectors, self.repository.widthOfAxisPositiveSide.value ) + + def moveViewpointGivenCoordinates( self, moveCoordinate, shift, startCoordinate ): + "Move the viewpoint given the move coordinates." + if abs( startCoordinate - moveCoordinate ) < 3: + startCoordinate = None + self.canvas.delete('mouse_item') + return + latitudeLongitude = LatitudeLongitude( startCoordinate, moveCoordinate, self.window, shift ) + self.repository.viewpointLatitude.value = latitudeLongitude.latitude + self.repository.viewpointLatitude.setStateToValue() + self.repository.viewpointLongitude.value = latitudeLongitude.longitude + self.repository.viewpointLongitude.setStateToValue() + startCoordinate = None + settings.writeSettings(self.repository) + self.window.update() + self.destroyEverything() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/zoom_in.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/zoom_in.py new file mode 100644 index 0000000..2d0e344 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/zoom_in.py @@ -0,0 +1,50 @@ +""" +This page is in the table of contents. +Zoom in is a mouse tool to zoom in the display at the point where the mouse was clicked, increasing the scale by a factor of two. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities.mouse_tool_base import MouseToolBase +from fabmetheus_utilities import settings + + +__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 getNewMouseTool(): + "Get a new mouse tool." + return ZoomIn() + + +class ZoomIn( MouseToolBase ): + "The zoom in mouse tool." + def button1( self, event, shift = False ): + "Print line text and connection line." + scaleSetting = self.window.repository.scale + scaleSetting.value *= self.getMultiplier() + delta = complex( float(event.x) / float( self.window.screenSize.real ), float(event.y) / float( self.window.screenSize.imag ) ) - self.window.canvasScreenCenter + delta *= 1.0 - 1.0 / self.getMultiplier() + scrollPaneCenter = self.window.getScrollPaneCenter() + delta + self.window.updateNewDestroyOld( scrollPaneCenter ) + + def click(self, event=None): + "Set the window mouse tool to this." + self.window.destroyMouseToolRaiseMouseButtons() + self.window.mouseTool = self + self.mouseButton['relief'] = settings.Tkinter.SUNKEN + + def getMultiplier(self): + "Get the scale multiplier." + return 2.0 + + def getReset( self, window ): + "Reset the mouse tool to default." + self.setWindowItems( window ) + self.mouseButton = None + return self diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/zoom_out.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/zoom_out.py new file mode 100644 index 0000000..b104807 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/analyze_utilities/zoom_out.py @@ -0,0 +1,29 @@ +""" +This page is in the table of contents. +Zoom out is a mouse tool to zoom out the display at the point where the mouse was clicked, decreasing the scale by a factor of two. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities import zoom_in +from fabmetheus_utilities import settings + + +__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 getNewMouseTool(): + "Get a new mouse tool." + return ZoomOut() + + +class ZoomOut( zoom_in.ZoomIn ): + "The zoom out mouse tool." + def getMultiplier(self): + "Get the scale multiplier." + return 0.5 diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/clairvoyance.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/clairvoyance.py new file mode 100644 index 0000000..55698f5 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/clairvoyance.py @@ -0,0 +1,118 @@ +""" +This page is in the table of contents. +Clairvoyance is an analyze plugin to open the gcode file with an outside program. + +The clairvoyance manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Clairvoyance + +==Operation== +The default 'Activate Clairvoyance' checkbox is off. When it is on, the functions described below will work when called from the skeinforge toolchain, when it is off, the functions will not be called from the toolchain. The functions will still be called, whether or not the 'Activate Clairvoyance' checkbox is on, when clairvoyance is run directly. + +==Settings== +===Gcode Program=== +Default is webbrowser. + +If the 'Gcode Program' is set to webbrowser, the gcode file will be sent to the default browser to be opened. If the 'Gcode Program' is set to a program name, the gcode file will be sent to that program to be opened. A good gcode viewer is Pleasant3D, at: +http://www.pleasantsoftware.com/developer/pleasant3d/index.shtml + +==Examples== +Below are examples of clairvoyance being used. These examples are run in a terminal in the folder which contains Screw Holder_penultimate.gcode and clairvoyance.py. + +> python clairvoyance.py +This brings up the clairvoyance dialog. + +> python clairvoyance.py Screw Holder_penultimate.gcode +The file is opened by an outside program + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import subprocess +import sys +import traceback + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getNewRepository(): + 'Get new repository.' + return ClairvoyanceRepository() + +def getWindowAnalyzeFile(fileName, repository=None): + 'Open penultimate file with outside program.' + print('') + if repository == None: + repository = settings.getReadRepository(ClairvoyanceRepository()) + gcodeProgram = repository.gcodeProgram.value + if gcodeProgram == '': + print('Warning, nothing will be done in getWindowAnalyzeFile in clairvoyance because the Gcode Program field is empty.') + print('') + return + if gcodeProgram == 'webbrowser': + print('Clairvoyance will use a web browser to open the file:') + print(archive.getSummarizedFileName(fileName)) + settings.openWebPage(fileName) + return + try: + subprocess.Popen([gcodeProgram, fileName]) + print('Clairvoyance has opened the file:') + print(archive.getSummarizedFileName(fileName)) + print('with the gcode program:') + print(gcodeProgram) + except: + print('Warning, getWindowAnalyzeFile in clairvoyance could not open the file:') + print(fileName) + print('with the gcode program:') + print(gcodeProgram) + print('Error traceback is the following:') + traceback.print_exc(file=sys.stdout) + print('') + +def writeOutput(fileName, fileNamePenultimate, fileNameSuffix, filePenultimateWritten, gcodeText=''): + 'Open penultimate file with outside program given text.' + repository = settings.getReadRepository(ClairvoyanceRepository()) + if repository.activateClairvoyance.value: + if not filePenultimateWritten: + archive.writeFileText(fileNamePenultimate, gcodeText) + getWindowAnalyzeFile(fileNamePenultimate, repository) + + +class ClairvoyanceRepository: + 'A class to handle the clairvoyance settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.analyze_plugins.clairvoyance.html', self) + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Clairvoyance') + self.activateClairvoyance = settings.BooleanSetting().getFromValue('Activate Clairvoyance', self, False) + settings.LabelSeparator().getFromRepository(self) + self.fileNameInput = settings.FileNameInput().getFromFileName([('Gcode text files', '*.gcode')], 'Open File to Generate Clairvoyances for', self, '') + self.gcodeProgram = settings.StringSetting().getFromValue('Gcode Program:', self, 'webbrowser') + self.executeTitle = 'Clairvoyance' + + def execute(self): + 'Write button has been clicked.' + fileNames = skeinforge_polyfile.getFileOrGcodeDirectory( self.fileNameInput.value, self.fileNameInput.wasCancelled, ['_comment'] ) + for fileName in fileNames: + getWindowAnalyzeFile(fileName) + + +def main(): + 'Display the clairvoyance dialog.' + if len(sys.argv) > 1: + getWindowAnalyzeFile(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/comment.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/comment.py new file mode 100644 index 0000000..42bfcd1 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/comment.py @@ -0,0 +1,170 @@ +""" +This page is in the table of contents. +Comment is an analyze plugin to comment a gcode file. + +The comment manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Comment + +==Operation== +The default 'Activate Comment' checkbox is off. When it is on, the file will be commented when called from the skeinforge toolchain, when it is off, the file will not be commented when called from the toolchain. The file will still be commented, whether or not the 'Activate Comment' checkbox is on, when comment is run directly. + +==Gcodes== +An explanation of the gcodes is at: +http://reprap.org/bin/view/Main/Arduino_GCode_Interpreter + +and at: +http://reprap.org/bin/view/Main/MCodeReference + +A gode example is at: +http://forums.reprap.org/file.php?12,file=565 + +==Examples== +Below are examples of comment being used. These examples are run in a terminal in the folder which contains Screw_Holder_penultimate.gcode and comment.py. + +> python comment.py +This brings up the comment dialog. + +> python comment.py Screw Holder_penultimate.gcode +The comment file is saved as Screw_Holder_penultimate_comment.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import cStringIO +import sys + + +__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 getNewRepository(): + 'Get new repository.' + return CommentRepository() + +def getWindowAnalyzeFile(fileName): + "Comment a gcode file." + gcodeText = archive.getFileText(fileName) + return getWindowAnalyzeFileGivenText(fileName, gcodeText) + +def getWindowAnalyzeFileGivenText(fileName, gcodeText): + "Write a commented gcode file for a gcode file." + skein = CommentSkein() + skein.parseGcode(gcodeText) + archive.writeFileMessageEnd('_comment.gcode', fileName, skein.output.getvalue(), 'The commented file is saved as ') + +def writeOutput(fileName, fileNamePenultimate, fileNameSuffix, filePenultimateWritten, gcodeText=''): + "Write a commented gcode file for a skeinforge gcode file, if 'Write Commented File for Skeinforge Chain' is selected." + repository = settings.getReadRepository( CommentRepository() ) + if gcodeText == '': + gcodeText = archive.getFileText( fileNameSuffix ) + if repository.activateComment.value: + getWindowAnalyzeFileGivenText( fileNameSuffix, gcodeText ) + + +class CommentRepository: + "A class to handle the comment settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.analyze_plugins.comment.html', self) + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Comment') + self.activateComment = settings.BooleanSetting().getFromValue('Activate Comment', self, False ) + self.fileNameInput = settings.FileNameInput().getFromFileName( [ ('Gcode text files', '*.gcode') ], 'Open File to Write Comments for', self, '') + self.executeTitle = 'Write Comments' + + def execute(self): + "Write button has been clicked." + fileNames = skeinforge_polyfile.getFileOrGcodeDirectory( self.fileNameInput.value, self.fileNameInput.wasCancelled, ['_comment'] ) + for fileName in fileNames: + getWindowAnalyzeFile(fileName) + + +class CommentSkein: + "A class to comment a gcode skein." + def __init__(self): + self.oldLocation = None + self.output = cStringIO.StringIO() + + def addComment( self, comment ): + "Add a gcode comment and a newline to the output." + self.output.write( "( " + comment + " )\n" ) + + def linearMove( self, splitLine ): + "Comment a linear move." + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + self.addComment( "Linear move to " + str( location ) + "." ); + self.oldLocation = location + + def parseGcode( self, gcodeText ): + "Parse gcode text and store the commented gcode." + lines = archive.getTextLines(gcodeText) + for line in lines: + self.parseLine(line) + + def parseLine(self, line): + "Parse a gcode line and add it to the commented gcode." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + self.linearMove(splitLine) + elif firstWord == 'G2': + self.setHelicalMoveEndpoint(splitLine) + self.addComment( "Helical clockwise move to " + str( self.oldLocation ) + "." ) + elif firstWord == 'G3': + self.setHelicalMoveEndpoint(splitLine) + self.addComment( "Helical counterclockwise move to " + str( self.oldLocation ) + "." ) + elif firstWord == 'G21': + self.addComment( "Set units to mm." ) + elif firstWord == 'G28': + self.addComment( "Start at home." ) + elif firstWord == 'G90': + self.addComment( "Set positioning to absolute." ) + elif firstWord == 'M101': + self.addComment( "Extruder on, forward." ); + elif firstWord == 'M102': + self.addComment( "Extruder on, reverse." ); + elif firstWord == 'M103': + self.addComment( "Extruder off." ) + elif firstWord == 'M104': + self.addComment( "Set temperature to " + str( gcodec.getDoubleAfterFirstLetter(splitLine[1]) ) + " C." ) + elif firstWord == 'M105': + self.addComment( "Custom code for temperature reading." ) + elif firstWord == 'M106': + self.addComment( "Turn fan on." ) + elif firstWord == 'M107': + self.addComment( "Turn fan off." ) + elif firstWord == 'M108': + self.addComment( "Set extruder speed to " + str( gcodec.getDoubleAfterFirstLetter(splitLine[1]) ) + "." ) + self.output.write(line + '\n') + + def setHelicalMoveEndpoint( self, splitLine ): + "Get the endpoint of a helical move." + if self.oldLocation == None: + print("A helical move is relative and therefore must not be the first move of a gcode file.") + return + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + location += self.oldLocation + self.oldLocation = location + + +def main(): + "Display the comment dialog." + if len(sys.argv) > 1: + getWindowAnalyzeFile(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() + diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/export_canvas_plugins/__init__.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/export_canvas_plugins/__init__.py new file mode 100644 index 0000000..58ec332 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/export_canvas_plugins/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 4 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/export_canvas_plugins/postscript.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/export_canvas_plugins/postscript.py new file mode 100644 index 0000000..ef3435d --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/export_canvas_plugins/postscript.py @@ -0,0 +1,97 @@ +""" +This page is in the table of contents. +Postscript is an export canvas plugin to export the canvas to a postscript file. + +When the export menu item in the file menu in an analyze viewer tool, like skeinlayer or skeiniso is clicked, the postscript dialog will be displayed. When the 'Export to Postscript' button on that dialog is clicked, the canvas will be exported as a postscript file. If the 'Postscript Program' is set to a program name, the postscript file will be sent to that program to be opened. The default is gimp, the Gnu Image Manipulation Program (Gimp), which is open source, can open postscript and save in a variety of formats. It is available at: +http://www.gimp.org/ + +If furthermore the 'File Extension' is set to a file extension, the postscript file will be sent to the program, along with the file extension for the converted output. The default is blank because some systems do not have an image conversion program; if you have or will install an image conversion program, a common 'File Extension' is png. A good open source conversion program is Image Magick, which is available at: +http://www.imagemagick.org/script/index.php + +An export canvas plugin is a script in the export_canvas_plugins folder which has the function getNewRepository, and which has a repository class with the functions setCanvasFileNameSuffix to set variables and execute to save the file. It is meant to be run from an analyze viewer tool, like skeinlayer or skeiniso. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name. + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import cStringIO +import os +import sys + + +__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 getNewRepository(): + 'Get new repository.' + return PostscriptRepository() + + +class PostscriptRepository: + "A class to handle the export settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository( + 'skeinforge_application.skeinforge_plugins.analyze_plugins.export_canvas_plugins.postscript.html', self) + self.fileExtension = settings.StringSetting().getFromValue('File Extension:', self, '') + self.postscriptProgram = settings.StringSetting().getFromValue('Postscript Program:', self, 'gimp') + + def execute(self): + "Convert to postscript button has been clicked. Export the canvas as a postscript file." + postscriptFileName = archive.getFilePathWithUnderscoredBasename( self.fileName, self.suffix ) + boundingBox = self.canvas.bbox( settings.Tkinter.ALL ) # tuple (w, n, e, s) + boxW = boundingBox[0] + boxN = boundingBox[1] + boxWidth = boundingBox[2] - boxW + boxHeight = boundingBox[3] - boxN + print('Exported postscript file saved as ' + postscriptFileName ) + self.canvas.postscript( file = postscriptFileName, height = boxHeight, width = boxWidth, pageheight = boxHeight, pagewidth = boxWidth, x = boxW, y = boxN ) + fileExtension = self.fileExtension.value + postscriptProgram = self.postscriptProgram.value + if postscriptProgram == '': + return + postscriptFilePath = '"' + os.path.normpath( postscriptFileName ) + '"' # " to send in file name with spaces + shellCommand = postscriptProgram + print('') + if fileExtension == '': + shellCommand += ' ' + postscriptFilePath + 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.' % postscriptProgram ) + print('If so, try installing the %s program or look for another one, like the Gnu Image Manipulation Program (Gimp) which can be found at:' % postscriptProgram ) + print('http://www.gimp.org/') + return + shellCommand += ' ' + archive.getFilePathWithUnderscoredBasename( postscriptFilePath, '.' + fileExtension + '"') + print('Sending the shell command:') + print(shellCommand) + commandResult = os.system(shellCommand) + if commandResult != 0: + print('The %s program could not convert the postscript to the %s file format.' % ( postscriptProgram, fileExtension ) ) + print('Try installing the %s program or look for another one, like Image Magick which can be found at:' % postscriptProgram ) + print('http://www.imagemagick.org/script/index.php') + + def setCanvasFileNameSuffix( self, canvas, fileName, suffix ): + "Set the canvas and initialize the execute title." + self.canvas = canvas + self.executeTitle = 'Export to Postscript' + self.fileName = fileName + self.suffix = suffix + '.ps' + + +def main(): + "Display the file or directory dialog." + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/export_canvas_plugins/scalable_vector_graphics.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/export_canvas_plugins/scalable_vector_graphics.py new file mode 100644 index 0000000..26d5fd8 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/export_canvas_plugins/scalable_vector_graphics.py @@ -0,0 +1,137 @@ +""" +This page is in the table of contents. +Scalable vector graphics is an export canvas plugin to export the canvas to a scalable vector graphics (.svg) file. + +When the export menu item in the file menu in an analyze viewer tool, like skeinlayer or skeiniso is clicked, the postscript dialog will be displayed. When the 'Export to Scalable Vector Graphics' button on that dialog is clicked, the canvas will be exported as a scalable vector graphics file. If the 'Scalable Vector Graphics Program' is set to the default 'webbrowser', the scalable vector graphics file will be sent to the default browser to be opened. If the 'Scalable Vector Graphics Program' is set to a program name, the scalable vector graphics file will be sent to that program to be opened. + +If furthermore the 'File Extension' is set to a file extension, the scalable vector graphics file will be sent to the program, along with the file extension for the converted output. The default is blank because some systems do not have an image conversion program; if you have or will install an image conversion program, a common 'File Extension' is png. A good open source conversion program is Image Magick, which is available at: +http://www.imagemagick.org/script/index.php + +An export canvas plugin is a script in the export_canvas_plugins folder which has the function getNewRepository, and which has a repository class with the functions setCanvasFileNameSuffix to set variables and execute to save the file. It is meant to be run from an analyze viewer tool, like skeinlayer or skeiniso. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import cStringIO +import os +import sys + + +__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 getNewRepository(): + 'Get new repository.' + return ScalableVectorGraphicsRepository() + +def parseLineReplace( firstWordTable, line, output ): + "Parse the line and replace it if the first word of the line is in the first word table." + firstWord = gcodec.getFirstWordFromLine(line) + if firstWord in firstWordTable: + line = firstWordTable[ firstWord ] + gcodec.addLineAndNewlineIfNecessary( line, output ) + + +class ScalableVectorGraphicsRepository: + "A class to handle the export settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository( + 'skeinforge_application.skeinforge_plugins.analyze_plugins.export_canvas_plugins.scalable_vector_graphics.html', self) + self.fileExtension = settings.StringSetting().getFromValue('File Extension:', self, '') + self.svgViewer = settings.StringSetting().getFromValue('SVG Viewer:', self, 'webbrowser') + + def addCanvasLineToOutput( self, canvasLinesOutput, objectIDNumber ): + "Add the canvas line to the output." + coordinates = self.canvas.coords( objectIDNumber ) + xBegin = coordinates[0] - self.boxW + xEnd = coordinates[2] - self.boxW + yBegin = coordinates[1] - self.boxN + yEnd = coordinates[3] - self.boxN + west = self.boxW + color = self.canvas.itemcget( objectIDNumber, 'fill') + width = self.canvas.itemcget( objectIDNumber, 'width') + line = '\n' % ( xBegin, yBegin, xEnd, yEnd, color, width ) + canvasLinesOutput.write(line + '\n') + + def execute(self): + "Export the canvas as an svg file." + svgFileName = archive.getFilePathWithUnderscoredBasename( self.fileName, self.suffix ) + boundingBox = self.canvas.bbox( settings.Tkinter.ALL ) # tuple (w, n, e, s) + self.boxW = boundingBox[0] + self.boxN = boundingBox[1] + boxWidth = boundingBox[2] - self.boxW + boxHeight = boundingBox[3] - self.boxN + print('Exported svg file saved as ' + svgFileName ) + svgTemplateText = archive.getFileText(archive.getTemplatesPath('canvas_template.svg')) + output = cStringIO.StringIO() + lines = archive.getTextLines( svgTemplateText ) + firstWordTable = {} + firstWordTable['height="999px"'] = ' height="%spx"' % int( round( boxHeight ) ) + firstWordTable[''] = self.getCanvasLinesOutput() + firstWordTable['replaceLineWithTitle'] = archive.getSummarizedFileName( self.fileName ) + firstWordTable['width="999px"'] = ' width="%spx"' % int( round( boxWidth ) ) + for line in lines: + parseLineReplace( firstWordTable, line, output ) + archive.writeFileText( svgFileName, output.getvalue() ) + fileExtension = self.fileExtension.value + svgViewer = self.svgViewer.value + if svgViewer == '': + return + if svgViewer == 'webbrowser': + settings.openWebPage( svgFileName ) + return + svgFilePath = '"' + os.path.normpath( svgFileName ) + '"' # " to send in file name with spaces + shellCommand = svgViewer + print('') + if fileExtension == '': + shellCommand += ' ' + svgFilePath + 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.' % svgViewer ) + print('If so, try installing the %s program or look for another svg viewer, like Netscape which can be found at:' % svgViewer ) + print('http://www.netscape.org/') + return + shellCommand += ' ' + archive.getFilePathWithUnderscoredBasename( svgFilePath, '.' + fileExtension + '"') + print('Sending the shell command:') + print(shellCommand) + commandResult = os.system(shellCommand) + if commandResult != 0: + print('The %s program could not convert the svg to the %s file format.' % ( svgViewer, fileExtension ) ) + print('Try installing the %s program or look for another one, like Image Magick which can be found at:' % svgViewer ) + print('http://www.imagemagick.org/script/index.php') + + def getCanvasLinesOutput(self): + "Add the canvas line to the output." + canvasLinesOutput = cStringIO.StringIO() + objectIDNumbers = self.canvas.find_all() + for objectIDNumber in objectIDNumbers: + if self.canvas.type( objectIDNumber ) == 'line': + self.addCanvasLineToOutput( canvasLinesOutput, objectIDNumber ) + return canvasLinesOutput.getvalue() + + def setCanvasFileNameSuffix( self, canvas, fileName, suffix ): + "Set the canvas and initialize the execute title." + self.canvas = canvas + self.executeTitle = 'Convert to Scalable Vector Graphics' + self.fileName = fileName + self.suffix = suffix + '.svg' + + +def main(): + "Display the file or directory dialog." + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/interpret.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/interpret.py new file mode 100644 index 0000000..b3b2f42 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/interpret.py @@ -0,0 +1,66 @@ +""" +This page is in the table of contents. +Interpret is an analyze plugin to interpret a file, turning a 2D file into svg and a 3D file into constructive solid geometry xml. + +The comment manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Interpret + +==Operation== +The default 'Activate Interpret' checkbox is off. When it is on, the functions described below will work when called from the skeinforge toolchain, when it is off, the functions will not be called from the tool chain. The functions will still be called, whether or not the 'Activate Interpret' checkbox is on, when interpret is run directly. + +==Settings== +===Print Interpretion=== +Default is off. + +When selected, the xml text will be printed to the console. + +===Text Program=== +Default is webbrowser. + +If the 'Text Program' is set the default 'webbrowser', the XML file will be sent to the default browser to be opened. If the 'Text Program' is set to a program name, the XML file will be sent to that program to be opened. + +==Examples== +Below are examples of interpret being used. These examples are run in a terminal in the folder which contains Screw_Holder.stl and interpret.py. + +> python interpret.py +This brings up the interpret dialog. + +> python interpret.py Screw_Holder.stl +The comment file is saved as Screw_Holder_interpret.xml + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import settings +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +import sys + + +__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 getNewRepository(): + 'Get new repository.' + return fabmetheus_interpret.InterpretRepository() + +def writeOutput(fileName, fileNamePenultimate, fileNameSuffix, filePenultimateWritten, gcodeText=''): + "Write file interpretation, if activate interpret is selected." + repository = settings.getReadRepository(getNewRepository()) + if repository.activateInterpret.value: + fabmetheus_interpret.getWindowAnalyzeFile(fileName) + + +def main(): + "Display the interpret dialog." + if len(sys.argv) > 1: + fabmetheus_interpret.getWindowAnalyzeFile(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/skeiniso.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/skeiniso.py new file mode 100644 index 0000000..68406e7 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/skeiniso.py @@ -0,0 +1,886 @@ +""" +This page is in the table of contents. +Skeiniso is an analyze viewer to display a gcode file in an isometric view. + +The skeiniso manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Skeiniso + +==Operation== +The default 'Activate Skeiniso' checkbox is off. When it is on, the functions described below will work when called from the skeinforge toolchain, when it is off, the functions will not be called from the toolchain. The functions will still be called, whether or not the 'Activate Skeiniso' checkbox is on, when skeiniso is run directly. + +Skeiniso requires skeinforge comments in the gcode file to distinguish the loops and edges. If the comments are deleted, all threads will be displayed as generic threads. To get the penultimate file of the tool chain, just before export deletes the comments, select 'Save Penultimate Gcode' in export, and open the gcode file with the suffix '_penultimate.gcode' with skeiniso. + +The viewer is simple, the viewpoint can only be moved in a sphere around the center of the model by changing the viewpoint latitude and longitude. Different regions of the model can be hidden by setting the width of the thread to zero. The alternating bands act as contour bands and their brightness and width can be changed. + +==Settings== +===Animation=== +====Animation Line Quickening==== +Default is one. + +The quickness of the tool animation over the quickness of the actual tool. + +====Animation Slide Show Rate==== +Default is two layers per second. + +The rate, in layers per second, at which the layer changes when the soar or dive button is pressed.. + +===Axis Rulings=== +Default is on. + +When selected, rulings will be drawn on the axis lines. + +===Banding=== +====Band Height==== +Default is five layers. + +Defines the height of the band in layers, a pair of bands is twice that height. + +====Bottom Band Brightness==== +Default is 0.7. + +Defines the ratio of the brightness of the bottom band over the brightness of the top band. The higher it is the brighter the bottom band will be. + +====Bottom Layer Brightness==== +Default is one. + +Defines the ratio of the brightness of the bottom layer over the brightness of the top layer. With a low bottom layer brightness ratio the bottom of the model will be darker than the top of the model, as if it was being illuminated by a light just above the top. + +====Bright Band Start==== +Default choice is 'From the Top'. + +The button group that determines where the bright band starts from. + +=====From the Bottom===== +When selected, the bright bands will start from the bottom. + +=====From the Top===== +When selected, the bright bands will start from the top. + +===Draw Arrows=== +Default is on. + +When selected, arrows will be drawn at the end of each line segment. + +===Export Menu=== +When the submenu in the export menu item in the file menu is clicked, an export canvas dialog will be displayed, which can export the canvas to a file. + +===Go Around Extruder Off Travel=== +Default is off. + +When selected, the display will include the travel when the extruder is off, which means it will include the nozzle wipe path if any. + +===Layers=== +====Layer==== +Default is zero. + +On the display window, the Up button increases the 'Layer' by one, and the Down button decreases the layer by one. When the layer displayed in the layer spin box is changed then is hit, the layer shown will be set to the spin box, to a mimimum of zero and to a maximum of the highest index layer.The Soar button increases the layer at the 'Animation Slide Show Rate', and the Dive (double left arrow button beside the layer field) button decreases the layer at the slide show rate. + +====Layer Extra Span==== +Default is a huge number. + +The viewer will draw the layers in the range including the 'Layer' index and the 'Layer' index plus the 'Layer Extra Span'. If the 'Layer Extra Span' is negative, the layers viewed will start at the 'Layer' index, plus the 'Layer Extra Span', and go up to and include the 'Layer' index. If the 'Layer Extra Span' is zero, only the 'Layer' index layer will be displayed. If the 'Layer Extra Span' is positive, the layers viewed will start at the 'Layer' index, and go up to and include the 'Layer' index plus the 'Layer Extra Span'. + +===Line=== +Default is zero. + +The index of the selected line on the layer that is highlighted when the 'Display Line' mouse tool is chosen. The line spin box up button increases the 'Line' by one. If the line index of the layer goes over the index of the last line, the layer index will be increased by one and the new line index will be zero. The down button decreases the line index by one. If the line index goes below the index of the first line, the layer index will be decreased by one and the new line index will be at the last line. When the line displayed in the line field is changed then is hit, the line shown will be set to the line field, to a mimimum of zero and to a maximum of the highest index line. The Soar button increases the line at the speed at which the extruder would move, times the 'Animation Line Quickening' ratio, and the Dive (double left arrow button beside the line field) button decreases the line at the animation line quickening ratio. + +===Mouse Mode=== +Default is 'Display Line'. + +The mouse tool can be changed from the 'Mouse Mode' menu button or picture button. The mouse tools listen to the arrow keys when the canvas has the focus. Clicking in the canvas gives the canvas the focus, and when the canvas has the focus a thick black border is drawn around the canvas. + +====Display Line==== +The 'Display Line' tool will display the highlight the selected line, and display the file line count, counting from one, and the gcode line itself. When the 'Display Line' tool is active, clicking the canvas will select the closest line to the mouse click. + +====Viewpoint Move==== +The 'Viewpoint Move' tool will move the viewpoint in the xy plane when the mouse is clicked and dragged on the canvas. + +====Viewpoint Rotate==== +The 'Viewpoint Rotate' tool will rotate the viewpoint around the origin, when the mouse is clicked and dragged on the canvas, or the arrow keys have been used and is pressed. The viewpoint can also be moved by dragging the mouse. The viewpoint latitude will be increased when the mouse is dragged from the center towards the edge. The viewpoint longitude will be changed by the amount around the center the mouse is dragged. This is not very intuitive, but I don't know how to do this the intuitive way and I have other stuff to develop. If the shift key is pressed; if the latitude is changed more than the longitude, only the latitude will be changed, if the longitude is changed more only the longitude will be changed. + +===Number of Fill Layers=== +====Number of Fill Bottom Layers==== +Default is one. + +The "Number of Fill Bottom Layers" is the number of layers at the bottom which will be colored olive. + +===Number of Fill Top Layers=== +Default is one. + +The "Number of Fill Top Layers" is the number of layers at the top which will be colored blue. + +===Scale=== +Default is ten. + +The scale setting is the scale of the image in pixels per millimeter, the higher the number, the greater the size of the display. + +The zoom in mouse tool will zoom in the display at the point where the mouse was clicked, increasing the scale by a factor of two. The zoom out tool will zoom out the display at the point where the mouse was clicked, decreasing the scale by a factor of two. + +===Screen Inset=== +====Screen Horizontal Inset==== +Default is one hundred. + +The "Screen Horizontal Inset" determines how much the canvas will be inset in the horizontal direction from the edge of screen, the higher the number the more it will be inset and the smaller it will be. + +====Screen Vertical Inset==== +Default is two hundred and twenty. + +The "Screen Vertical Inset" determines how much the canvas will be inset in the vertical direction from the edge of screen, the higher the number the more it will be inset and the smaller it will be.. + +===Viewpoint=== +====Viewpoint Latitude==== +Default is fifteen degrees. + +The "Viewpoint Latitude" is the latitude of the viewpoint, a latitude of zero is the top pole giving a top view, a latitude of ninety gives a side view and a latitude of 180 gives a bottom view. + +====Viewpoint Longitude==== +Default is 210 degrees. + +The "Viewpoint Longitude" is the longitude of the viewpoint. + +===Width=== +The width of each type of thread and of each axis can be changed. If the width is set to zero, the thread will not be visible. + +====Width of Axis Negative Side==== +Default is two. + +Defines the width of the negative side of the axis. + +====Width of Axis Positive Side==== +Default is six. + +Defines the width of the positive side of the axis. + +====Width of Infill Thread==== +Default is one. + +The "Width of Infill Thread" sets the width of the green extrusion threads, those threads which are not loops and not part of the raft. + +====Width of Fill Bottom Thread==== +Default is two. + +The "Width of Fill Bottom Thread" sets the width of the olive extrusion threads at the bottom of the model. + +====Width of Fill Top Thread==== +Default is two. + +The "Width of Fill Top Thread" sets the width of the blue extrusion threads at the top of the model. + +====Width of Loop Thread==== +Default is three. + +The "Width of Loop Thread" sets the width of the yellow loop threads, which are not edges. + +====Width of Perimeter Inside Thread==== +Default is eight. + +The "Width of Perimeter Inside Thread" sets the width of the orange inside edge threads. + +====Width of Perimeter Outside Thread==== +Default is eight. + +The "Width of Perimeter Outside Thread" sets the width of the red outside edge threads. + +====Width of Raft Thread==== +Default is one. + +The "Width of Raft Thread" sets the width of the brown raft threads. + +====Width of Selection Thread==== +Default is six. + +The "Width of Selection Thread" sets the width of the selected line. + +====Width of Travel Thread==== +Default is zero. + +The "Width of Travel Thread" sets the width of the grey extruder off travel threads. + +==Icons== +The dive, soar and zoom icons are from Mark James' soarSilk icon set 1.3 at: +http://www.famfamfam.com/lab/icons/silk/ + +==Gcodes== +An explanation of the gcodes is at: +http://reprap.org/bin/view/Main/Arduino_GCode_Interpreter + +and at: +http://reprap.org/bin/view/Main/MCodeReference + +A gode example is at: +http://forums.reprap.org/file.php?12,file=565 + +==Examples== +Below are examples of skeiniso being used. These examples are run in a terminal in the folder which contains Screw Holder_penultimate.gcode and skeiniso.py. + +> python skeiniso.py +This brings up the skeiniso dialog. + +> python skeiniso.py Screw Holder_penultimate.gcode +This brings up the skeiniso viewer to view the gcode file. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities import display_line +from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities import tableau +from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities import view_move +from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities import view_rotate +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import sys + + +__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 compareLayerSequence( first, second ): + "Get comparison in order to sort skein panes in ascending order of layer zone index then sequence index." + if first.layerZoneIndex < second.layerZoneIndex: + return - 1 + if first.layerZoneIndex > second.layerZoneIndex: + return 1 + if first.sequenceIndex < second.sequenceIndex: + return - 1 + return int( first.sequenceIndex > second.sequenceIndex ) + +def getNewRepository(): + 'Get new repository.' + return SkeinisoRepository() + +def getWindowAnalyzeFile(fileName): + "Skeiniso a gcode file." + gcodeText = archive.getFileText(fileName) + return getWindowAnalyzeFileGivenText(fileName, gcodeText) + +def getWindowAnalyzeFileGivenText( fileName, gcodeText, repository=None): + "Display a skeiniso gcode file for a gcode file." + if gcodeText == '': + return None + if repository == None: + repository = settings.getReadRepository( SkeinisoRepository() ) + skeinWindow = getWindowGivenTextRepository( fileName, gcodeText, repository ) + skeinWindow.updateDeiconify() + return skeinWindow + +def getWindowGivenTextRepository( fileName, gcodeText, repository ): + "Display the gcode text in a skeiniso viewer." + skein = SkeinisoSkein() + skein.parseGcode( fileName, gcodeText, repository ) + return SkeinWindow( repository, skein ) + +def writeOutput(fileName, fileNamePenultimate, fileNameSuffix, filePenultimateWritten, gcodeText=''): + "Write a skeinisoed gcode file for a skeinforge gcode file, if 'Activate Skeiniso' is selected." + try: + import Tkinter + except: + print('Warning, skeiniso will do nothing because Tkinter is not installed.') + return + repository = settings.getReadRepository( SkeinisoRepository() ) + if repository.activateSkeiniso.value: + gcodeText = archive.getTextIfEmpty( fileNameSuffix, gcodeText ) + return getWindowAnalyzeFileGivenText( fileNameSuffix, gcodeText, repository ) + + +class SkeinisoRepository( tableau.TableauRepository ): + "A class to handle the skeiniso settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.analyze_plugins.skeiniso.html', self) + self.baseNameSynonym = 'behold.csv' + self.fileNameInput = settings.FileNameInput().getFromFileName( [ ('Gcode text files', '*.gcode') ], 'Open File for Skeiniso', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Skeiniso') + self.activateSkeiniso = settings.BooleanSetting().getFromValue('Activate Skeiniso', self, False) + self.addAnimation() + self.axisRulings = settings.BooleanSetting().getFromValue('Axis Rulings', self, True ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Banding -', self ) + self.bandHeight = settings.IntSpinUpdate().getFromValue( 0, 'Band Height (layers):', self, 10, 5 ) + self.bottomBandBrightness = settings.FloatSpinUpdate().getFromValue( 0.0, 'Bottom Band Brightness (ratio):', self, 1.0, 0.7 ) + self.bottomLayerBrightness = settings.FloatSpinUpdate().getFromValue( 0.0, 'Bottom Layer Brightness (ratio):', self, 1.0, 1.0 ) + self.brightBandStart = settings.MenuButtonDisplay().getFromName('Bright Band Start:', self ) + self.fromTheBottom = settings.MenuRadio().getFromMenuButtonDisplay( self.brightBandStart, 'From the Bottom', self, False ) + self.fromTheTop = settings.MenuRadio().getFromMenuButtonDisplay( self.brightBandStart, 'From the Top', self, True ) + settings.LabelSeparator().getFromRepository(self) + self.drawArrows = settings.BooleanSetting().getFromValue('Draw Arrows', self, False ) + self.goAroundExtruderOffTravel = settings.BooleanSetting().getFromValue('Go Around Extruder Off Travel', self, False ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Layers -', self ) + self.layer = settings.IntSpinNotOnMenu().getSingleIncrementFromValue( 0, 'Layer (index):', self, 912345678, 0 ) + self.layerExtraSpan = settings.IntSpinUpdate().getSingleIncrementFromValue( - 912345678, 'Layer Extra Span (integer):', self, 912345678, 912345678 ) + settings.LabelSeparator().getFromRepository(self) + self.line = settings.IntSpinNotOnMenu().getSingleIncrementFromValue( 0, 'Line (index):', self, 912345678, 0 ) + self.mouseMode = settings.MenuButtonDisplay().getFromName('Mouse Mode:', self ) + self.displayLine = settings.MenuRadio().getFromMenuButtonDisplay( self.mouseMode, 'Display Line', self, True ) + self.viewMove = settings.MenuRadio().getFromMenuButtonDisplay( self.mouseMode, 'View Move', self, False ) + self.viewRotate = settings.MenuRadio().getFromMenuButtonDisplay( self.mouseMode, 'View Rotate', self, False ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Number of Fill Layers -', self ) + self.numberOfFillBottomLayers = settings.IntSpinUpdate().getFromValue( 0, 'Number of Fill Bottom Layers (integer):', self, 5, 1 ) + self.numberOfFillTopLayers = settings.IntSpinUpdate().getFromValue( 0, 'Number of Fill Top Layers (integer):', self, 5, 1 ) + settings.LabelSeparator().getFromRepository(self) + self.addScaleScreenSlide() + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Viewpoint -', self ) + self.viewpointLatitude = settings.FloatSpin().getFromValue( 0.0, 'Viewpoint Latitude (degrees):', self, 180.0, 15.0 ) + self.viewpointLongitude = settings.FloatSpin().getFromValue( 0.0, 'Viewpoint Longitude (degrees):', self, 360.0, 210.0 ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Width -', self ) + self.widthOfAxisNegativeSide = settings.IntSpinUpdate().getFromValue( 0, 'Width of Axis Negative Side (pixels):', self, 10, 2 ) + self.widthOfAxisPositiveSide = settings.IntSpinUpdate().getFromValue( 0, 'Width of Axis Positive Side (pixels):', self, 10, 6 ) + self.widthOfFillBottomThread = settings.IntSpinUpdate().getFromValue( 0, 'Width of Fill Bottom Thread (pixels):', self, 10, 2 ) + self.widthOfFillTopThread = settings.IntSpinUpdate().getFromValue( 0, 'Width of Fill Top Thread (pixels):', self, 10, 2 ) + self.widthOfInfillThread = settings.IntSpinUpdate().getFromValue( 0, 'Width of Infill Thread (pixels):', self, 10, 1 ) + self.widthOfLoopThread = settings.IntSpinUpdate().getFromValue( 0, 'Width of Loop Thread (pixels):', self, 10, 2 ) + self.widthOfPerimeterInsideThread = settings.IntSpinUpdate().getFromValue( 0, 'Width of Perimeter Inside Thread (pixels):', self, 10, 8 ) + self.widthOfPerimeterOutsideThread = settings.IntSpinUpdate().getFromValue( 0, 'Width of Perimeter Outside Thread (pixels):', self, 10, 8 ) + self.widthOfRaftThread = settings.IntSpinUpdate().getFromValue( 0, 'Width of Raft Thread (pixels):', self, 10, 1 ) + self.widthOfSelectionThread = settings.IntSpinUpdate().getFromValue( 0, 'Width of Selection Thread (pixels):', self, 10, 6 ) + self.widthOfTravelThread = settings.IntSpinUpdate().getFromValue( 0, 'Width of Travel Thread (pixels):', self, 10, 0 ) + self.executeTitle = 'Skeiniso' + + def execute(self): + "Write button has been clicked." + fileNames = skeinforge_polyfile.getFileOrGcodeDirectory( self.fileNameInput.value, self.fileNameInput.wasCancelled ) + for fileName in fileNames: + getWindowAnalyzeFile(fileName) + + +class SkeinisoSkein: + "A class to write a get a scalable vector graphics text for a gcode skein." + def __init__(self): + self.coloredThread = [] + self.feedRateMinute = 960.1 + self.hasANestedRingBeenReached = False + self.isEdge = False + self.isLoop = False + self.isOuter = False + self.isThereALayerStartWord = False + self.layerCount = settings.LayerCount() + self.layerTops = [] + self.lineIndex = 0 + self.oldLayerZoneIndex = 0 + self.oldZ = - 999987654321.0 + self.skeinPane = None + self.skeinPanes = [] + self.thirdLayerThickness = 0.133333 + + def addToPath( self, line, location ): + 'Add a point to travel and maybe extrusion.' + if self.oldLocation == None: + return + begin = self.scale * self.oldLocation - self.scaleCenterBottom + end = self.scale * location - self.scaleCenterBottom + displayString = '%s %s' % ( self.lineIndex + 1, line ) + tagString = 'colored_line_index: %s %s' % ( len( self.skeinPane.coloredLines ), len( self.skeinPanes ) - 1 ) + coloredLine = tableau.ColoredLine( begin, '', displayString, end, tagString ) + coloredLine.z = location.z + self.skeinPane.coloredLines.append( coloredLine ) + self.coloredThread.append( coloredLine ) + + def getLayerTop(self): + "Get the layer top." + if len( self.layerTops ) < 1: + return - 9123456789123.9 + return self.layerTops[-1] + + def getLayerZoneIndex( self, z ): + "Get the layer zone index." + if self.layerTops[ self.oldLayerZoneIndex ] > z: + if self.oldLayerZoneIndex == 0: + return 0 + elif self.layerTops[ self.oldLayerZoneIndex - 1 ] < z: + return self.oldLayerZoneIndex + for layerTopIndex in xrange( len( self.layerTops ) ): + layerTop = self.layerTops[ layerTopIndex ] + if layerTop > z: + self.oldLayerZoneIndex = layerTopIndex + return layerTopIndex + self.oldLayerZoneIndex = len( self.layerTops ) - 1 + return self.oldLayerZoneIndex + + def initializeActiveLocation(self): + "Set variables to default." + self.extruderActive = False + self.oldLocation = None + + def linearCorner( self, splitLine ): + "Update the bounding corners." + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + if self.extruderActive or self.goAroundExtruderOffTravel: + self.cornerMaximum.maximize(location) + self.cornerMinimum.minimize(location) + self.oldLocation = location + + def linearMove( self, line, location ): + "Get statistics for a linear move." + if self.skeinPane == None: + return + self.addToPath(line, location) + + def moveColoredThreadToSkeinPane(self): + 'Move a colored thread to the skein pane.' + if len( self.coloredThread ) <= 0: + return + layerZoneIndex = self.getLayerZoneIndex( self.coloredThread[0].z ) + if not self.extruderActive: + self.setColoredThread( ( 190.0, 190.0, 190.0 ), self.skeinPane.travelLines ) #grey + return + self.skeinPane.layerZoneIndex = layerZoneIndex + if self.isEdge: + if self.isOuter: + self.setColoredThread( ( 255.0, 0.0, 0.0 ), self.skeinPane.edgeOutsideLines ) #red + else: + self.setColoredThread( ( 255.0, 165.0, 0.0 ), self.skeinPane.edgeInsideLines ) #orange + return + if self.isLoop: + self.setColoredThread( ( 255.0, 255.0, 0.0 ), self.skeinPane.loopLines ) #yellow + return + if not self.hasANestedRingBeenReached: + self.setColoredThread( ( 165.0, 42.0, 42.0 ), self.skeinPane.raftLines ) #brown + return + if layerZoneIndex < self.repository.numberOfFillBottomLayers.value: + self.setColoredThread( ( 128.0, 128.0, 0.0 ), self.skeinPane.fillBottomLines ) #olive + return + if layerZoneIndex >= self.firstTopLayer: + self.setColoredThread( ( 0.0, 0.0, 255.0 ), self.skeinPane.fillTopLines ) #blue + return + self.setColoredThread( ( 0.0, 255.0, 0.0 ), self.skeinPane.infillLines ) #green + + def parseCorner(self, line): + "Parse a gcode line and use the location to update the bounding corners." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if tableau.getIsLayerStart(firstWord, self, splitLine): + if firstWord == '(': + self.layerTopZ = float(splitLine[1]) + self.thirdLayerThickness + else: + self.layerTopZ = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine).z + self.thirdLayerThickness + self.layerTops.append( self.layerTopZ ) + if firstWord == 'G1': + self.linearCorner(splitLine) + elif firstWord == 'M101': + self.extruderActive = True + elif firstWord == 'M103': + self.extruderActive = False + elif firstWord == '(': + self.thirdLayerThickness = 0.33333333333 * float(splitLine[1]) + if firstWord == '()': + if self.layerTopZ > self.getLayerTop(): + self.layerTops.append( self.layerTopZ ) + + def parseGcode( self, fileName, gcodeText, repository ): + "Parse gcode text and store the vector output." + self.repository = repository + self.fileName = fileName + self.gcodeText = gcodeText + self.initializeActiveLocation() + self.cornerMaximum = Vector3(-987654321.0, -987654321.0, -987654321.0) + self.cornerMinimum = Vector3(987654321.0, 987654321.0, 987654321.0) + self.goAroundExtruderOffTravel = repository.goAroundExtruderOffTravel.value + self.lines = archive.getTextLines(gcodeText) + self.isThereALayerStartWord = (gcodec.getFirstWordIndexReverse('(', self.lines, 1) > -1) + if self.isThereALayerStartWord: + self.parseInitialization() + else: + print('') + print('') + print('') + print('Warning, there are no skeinforge comments in this text, probably because they have been removed by export.') + print('So there is no loop information, and therefore the lines will not be colored.') + print('') + print('To see the full information in an exported file, either deselect Delete Comments in export, or') + print('select Save Penultimate Gcode in export, and open the generated file with the suffix _penultimate.gcode.') + print('') + print('') + print('') + for line in self.lines[self.lineIndex :]: + self.parseCorner(line) + self.oldZ = - 999987654321.0 + if len( self.layerTops ) > 0: + self.layerTops[-1] += 912345678.9 + if len( self.layerTops ) > 1: + self.oneMinusBrightnessOverTopLayerIndex = ( 1.0 - repository.bottomLayerBrightness.value ) / float( len( self.layerTops ) - 1 ) + self.firstTopLayer = len( self.layerTops ) - self.repository.numberOfFillTopLayers.value + self.centerComplex = 0.5 * ( self.cornerMaximum.dropAxis() + self.cornerMinimum.dropAxis() ) + self.centerBottom = Vector3( self.centerComplex.real, self.centerComplex.imag, self.cornerMinimum.z ) + self.scale = repository.scale.value + self.scaleCenterBottom = self.scale * self.centerBottom + self.scaleCornerHigh = self.scale * self.cornerMaximum.dropAxis() + self.scaleCornerLow = self.scale * self.cornerMinimum.dropAxis() + print("The lower left corner of the skeiniso window is at %s, %s" % (self.cornerMinimum.x, self.cornerMinimum.y)) + print("The upper right corner of the skeiniso window is at %s, %s" % (self.cornerMaximum.x, self.cornerMaximum.y)) + self.cornerImaginaryTotal = self.cornerMaximum.y + self.cornerMinimum.y + margin = complex( 5.0, 5.0 ) + self.marginCornerLow = self.scaleCornerLow - margin + self.screenSize = margin + 2.0 * ( self.scaleCornerHigh - self.marginCornerLow ) + self.initializeActiveLocation() + for self.lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[self.lineIndex] + self.parseLine(line) + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == '()': + return + elif firstWord == '(': + self.feedRateMinute = 60.0 * float(splitLine[1]) + + def parseLine(self, line): + "Parse a gcode line and add it to the vector output." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if tableau.getIsLayerStart(firstWord, self, splitLine): + self.layerCount.printProgressIncrement('skeiniso') + self.skeinPane = SkeinPane( len( self.skeinPanes ) ) + self.skeinPanes.append( self.skeinPane ) + if firstWord == 'G1': + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + self.linearMove(line, location) + self.oldLocation = location + elif firstWord == 'M101': + self.moveColoredThreadToSkeinPane() + self.extruderActive = True + elif firstWord == 'M103': + self.moveColoredThreadToSkeinPane() + self.extruderActive = False + self.isEdge = False + self.isLoop = False + elif firstWord == '(': + self.isLoop = True + elif firstWord == '()': + self.moveColoredThreadToSkeinPane() + self.isLoop = False + elif firstWord == '()': + self.hasANestedRingBeenReached = True + elif firstWord == '(': + self.isEdge = True + self.isOuter = ( splitLine[1] == 'outer') + elif firstWord == '()': + self.moveColoredThreadToSkeinPane() + self.isEdge = False + if firstWord == 'G2' or firstWord == 'G3': + relativeLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + relativeLocation.z = 0.0 + location = self.oldLocation + relativeLocation + self.linearMove(line, location) + self.oldLocation = location + + def setColoredLineColor( self, coloredLine, colorTuple ): + 'Set the color and stipple of the colored line.' + layerZoneIndex = self.getLayerZoneIndex( coloredLine.z ) + multiplier = self.repository.bottomLayerBrightness.value + if len( self.layerTops ) > 1: + multiplier += self.oneMinusBrightnessOverTopLayerIndex * float( layerZoneIndex ) + bandIndex = layerZoneIndex / self.repository.bandHeight.value + if self.repository.fromTheTop.value: + brightZoneIndex = len( self.layerTops ) - 1 - layerZoneIndex + bandIndex = brightZoneIndex / self.repository.bandHeight.value + 1 + if bandIndex % 2 == 0: + multiplier *= self.repository.bottomBandBrightness.value + red = settings.getWidthHex( int( colorTuple[0] * multiplier ), 2 ) + green = settings.getWidthHex( int( colorTuple[1] * multiplier ), 2 ) + blue = settings.getWidthHex( int( colorTuple[2] * multiplier ), 2 ) + coloredLine.colorName = '#%s%s%s' % ( red, green, blue ) + + def setColoredThread( self, colorTuple, lineList ): + 'Set the colored thread, then move it to the line list and stipple of the colored line.' + for coloredLine in self.coloredThread: + self.setColoredLineColor( coloredLine, colorTuple ) + lineList += self.coloredThread + self.coloredThread = [] + + +class SkeinPane: + "A class to hold the colored lines for a layer." + def __init__( self, sequenceIndex ): + "Create empty line lists." + self.coloredLines = [] + self.edgeInsideLines = [] + self.edgeOutsideLines = [] + self.fillBottomLines = [] + self.fillTopLines = [] + self.index = 0 + self.infillLines = [] + self.layerZoneIndex = 0 + self.loopLines = [] + self.raftLines = [] + self.sequenceIndex = sequenceIndex + self.travelLines = [] + + +class Ruling: + def __init__( self, modelDistance, roundedRulingText ): + "Initialize the ruling." + self.modelDistance = modelDistance + self.roundedRulingText = roundedRulingText + + +class SkeinWindow( tableau.TableauWindow ): + def __init__( self, repository, skein ): + "Initialize the skein window." + self.arrowshape = ( 24, 30, 9 ) + self.addCanvasMenuRootScrollSkein( repository, skein, '_skeiniso', 'Skeiniso') + self.center = 0.5 * self.screenSize + self.motionStippleName = 'gray75' + halfCenter = 0.5 * self.center.real + negativeHalfCenter = - halfCenter + self.halfCenterModel = halfCenter / skein.scale + negativeHalfCenterModel = - self.halfCenterModel + roundedHalfCenter = euclidean.getThreeSignificantFigures( self.halfCenterModel ) + roundedNegativeHalfCenter = euclidean.getThreeSignificantFigures( negativeHalfCenterModel ) + self.negativeAxisLineX = tableau.ColoredLine( Vector3(), 'darkorange', None, Vector3( negativeHalfCenter ), 'X Negative Axis: Origin -> %s,0,0' % roundedNegativeHalfCenter ) + self.negativeAxisLineY = tableau.ColoredLine( Vector3(), 'gold', None, Vector3( 0.0, negativeHalfCenter ), 'Y Negative Axis: Origin -> 0,%s,0' % roundedNegativeHalfCenter ) + self.negativeAxisLineZ = tableau.ColoredLine( Vector3(), 'skyblue', None, Vector3( 0.0, 0.0, negativeHalfCenter ), 'Z Negative Axis: Origin -> 0,0,%s' % roundedNegativeHalfCenter ) + self.positiveAxisLineX = tableau.ColoredLine( Vector3(), 'darkorange', None, Vector3( halfCenter ), 'X Positive Axis: Origin -> %s,0,0' % roundedHalfCenter ) + self.positiveAxisLineY = tableau.ColoredLine( Vector3(), 'gold', None, Vector3( 0.0, halfCenter ), 'Y Positive Axis: Origin -> 0,%s,0' % roundedHalfCenter ) + self.positiveAxisLineZ = tableau.ColoredLine( Vector3(), 'skyblue', None, Vector3( 0.0, 0.0, halfCenter ), 'Z Positive Axis: Origin -> 0,0,%s' % roundedHalfCenter ) + self.repository.axisRulings.setUpdateFunction( self.setWindowToDisplaySaveUpdate ) + self.repository.bandHeight.setUpdateFunction( self.setWindowToDisplaySavePhoenixUpdate ) + self.repository.bottomBandBrightness.setUpdateFunction( self.setWindowToDisplaySavePhoenixUpdate ) + self.repository.bottomLayerBrightness.setUpdateFunction( self.setWindowToDisplaySavePhoenixUpdate ) + self.repository.fromTheBottom.setUpdateFunction( self.setWindowToDisplaySavePhoenixUpdate ) + self.repository.fromTheTop.setUpdateFunction( self.setWindowToDisplaySavePhoenixUpdate ) + self.setWindowNewMouseTool( display_line.getNewMouseTool, self.repository.displayLine ) + self.setWindowNewMouseTool( view_move.getNewMouseTool, self.repository.viewMove ) + self.setWindowNewMouseTool( view_rotate.getNewMouseTool, self.repository.viewRotate ) + self.repository.numberOfFillBottomLayers.setUpdateFunction( self.setWindowToDisplaySavePhoenixUpdate ) + self.repository.numberOfFillTopLayers.setUpdateFunction( self.setWindowToDisplaySavePhoenixUpdate ) + self.repository.viewpointLatitude.setUpdateFunction( self.setWindowToDisplaySaveUpdate ) + self.repository.viewpointLongitude.setUpdateFunction( self.setWindowToDisplaySaveUpdate ) + self.repository.widthOfAxisNegativeSide.setUpdateFunction( self.setWindowToDisplaySaveUpdate ) + self.repository.widthOfAxisPositiveSide.setUpdateFunction( self.setWindowToDisplaySaveUpdate ) + self.repository.widthOfFillBottomThread.setUpdateFunction( self.setWindowToDisplaySaveUpdate ) + self.repository.widthOfFillTopThread.setUpdateFunction( self.setWindowToDisplaySaveUpdate ) + self.repository.widthOfInfillThread.setUpdateFunction( self.setWindowToDisplaySaveUpdate ) + self.repository.widthOfLoopThread.setUpdateFunction( self.setWindowToDisplaySaveUpdate ) + self.repository.widthOfPerimeterInsideThread.setUpdateFunction( self.setWindowToDisplaySaveUpdate ) + self.repository.widthOfPerimeterOutsideThread.setUpdateFunction( self.setWindowToDisplaySaveUpdate ) + self.repository.widthOfRaftThread.setUpdateFunction( self.setWindowToDisplaySaveUpdate ) + self.addMouseToolsBind() + self.negativeRulings = [] + self.positiveRulings = [] + for rulingIndex in xrange( 1, int( math.ceil( self.halfCenterModel / self.rulingSeparationWidthMillimeters ) ) ): + modelDistance = rulingIndex * self.rulingSeparationWidthMillimeters + self.negativeRulings.append( Ruling( modelDistance, self.getRoundedRulingText( 1, - modelDistance ) ) ) + self.positiveRulings.append( Ruling( modelDistance, self.getRoundedRulingText( 1, modelDistance ) ) ) + self.rulingExtentHalf = 0.5 * self.rulingExtent + + def drawRuling( self, projectiveSpace, relativeRulingEnd, ruling, tags, viewBegin, viewEnd ): + "Draw ruling." + alongWay = ruling.modelDistance / self.halfCenterModel + oneMinusAlongWay = 1.0 - alongWay + alongScreen = alongWay * viewEnd + oneMinusAlongWay * viewBegin + alongScreenEnd = alongScreen + relativeRulingEnd + self.canvas.create_line( + alongScreen.real, + alongScreen.imag, + alongScreenEnd.real, + alongScreenEnd.imag, + fill = 'black', + tags = tags, + width = 2 ) + self.canvas.create_text( int( alongScreenEnd.real ) + 3, alongScreenEnd.imag, anchor = settings.Tkinter.W, text = ruling.roundedRulingText ) + + def drawRulings( self, axisLine, projectiveSpace, rulings ): + "Draw rulings for the axis line." + if not self.repository.axisRulings.value: + return + viewBegin = self.getScreenView( axisLine.begin, projectiveSpace ) + viewEnd = self.getScreenView( axisLine.end, projectiveSpace ) + viewSegment = viewEnd - viewBegin + viewSegmentLength = abs( viewSegment ) + if viewSegmentLength < self.rulingExtent: + return + normalizedViewSegment = viewSegment / viewSegmentLength + relativeRulingEnd = complex( - normalizedViewSegment.imag, normalizedViewSegment.real ) + if normalizedViewSegment.imag > 0.0: + relativeRulingEnd = complex( normalizedViewSegment.imag, - normalizedViewSegment.real ) + for ruling in rulings: + self.drawRuling( projectiveSpace, relativeRulingEnd * self.rulingExtentHalf, ruling, axisLine.tagString, viewBegin, viewEnd ) + + def drawSkeinPane( self, projectiveSpace, skeinPane ): + "Draw colored lines." + self.getDrawnColoredLines( skeinPane.raftLines, projectiveSpace, self.repository.widthOfRaftThread.value ) + self.getDrawnColoredLines( skeinPane.travelLines, projectiveSpace, self.repository.widthOfTravelThread.value ) + self.getDrawnColoredLines( skeinPane.fillBottomLines, projectiveSpace, self.repository.widthOfFillBottomThread.value ) + self.getDrawnColoredLines( skeinPane.fillTopLines, projectiveSpace, self.repository.widthOfFillTopThread.value ) + self.getDrawnColoredLines( skeinPane.infillLines, projectiveSpace, self.repository.widthOfInfillThread.value ) + self.getDrawnColoredLines( skeinPane.loopLines, projectiveSpace, self.repository.widthOfLoopThread.value ) + self.getDrawnColoredLines( skeinPane.edgeInsideLines, projectiveSpace, self.repository.widthOfPerimeterInsideThread.value ) + self.getDrawnColoredLines( skeinPane.edgeOutsideLines, projectiveSpace, self.repository.widthOfPerimeterOutsideThread.value ) + + def drawXYAxisLines( self, projectiveSpace ): + "Draw the x and y axis lines." + if self.repository.widthOfAxisNegativeSide.value > 0: + self.getDrawnColoredLineWithoutArrow( self.negativeAxisLineX, projectiveSpace, self.negativeAxisLineX.tagString, self.repository.widthOfAxisNegativeSide.value ) + self.getDrawnColoredLineWithoutArrow( self.negativeAxisLineY, projectiveSpace, self.negativeAxisLineY.tagString, self.repository.widthOfAxisNegativeSide.value ) + if self.repository.widthOfAxisPositiveSide.value > 0: + self.getDrawnColoredLine('last', self.positiveAxisLineX, projectiveSpace, self.positiveAxisLineX.tagString, self.repository.widthOfAxisPositiveSide.value ) + self.getDrawnColoredLine('last', self.positiveAxisLineY, projectiveSpace, self.positiveAxisLineY.tagString, self.repository.widthOfAxisPositiveSide.value ) + + def drawZAxisLine( self, projectiveSpace ): + "Draw the z axis line." + if self.repository.widthOfAxisNegativeSide.value > 0: + self.getDrawnColoredLineWithoutArrow( self.negativeAxisLineZ, projectiveSpace, self.negativeAxisLineZ.tagString, self.repository.widthOfAxisNegativeSide.value ) + if self.repository.widthOfAxisPositiveSide.value > 0: + self.getDrawnColoredLine('last', self.positiveAxisLineZ, projectiveSpace, self.positiveAxisLineZ.tagString, self.repository.widthOfAxisPositiveSide.value ) + + def getCanvasRadius(self): + "Get half of the minimum of the canvas height and width." + return 0.5 * min( float( self.canvasHeight ), float( self.canvasWidth ) ) + + def getCentered( self, coordinate ): + "Get the centered coordinate." + relativeToCenter = complex( coordinate.real - self.center.real, self.center.imag - coordinate.imag ) + if abs( relativeToCenter ) < 1.0: + relativeToCenter = complex( 0.0, 1.0 ) + return relativeToCenter + + def getCenteredScreened( self, coordinate ): + "Get the normalized centered coordinate." + return self.getCentered( coordinate ) / self.getCanvasRadius() + + def getColoredLines(self): + "Get the colored lines from the skein pane." + return self.skeinPanes[ self.repository.layer.value ].coloredLines + + def getCopy(self): + "Get a copy of this window." + return SkeinWindow( self.repository, self.skein ) + + def getCopyWithNewSkein(self): + "Get a copy of this window with a new skein." + return getWindowGivenTextRepository( self.skein.fileName, self.skein.gcodeText, self.repository ) + + def getDrawnColoredLine( self, arrowType, coloredLine, projectiveSpace, tags, width ): + "Draw colored line." + viewBegin = self.getScreenView( coloredLine.begin, projectiveSpace ) + viewEnd = self.getScreenView( coloredLine.end, projectiveSpace ) + return self.canvas.create_line( + viewBegin.real, + viewBegin.imag, + viewEnd.real, + viewEnd.imag, + fill = coloredLine.colorName, + arrow = arrowType, + tags = tags, + width = width ) + + def getDrawnColoredLineMotion( self, coloredLine, projectiveSpace, width ): + "Draw colored line with motion stipple and tag." + viewBegin = self.getScreenView( coloredLine.begin, projectiveSpace ) + viewEnd = self.getScreenView( coloredLine.end, projectiveSpace ) + return self.canvas.create_line( + viewBegin.real, + viewBegin.imag, + viewEnd.real, + viewEnd.imag, + fill = coloredLine.colorName, + arrow = 'last', + arrowshape = self.arrowshape, + stipple = self.motionStippleName, + tags = 'mouse_item', + width = width + 4 ) + + def getDrawnColoredLines( self, coloredLines, projectiveSpace, width ): + "Draw colored lines." + if width <= 0: + return + drawnColoredLines = [] + for coloredLine in coloredLines: + drawnColoredLines.append( self.getDrawnColoredLine( self.arrowType, coloredLine, projectiveSpace, coloredLine.tagString, width ) ) + return drawnColoredLines + + def getDrawnColoredLineWithoutArrow( self, coloredLine, projectiveSpace, tags, width ): + "Draw colored line without an arrow." + viewBegin = self.getScreenView( coloredLine.begin, projectiveSpace ) + viewEnd = self.getScreenView( coloredLine.end, projectiveSpace ) + return self.canvas.create_line( + viewBegin.real, + viewBegin.imag, + viewEnd.real, + viewEnd.imag, + fill = coloredLine.colorName, + tags = tags, + width = width ) + + def getDrawnSelectedColoredLine( self, coloredLine ): + "Get the drawn selected colored line." + projectiveSpace = euclidean.ProjectiveSpace().getByLatitudeLongitude( self.repository.viewpointLatitude.value, self.repository.viewpointLongitude.value ) + return self.getDrawnColoredLine( self.arrowType, coloredLine, projectiveSpace, 'selection_line', self.repository.widthOfSelectionThread.value ) + + def getScreenComplex( self, pointComplex ): + "Get the point in screen perspective." + return complex( pointComplex.real, - pointComplex.imag ) + self.center + + def getScreenView( self, point, projectiveSpace ): + "Get the point in screen view perspective." + return self.getScreenComplex( projectiveSpace.getDotComplex(point) ) + + def printHexadecimalColorName(self, name): + "Print the color name in hexadecimal." + colorTuple = self.canvas.winfo_rgb( name ) + print('#%s%s%s' % ( settings.getWidthHex( colorTuple[0], 2 ), settings.getWidthHex( colorTuple[1], 2 ), settings.getWidthHex( colorTuple[2], 2 ) ) ) + + def update(self): + "Update the screen." + if len( self.skeinPanes ) < 1: + return + self.limitIndexSetArrowMouseDeleteCanvas() + self.repository.viewpointLatitude.value = view_rotate.getBoundedLatitude( self.repository.viewpointLatitude.value ) + self.repository.viewpointLongitude.value = round( self.repository.viewpointLongitude.value, 1 ) + projectiveSpace = euclidean.ProjectiveSpace().getByLatitudeLongitude( self.repository.viewpointLatitude.value, self.repository.viewpointLongitude.value ) + skeinPanesCopy = self.getUpdateSkeinPanes()[:] + skeinPanesCopy.sort( compareLayerSequence ) + if projectiveSpace.basisZ.z > 0.0: + self.drawXYAxisLines( projectiveSpace ) + else: + skeinPanesCopy.reverse() + self.drawZAxisLine( projectiveSpace ) + for skeinPane in skeinPanesCopy: + self.drawSkeinPane( projectiveSpace, skeinPane ) + if projectiveSpace.basisZ.z > 0.0: + self.drawZAxisLine( projectiveSpace ) + else: + self.drawXYAxisLines( projectiveSpace ) + if self.repository.widthOfAxisNegativeSide.value > 0: + self.drawRulings( self.negativeAxisLineX, projectiveSpace, self.negativeRulings ) + self.drawRulings( self.negativeAxisLineY, projectiveSpace, self.negativeRulings ) + self.drawRulings( self.negativeAxisLineZ, projectiveSpace, self.negativeRulings ) + if self.repository.widthOfAxisPositiveSide.value > 0: + self.drawRulings( self.positiveAxisLineX, projectiveSpace, self.positiveRulings ) + self.drawRulings( self.positiveAxisLineY, projectiveSpace, self.positiveRulings ) + self.drawRulings( self.positiveAxisLineZ, projectiveSpace, self.positiveRulings ) + self.setDisplayLayerIndex() + + +def main(): + "Display the skeiniso dialog." + if len(sys.argv) > 1: + settings.startMainLoopFromWindow( getWindowAnalyzeFile(' '.join(sys.argv[1 :])) ) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/skeinlayer.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/skeinlayer.py new file mode 100644 index 0000000..35d8cde --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/skeinlayer.py @@ -0,0 +1,575 @@ +""" +This page is in the table of contents. +Skeinlayer is an analyze viewer to display each layer of a gcode file. + +The skeinlayer manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Skeinlayer + +Skeinlayer is derived from Nophead's preview script. The extruded lines are in the resistor colors red, orange, yellow, green, blue, purple & brown. When the extruder is off, the travel line is grey. Skeinlayer is useful for a detailed view of the extrusion, skeiniso is better to see the orientation of the shape. To get an initial overview of the skein, when the skeinlayer display window appears, click the Soar button (double right arrow button beside the layer field). + +==Operation== +The default 'Activate Skeinlayer' checkbox is on. When it is on, the functions described below will work when called from the skeinforge toolchain, when it is off, the functions will not be called from the toolchain. The functions will still be called, whether or not the 'Activate Skeinlayer' checkbox is on, when skeinlayer is run directly. Skeinlayer has trouble separating the layers when it reads gcode without comments. + +==Settings== +===Animation=== +====Animation Line Quickening==== +Default is one. + +The quickness of the tool animation over the quickness of the actual tool. + +====Animation Slide Show Rate==== +Default is two layers per second. + +The rate, in layers per second, at which the layer changes when the soar or dive button is pressed.. + +===Draw Arrows=== +Default is on. + +When selected, arrows will be drawn at the end of each line segment. + +===Export Menu=== +When the submenu in the export menu item in the file menu is clicked, an export canvas dialog will be displayed, which can export the canvas to a file. + +===Go Around Extruder Off Travel=== +Default is off. + +When selected, the display will include the travel when the extruder is off, which means it will include the nozzle wipe path if any. + +===Layers=== +====Layer==== +Default is zero. + +On the display window, the Up button increases the 'Layer' by one, and the Down button decreases the layer by one. When the layer displayed in the layer spin box is changed then is hit, the layer shown will be set to the spin box, to a mimimum of zero and to a maximum of the highest index layer.The Soar button increases the layer at the 'Animation Slide Show Rate', and the Dive (double left arrow button beside the layer field) button decreases the layer at the slide show rate. + +====Layer Extra Span==== +Default is zero. + +The viewer will draw the layers in the range including the 'Layer' index and the 'Layer' index plus the 'Layer Extra Span'. If the 'Layer Extra Span' is negative, the layers viewed will start at the 'Layer' index, plus the 'Layer Extra Span', and go up to and include the 'Layer' index. If the 'Layer Extra Span' is zero, only the 'Layer' index layer will be displayed. If the 'Layer Extra Span' is positive, the layers viewed will start at the 'Layer' index, and go up to and include the 'Layer' index plus the 'Layer Extra Span'. + +===Line=== +Default is zero. + +The index of the selected line on the layer that is highlighted when the 'Display Line' mouse tool is chosen. The line spin box up button increases the 'Line' by one. If the line index of the layer goes over the index of the last line, the layer index will be increased by one and the new line index will be zero. The down button decreases the line index by one. If the line index goes below the index of the first line, the layer index will be decreased by one and the new line index will be at the last line. When the line displayed in the line field is changed then is hit, the line shown will be set to the line field, to a mimimum of zero and to a maximum of the highest index line. The Soar button increases the line at the speed at which the extruder would move, times the 'Animation Line Quickening' ratio, and the Dive (double left arrow button beside the line field) button decreases the line at the animation line quickening ratio. + +===Mouse Mode=== +Default is 'Display Line'. + +The mouse tool can be changed from the 'Mouse Mode' menu button or picture button. The mouse tools listen to the arrow keys when the canvas has the focus. Clicking in the canvas gives the canvas the focus, and when the canvas has the focus a thick black border is drawn around the canvas. + +====Display Line==== +The 'Display Line' tool will display the highlight the selected line, and display the file line count, counting from one, and the gcode line itself. When the 'Display Line' tool is active, clicking the canvas will select the closest line to the mouse click. + +====Viewpoint Move==== +The 'Viewpoint Move' tool will move the viewpoint in the xy plane when the mouse is clicked and dragged on the canvas. + +===Numeric Pointer=== +Default is on. + +When selected, the distance along the ruler of the arrow pointers will be drawn next to the pointers. + +===Scale=== +Default is ten. + +The scale setting is the scale of the image in pixels per millimeter, the higher the number, the greater the size of the display. + +The zoom in mouse tool will zoom in the display at the point where the mouse was clicked, increasing the scale by a factor of two. The zoom out tool will zoom out the display at the point where the mouse was clicked, decreasing the scale by a factor of two. + +===Screen Inset=== +====Screen Horizontal Inset==== +Default is one hundred. + +The "Screen Horizontal Inset" determines how much the canvas will be inset in the horizontal direction from the edge of screen, the higher the number the more it will be inset and the smaller it will be. + +====Screen Vertical Inset==== +Default is two hundred and twenty. + +The "Screen Vertical Inset" determines how much the canvas will be inset in the vertical direction from the edge of screen, the higher the number the more it will be inset and the smaller it will be. + +===Width=== +The width of each type of thread and of each axis can be changed. If the width is set to zero, the thread will not be visible. + +====Width of Extrusion Thread==== +Default is three. + +The "Width of Extrusion Thread" sets the width of the extrusion threads. + +====Width of Selection Thread==== +Default is six. + +The "Width of Selection Thread" sets the width of the selected line. + +====Width of Travel Thread==== +Default is one. + +The "Width of Travel Thread" sets the width of the grey extruder off travel threads. + +==Icons== +The dive, soar and zoom icons are from Mark James' soarSilk icon set 1.3 at: +http://www.famfamfam.com/lab/icons/silk/ + +==Gcodes== +An explanation of the gcodes is at: +http://reprap.org/bin/view/Main/Arduino_GCode_Interpreter + +and at: +http://reprap.org/bin/view/Main/MCodeReference + +A gode example is at: +http://forums.reprap.org/file.php?12,file=565 + +==Examples== +Below are examples of skeinlayer being used. These examples are run in a terminal in the folder which contains Screw Holder_penultimate.gcode and skeinlayer.py. + +> python skeinlayer.py +This brings up the skeinlayer dialog. + +> python skeinlayer.py Screw Holder_penultimate.gcode +This brings up the skeinlayer viewer to view each layer of a gcode file. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities import display_line +from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities import tableau +from skeinforge_application.skeinforge_plugins.analyze_plugins.analyze_utilities import view_move +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import os +import sys + +__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 getNewRepository(): + 'Get new repository.' + return SkeinlayerRepository() + +def getRankIndex( rulingSeparationWidthMillimeters, screenOrdinate ): + "Get rank index." + return int( round( screenOrdinate / rulingSeparationWidthMillimeters ) ) + +def getWindowAnalyzeFile(fileName): + "Display a gcode file in a skeinlayer window." + gcodeText = archive.getFileText(fileName) + return getWindowAnalyzeFileGivenText(fileName, gcodeText) + +def getWindowAnalyzeFileGivenText( fileName, gcodeText, repository=None): + "Display a gcode file in a skeinlayer window given the text." + if gcodeText == '': + return None + if repository == None: + repository = settings.getReadRepository( SkeinlayerRepository() ) + skeinWindow = getWindowGivenTextRepository( fileName, gcodeText, repository ) + skeinWindow.updateDeiconify() + return skeinWindow + +def getWindowGivenTextRepository( fileName, gcodeText, repository ): + "Display a gcode file in a skeinlayer window given the text and settings." + skein = SkeinlayerSkein() + skein.parseGcode( fileName, gcodeText, repository ) + return SkeinWindow( repository, skein ) + +def writeOutput(fileName, fileNamePenultimate, fileNameSuffix, filePenultimateWritten, gcodeText=''): + "Display a skeinlayered gcode file for a skeinforge gcode file, if 'Activate Skeinlayer' is selected." + try: + import Tkinter + except: + print('Warning, skeinlayer will do nothing because Tkinter is not installed.') + return + repository = settings.getReadRepository( SkeinlayerRepository() ) + if repository.activateSkeinlayer.value: + gcodeText = archive.getTextIfEmpty( fileNameSuffix, gcodeText ) + return getWindowAnalyzeFileGivenText( fileNameSuffix, gcodeText, repository ) + + +class SkeinlayerRepository( tableau.TableauRepository ): + "A class to handle the skeinlayer settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.analyze_plugins.skeinlayer.html', self) + self.baseNameSynonym = 'skeinview.csv' + self.fileNameInput = settings.FileNameInput().getFromFileName( [ ('Gcode text files', '*.gcode') ], 'Open File for Skeinlayer', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Skeinlayer') + self.activateSkeinlayer = settings.BooleanSetting().getFromValue('Activate Skeinlayer', self, False ) + self.addAnimation() + self.drawArrows = settings.BooleanSetting().getFromValue('Draw Arrows', self, True ) + self.goAroundExtruderOffTravel = settings.BooleanSetting().getFromValue('Go Around Extruder Off Travel', self, False ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Layers -', self ) + self.layer = settings.IntSpinNotOnMenu().getSingleIncrementFromValue( 0, 'Layer (index):', self, 912345678, 0 ) + self.layerExtraSpan = settings.IntSpinUpdate().getSingleIncrementFromValue( - 3, 'Layer Extra Span (integer):', self, 3, 0 ) + settings.LabelSeparator().getFromRepository(self) + self.line = settings.IntSpinNotOnMenu().getSingleIncrementFromValue( 0, 'Line (index):', self, 912345678, 0 ) + self.mouseMode = settings.MenuButtonDisplay().getFromName('Mouse Mode:', self ) + self.displayLine = settings.MenuRadio().getFromMenuButtonDisplay( self.mouseMode, 'Display Line', self, True ) + self.viewMove = settings.MenuRadio().getFromMenuButtonDisplay( self.mouseMode, 'View Move', self, False ) + self.addScaleScreenSlide() + self.showPosition = settings.BooleanSetting().getFromValue('Show Position', self, True ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Width -', self ) + self.widthOfExtrusionThread = settings.IntSpinUpdate().getSingleIncrementFromValue( 0, 'Width of Extrusion Thread (pixels):', self, 5, 3 ) + self.widthOfSelectionThread = settings.IntSpinUpdate().getSingleIncrementFromValue( 0, 'Width of Selection Thread (pixels):', self, 10, 6 ) + self.widthOfTravelThread = settings.IntSpinUpdate().getSingleIncrementFromValue( 0, 'Width of Travel Thread (pixels):', self, 5, 1 ) + self.executeTitle = 'Skeinlayer' + + def execute(self): + "Write button has been clicked." + fileNames = skeinforge_polyfile.getFileOrGcodeDirectory( self.fileNameInput.value, self.fileNameInput.wasCancelled ) + for fileName in fileNames: + getWindowAnalyzeFile(fileName) + + +class SkeinlayerSkein: + "A class to write a get a scalable vector graphics text for a gcode skein." + def __init__(self): + 'Initialize.' + self.extrusionNumber = 0 + self.feedRateMinute = 960.1 + self.isThereALayerStartWord = False + self.layerCount = settings.LayerCount() + self.oldZ = - 999987654321.0 + self.skeinPane = None + self.skeinPanes = [] + + def addToPath( self, line, location ): + "Add a point to travel and maybe extrusion." + if self.oldLocation == None: + return + colorName = 'gray' + locationComplex = location.dropAxis() + oldLocationComplex = self.oldLocation.dropAxis() + begin = self.getScreenCoordinates( oldLocationComplex ) + end = self.getScreenCoordinates( locationComplex ) + if self.extruderActive: + colorName = self.colorNames[ self.extrusionNumber % len( self.colorNames ) ] + displayString = '%s %s' % ( self.lineIndex + 1, line ) + tagString = 'colored_line_index: %s %s' % ( len( self.skeinPane ), len( self.skeinPanes ) - 1 ) + coloredLine = tableau.ColoredLine( begin, colorName, displayString, end, tagString ) + coloredLine.isExtrusionThread = self.extruderActive + self.skeinPane.append( coloredLine ) + + def getModelCoordinates( self, screenCoordinates ): + "Get the model coordinates." + modelCoordinates = ( screenCoordinates + self.marginCornerLow ) / self.scale + return complex( modelCoordinates.real, self.cornerImaginaryTotal - modelCoordinates.imag ) + + def getScreenCoordinates( self, pointComplex ): + "Get the screen coordinates." + pointComplex = complex( pointComplex.real, self.cornerImaginaryTotal - pointComplex.imag ) + return self.scale * pointComplex - self.marginCornerLow + + def initializeActiveLocation(self): + "Set variables to default." + self.extruderActive = False + self.oldLocation = None + + def linearCorner( self, splitLine ): + "Update the bounding corners." + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + if self.extruderActive or self.repository.goAroundExtruderOffTravel.value: + self.cornerMaximum.maximize(location) + self.cornerMinimum.minimize(location) + self.oldLocation = location + + def linearMove( self, line, location ): + "Get statistics for a linear move." + if self.skeinPane != None: + self.addToPath(line, location) + + def parseCorner(self, line): + "Parse a gcode line and use the location to update the bounding corners." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + self.linearCorner(splitLine) + elif firstWord == 'M101': + self.extruderActive = True + elif firstWord == 'M103': + self.extruderActive = False + + def parseGcode( self, fileName, gcodeText, repository ): + "Parse gcode text and store the vector output." + self.fileName = fileName + self.gcodeText = gcodeText + self.repository = repository + self.initializeActiveLocation() + self.cornerMaximum = Vector3(-987654321.0, -987654321.0, -987654321.0) + self.cornerMinimum = Vector3(987654321.0, 987654321.0, 987654321.0) + self.lines = archive.getTextLines(gcodeText) + self.isThereALayerStartWord = (gcodec.getFirstWordIndexReverse('(', self.lines, 1) > -1) + self.parseInitialization() + for line in self.lines[self.lineIndex :]: + self.parseCorner(line) + self.cornerMaximumComplex = self.cornerMaximum.dropAxis() + self.cornerMinimumComplex = self.cornerMinimum.dropAxis() + self.scale = repository.scale.value + self.scaleCornerHigh = self.scale * self.cornerMaximumComplex + self.scaleCornerLow = self.scale * self.cornerMinimumComplex + self.cornerImaginaryTotal = self.cornerMaximum.y + self.cornerMinimum.y + self.margin = complex( 10.0, 10.0 ) + self.marginCornerHigh = self.scaleCornerHigh + self.margin + self.marginCornerLow = self.scaleCornerLow - self.margin + self.screenSize = self.marginCornerHigh - self.marginCornerLow + self.initializeActiveLocation() + self.colorNames = ['brown', 'red', 'orange', 'yellow', 'green', 'blue', 'purple'] + for self.lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[self.lineIndex] + self.parseLine(line) + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == '()': + return + elif firstWord == '(': + self.feedRateMinute = 60.0 * float(splitLine[1]) + self.lineIndex = 0 + + def parseLine(self, line): + "Parse a gcode line and add it to the vector output." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if tableau.getIsLayerStart(firstWord, self, splitLine): + self.extrusionNumber = 0 + self.layerCount.printProgressIncrement('skeinlayer') + self.skeinPane = [] + self.skeinPanes.append( self.skeinPane ) + if firstWord == 'G1': + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + self.linearMove(line, location) + self.oldLocation = location + elif firstWord == 'M101': + self.extruderActive = True + self.extrusionNumber += 1 + elif firstWord == 'M103': + self.extruderActive = False + if firstWord == 'G2' or firstWord == 'G3': + relativeLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + relativeLocation.z = 0.0 + location = self.oldLocation + relativeLocation + self.linearMove(line, location) + self.oldLocation = location + + +class SkeinWindow( tableau.TableauWindow ): + def __init__(self, repository, skein): + "Initialize the skein window.setWindowNewMouseTool" + self.addCanvasMenuRootScrollSkein(repository, skein, '_skeinlayer', 'Skeinlayer') + horizontalRulerBoundingBox = (0, 0, int( skein.screenSize.real ), self.rulingExtent) + self.horizontalRulerCanvas = settings.Tkinter.Canvas(self.root, width = self.canvasWidth, height = self.rulingExtent, scrollregion=horizontalRulerBoundingBox) + self.horizontalRulerCanvas.grid(row=1, column=2, columnspan=96, sticky=settings.Tkinter.E+settings.Tkinter.W) + self.horizontalRulerCanvas['xscrollcommand'] = self.xScrollbar.set + verticalRulerBoundingBox = (0, 0, self.rulingExtent, int(skein.screenSize.imag)) + self.verticalRulerCanvas = settings.Tkinter.Canvas(self.root, width=self.rulingExtent, height=self.canvasHeight, scrollregion=verticalRulerBoundingBox) + self.verticalRulerCanvas.grid(row=2, rowspan=96, column=1, sticky=settings.Tkinter.N+settings.Tkinter.S) + self.verticalRulerCanvas['yscrollcommand'] = self.yScrollbar.set + self.xStringVar = settings.Tkinter.StringVar(self.root) + self.xLabel = settings.Tkinter.Label(self.root, textvariable=self.xStringVar) + self.xLabel.grid(row=0, column=3, sticky=settings.Tkinter.W) + self.yStringVar = settings.Tkinter.StringVar(self.root) + self.yLabel = settings.Tkinter.Label(self.root, textvariable=self.yStringVar) + self.yLabel.grid(row=0, column=4, sticky=settings.Tkinter.W) + self.setWindowNewMouseTool(display_line.getNewMouseTool, repository.displayLine) + self.setWindowNewMouseTool(view_move.getNewMouseTool, repository.viewMove) + repository.showPosition.setUpdateFunction(self.setWindowToDisplaySaveUpdate) + repository.widthOfExtrusionThread.setUpdateFunction(self.setWindowToDisplaySaveUpdate) + self.addMouseToolsBind() + self.createRulers() + + def addHorizontalRulerRuling( self, xMillimeters ): + "Add a ruling to the horizontal ruler." + xPixel = self.skein.getScreenCoordinates( complex( xMillimeters, 0.0 ) ).real + self.createVerticalLine( 0.0, xPixel ) + self.horizontalRulerCanvas.create_text( xPixel + 2, 0, anchor = settings.Tkinter.NW, text = self.getRoundedRulingText( 1, xMillimeters ) ) + cumulativeDistance = xMillimeters + self.createVerticalLine( self.rulingExtentTiny, self.skein.getScreenCoordinates( complex( xMillimeters + self.separationWidthMillimetersTenth, 0.0 ) ).real ) + for subRulingIndex in xrange(4): + cumulativeDistance += self.separationWidthMillimetersFifth + self.createVerticalLine( self.rulingExtentShort, self.skein.getScreenCoordinates( complex( cumulativeDistance, 0.0 ) ).real ) + self.createVerticalLine( self.rulingExtentTiny, self.skein.getScreenCoordinates( complex( cumulativeDistance + self.separationWidthMillimetersTenth, 0.0 ) ).real ) + + def addVerticalRulerRuling( self, yMillimeters ): + "Add a ruling to the vertical ruler." + fontHeight = 12 + yPixel = self.skein.getScreenCoordinates( complex( 0.0, yMillimeters ) ).imag + self.createHorizontalLine( 0.0, yPixel ) + yPixel += 2 + roundedRulingText = self.getRoundedRulingText( 1, yMillimeters ) + effectiveRulingTextLength = len( roundedRulingText ) + if roundedRulingText.find('.') != - 1: + effectiveRulingTextLength -= 1 + cumulativeDistance = yMillimeters + self.createHorizontalLine( self.rulingExtentTiny, self.skein.getScreenCoordinates( complex( 0.0, yMillimeters + self.separationWidthMillimetersTenth ) ).imag ) + for subRulingIndex in xrange(4): + cumulativeDistance += self.separationWidthMillimetersFifth + self.createHorizontalLine( self.rulingExtentShort, self.skein.getScreenCoordinates( complex( 0.0, cumulativeDistance ) ).imag ) + self.createHorizontalLine( self.rulingExtentTiny, self.skein.getScreenCoordinates( complex( 0.0, cumulativeDistance + self.separationWidthMillimetersTenth ) ).imag ) + if effectiveRulingTextLength < 4: + self.verticalRulerCanvas.create_text( 0, yPixel, anchor = settings.Tkinter.NW, text = roundedRulingText ) + return + for character in roundedRulingText: + if character == '.': + yPixel -= fontHeight * 2 / 3 + self.verticalRulerCanvas.create_text( 0, yPixel, anchor = settings.Tkinter.NW, text = character ) + yPixel += fontHeight + + def createHorizontalLine( self, begin, yPixel ): + "Create a horizontal line for the horizontal ruler." + self.verticalRulerCanvas.create_line( begin, yPixel, self.rulingExtent, yPixel, fill = 'black') + + def createRulers(self): + "Create the rulers.." + self.rulingExtentShort = 0.382 * self.rulingExtent + self.rulingExtentTiny = 0.764 * self.rulingExtent + self.rulingExtentPointer = 0.5 * ( self.rulingExtentShort + self.rulingExtentTiny ) + self.rulingPointerRadius = self.rulingExtent - self.rulingExtentPointer + self.textBoxHeight = int( 0.8 * self.rulingExtent ) + self.textBoxWidth = int( 2.5 * self.rulingExtent ) + self.separationWidthMillimetersFifth = 0.2 * self.rulingSeparationWidthMillimeters + self.separationWidthMillimetersTenth = 0.1 * self.rulingSeparationWidthMillimeters + rulingSeparationWidthPixels = self.getRulingSeparationWidthPixels( self.rank ) + marginOverScale = self.skein.margin / self.skein.scale + cornerMaximumMargin = self.skein.cornerMaximumComplex + marginOverScale + cornerMinimumMargin = self.skein.cornerMinimumComplex - marginOverScale + xRankIndexHigh = getRankIndex( self.rulingSeparationWidthMillimeters, cornerMaximumMargin.real ) + xRankIndexLow = getRankIndex( self.rulingSeparationWidthMillimeters, cornerMinimumMargin.real ) + for xRankIndex in xrange( xRankIndexLow - 2, xRankIndexHigh + 2 ): # 1 is enough, 2 is to be on the safe side + self.addHorizontalRulerRuling( xRankIndex * self.rulingSeparationWidthMillimeters ) + yRankIndexHigh = getRankIndex( self.rulingSeparationWidthMillimeters, cornerMaximumMargin.imag ) + yRankIndexLow = getRankIndex( self.rulingSeparationWidthMillimeters, cornerMinimumMargin.imag ) + for yRankIndex in xrange( yRankIndexLow - 2, yRankIndexHigh + 2 ): # 1 is enough, 2 is to be on the safe side + self.addVerticalRulerRuling( yRankIndex * self.rulingSeparationWidthMillimeters ) + + def createVerticalLine( self, begin, xPixel ): + "Create a vertical line for the horizontal ruler." + self.horizontalRulerCanvas.create_line( xPixel, begin, xPixel, self.rulingExtent, fill = 'black') + + def getColoredLines(self): + "Get the colored lines from the skein pane." + return self.skeinPanes[self.repository.layer.value] + + def getCopy(self): + "Get a copy of this window." + return SkeinWindow(self.repository, self.skein) + + def getCopyWithNewSkein(self): + "Get a copy of this window with a new skein." + return getWindowGivenTextRepository( self.skein.fileName, self.skein.gcodeText, self.repository ) + + def getDrawnColoredLine( self, coloredLine, tags, width ): + "Get the drawn colored line." + return self.canvas.create_line( + coloredLine.begin.real, + coloredLine.begin.imag, + coloredLine.end.real, + coloredLine.end.imag, + fill = coloredLine.colorName, + arrow = self.arrowType, + tags = tags, + width = width ) + + def getDrawnColoredLineIfThick( self, coloredLine, width ): + "Get the drawn colored line if it has a positive thickness." + if width > 0: + return self.getDrawnColoredLine( coloredLine, coloredLine.tagString, width ) + + def getDrawnSelectedColoredLine(self, coloredLine): + "Get the drawn selected colored line." + return self.getDrawnColoredLine(coloredLine, 'selection_line', self.repository.widthOfSelectionThread.value) + + def motion(self, event): + "The mouse moved." + self.mouseTool.motion(event) + xString = '' + yString = '' + x = self.canvas.canvasx( event.x ) + y = self.canvas.canvasy( event.y ) + self.horizontalRulerCanvas.delete('pointer') + self.horizontalRulerCanvas.create_polygon( x - self.rulingPointerRadius, self.rulingExtentPointer, x + self.rulingPointerRadius, self.rulingExtentPointer, x, self.rulingExtent, tag = 'pointer') + self.verticalRulerCanvas.delete('pointer') + self.verticalRulerCanvas.create_polygon( self.rulingExtentPointer, y - self.rulingPointerRadius, self.rulingExtentPointer, y + self.rulingPointerRadius, self.rulingExtent, y, tag = 'pointer') + if self.repository.showPosition.value: + motionCoordinate = complex(x, y) + modelCoordinates = self.skein.getModelCoordinates( motionCoordinate ) + roundedXText = self.getRoundedRulingText(3, modelCoordinates.real) + roundedYText = self.getRoundedRulingText(3, modelCoordinates.imag) + xString = 'X: ' + roundedXText + yString = 'Y: ' + roundedYText + self.xStringVar.set(xString) + self.yStringVar.set(yString) + + def qqqmotion(self, event): + "The mouse moved." + self.mouseTool.motion(event) + x = self.canvas.canvasx( event.x ) + y = self.canvas.canvasy( event.y ) + self.horizontalRulerCanvas.delete('pointer') + self.horizontalRulerCanvas.create_polygon( x - self.rulingPointerRadius, self.rulingExtentPointer, x + self.rulingPointerRadius, self.rulingExtentPointer, x, self.rulingExtent, tag = 'pointer') + self.verticalRulerCanvas.delete('pointer') + self.verticalRulerCanvas.create_polygon( self.rulingExtentPointer, y - self.rulingPointerRadius, self.rulingExtentPointer, y + self.rulingPointerRadius, self.rulingExtent, y, tag = 'pointer') + if not self.repository.numericPointer.value: + return + motionCoordinate = complex(x, y) + modelCoordinates = self.skein.getModelCoordinates( motionCoordinate ) + roundedXText = self.getRoundedRulingText( 3, modelCoordinates.real ) + yStart = self.canvas.canvasy( 0 ) + self.canvas.create_rectangle( x - 2, yStart, x + self.textBoxWidth, yStart + self.textBoxHeight + 5, fill = self.canvas['background'], tag = 'pointer') + self.canvas.create_text( x, yStart + 5, anchor = settings.Tkinter.NW, tag = 'pointer', text = roundedXText ) + roundedYText = self.getRoundedRulingText( 3, modelCoordinates.imag ) + xStart = self.canvas.canvasx( 0 ) + self.canvas.create_rectangle( xStart, y - 2, xStart + self.textBoxWidth + 5, y + self.textBoxHeight, fill = self.canvas['background'], tag = 'pointer') + self.canvas.create_text( xStart + 5, y, anchor = settings.Tkinter.NW, tag = 'pointer', text = roundedYText ) + xString = '' + xString = 'X: ' + roundedXText + self.xStringVar.set(xString) + + def relayXview( self, *args ): + "Relay xview changes." + self.canvas.xview( *args ) + self.horizontalRulerCanvas.xview( *args ) + + def relayYview( self, *args ): + "Relay yview changes." + self.canvas.yview( *args ) + self.verticalRulerCanvas.yview( *args ) + + def update(self): + "Update the window." + if len( self.skeinPanes ) < 1: + return + self.limitIndexSetArrowMouseDeleteCanvas() + for coloredLines in self.getUpdateSkeinPanes(): + for coloredLine in coloredLines: + if coloredLine.isExtrusionThread: + self.getDrawnColoredLineIfThick( coloredLine, self.repository.widthOfExtrusionThread.value ) + else: + self.getDrawnColoredLineIfThick( coloredLine, self.repository.widthOfTravelThread.value ) + self.setDisplayLayerIndex() + + +def main(): + "Display the skeinlayer dialog." + if len(sys.argv) > 1: + settings.startMainLoopFromWindow(getWindowAnalyzeFile(' '.join(sys.argv[1 :]))) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/statistic.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/statistic.py new file mode 100644 index 0000000..dcc51d6 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/statistic.py @@ -0,0 +1,404 @@ +""" +This page is in the table of contents. +Statistic is an extremely valuable analyze plugin to print and/or save the statistics of the generated gcode. + +The statistic manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Statistic + +==Operation== +The default 'Activate Statistic' checkbox is on. When it is on, the functions described below will work when called from the skeinforge toolchain, when it is off, the functions will not be called from the toolchain. The functions will still be called, whether or not the 'Activate Statistic' checkbox is on, when statistic is run directly. + +==Settings== +===Extrusion Diameter over Thickness=== +Default is 1.25. + +The 'Extrusion Diameter over Thickness is the ratio of the extrusion diameter over the layer height, the default is 1.25. The extrusion fill density ratio that is printed to the console, ( it is derived quantity not a parameter ) is the area of the extrusion diameter over the extrusion width over the layer height. Assuming the extrusion diameter is correct, a high value means the filament will be packed tightly, and the object will be almost as dense as the filament. If the fill density ratio is too high, there could be too little room for the filament, and the extruder will end up plowing through the extra filament. A low fill density ratio means the filaments will be far away from each other, the object will be leaky and light. The fill density ratio with the default extrusion settings is around 0.68. + +===Print Statistics=== +Default is on. + +When the 'Print Statistics' checkbox is on, the statistics will be printed to the console. + +===Save Statistics=== +Default is off. + +When the 'Save Statistics' checkbox is on, the statistics will be saved as a .txt file. + +==Gcodes== +An explanation of the gcodes is at: +http://reprap.org/bin/view/Main/Arduino_GCode_Interpreter + +and at: +http://reprap.org/bin/view/Main/MCodeReference + +A gode example is at: +http://forums.reprap.org/file.php?12,file=565 + +==Examples== +Below are examples of statistic being used. These examples are run in a terminal in the folder which contains Screw Holder_penultimate.gcode and statistic.py. The 'Save Statistics' checkbox is selected. + +> python statistic.py +This brings up the statistic dialog. + +> python statistic.py Screw Holder_penultimate.gcode +Statistics are being generated for the file /home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/models/Screw Holder_penultimate.gcode + +Cost +Machine time cost is 0.31$. +Material cost is 0.2$. +Total cost is 0.51$. + +Extent +X axis extrusion starts at 61 mm and ends at 127 mm, for a width of 65 mm. +Y axis extrusion starts at 81 mm and ends at 127 mm, for a depth of 45 mm. +Z axis extrusion starts at 0 mm and ends at 15 mm, for a height of 15 mm. + +Extruder +Build time is 18 minutes 47 seconds. +Distance extruded is 46558.4 mm. +Distance traveled is 58503.3 mm. +Extruder speed is 50.0 +Extruder was extruding 79.6 percent of the time. +Extruder was toggled 1688 times. +Operating flow rate is 9.8 mm3/s. +Feed rate average is 51.9 mm/s, (3113.8 mm/min). + +Filament +Cross section area is 0.2 mm2. +Extrusion diameter is 0.5 mm. +Extrusion fill density ratio is 0.68 + +Material +Mass extruded is 9.8 grams. +Volume extruded is 9.1 cc. + +Meta +Text has 33738 lines and a size of 1239.0 KB. +Version is 11.09.28 + +Procedures +carve +bottom +preface +inset +fill +multiply +speed +temperature +raft +skirt +dimension +bookend + +Profile +UM-PLA-HighQuality + +Slice +Edge width is 0.72 mm. +Layer height is 0.4 mm. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import cStringIO +import math +import sys + + +__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 getNewRepository(): + 'Get new repository.' + return StatisticRepository() + +def getWindowAnalyzeFile(fileName): + "Write statistics for a gcode file." + return getWindowAnalyzeFileGivenText( fileName, archive.getFileText(fileName) ) + +def getWindowAnalyzeFileGivenText( fileName, gcodeText, repository=None): + "Write statistics for a gcode file." + print('') + print('') + print('Statistics are being generated for the file ' + archive.getSummarizedFileName(fileName) ) + if repository == None: + repository = settings.getReadRepository( StatisticRepository() ) + skein = StatisticSkein() + statisticGcode = skein.getCraftedGcode(gcodeText, repository) + if repository.printStatistics.value: + print(statisticGcode) + if repository.saveStatistics.value: + archive.writeFileMessageEnd('.txt', fileName, statisticGcode, 'The statistics file is saved as ') + +def writeOutput(fileName, fileNamePenultimate, fileNameSuffix, filePenultimateWritten, gcodeText=''): + "Write statistics for a skeinforge gcode file, if 'Write Statistics File for Skeinforge Chain' is selected." + repository = settings.getReadRepository( StatisticRepository() ) + if gcodeText == '': + gcodeText = archive.getFileText( fileNameSuffix ) + if repository.activateStatistic.value: + getWindowAnalyzeFileGivenText( fileNameSuffix, gcodeText, repository ) + + +class StatisticRepository: + "A class to handle the statistics settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.analyze_plugins.statistic.html', self) + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Statistic') + self.activateStatistic = settings.BooleanSetting().getFromValue('Activate Statistic', self, True ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Cost -', self ) + self.machineTime = settings.FloatSpin().getFromValue( 0.0, 'Machine Time ($/hour):', self, 5.0, 1.0 ) + self.material = settings.FloatSpin().getFromValue( 0.0, 'Material ($/kg):', self, 40.0, 20.0 ) + settings.LabelSeparator().getFromRepository(self) + self.density = settings.FloatSpin().getFromValue( 500.0, 'Density (kg/m3):', self, 2000.0, 930.0 ) + self.extrusionDiameterOverThickness = settings.FloatSpin().getFromValue( 1.0, 'Extrusion Diameter over Thickness (ratio):', self, 1.5, 1.25 ) + self.fileNameInput = settings.FileNameInput().getFromFileName( [ ('Gcode text files', '*.gcode') ], 'Open File to Generate Statistics for', self, '') + self.printStatistics = settings.BooleanSetting().getFromValue('Print Statistics', self, True ) + self.saveStatistics = settings.BooleanSetting().getFromValue('Save Statistics', self, False ) + self.executeTitle = 'Generate Statistics' + + def execute(self): + "Write button has been clicked." + fileNames = skeinforge_polyfile.getFileOrGcodeDirectory( self.fileNameInput.value, self.fileNameInput.wasCancelled, ['_comment'] ) + for fileName in fileNames: + getWindowAnalyzeFile(fileName) + + +class StatisticSkein: + "A class to get statistics for a gcode skein." + def __init__(self): + self.extrusionDiameter = None + self.oldLocation = None + self.operatingFeedRatePerSecond = None + self.output = cStringIO.StringIO() + self.profileName = None + self.version = None + + def addLine(self, line): + "Add a line of text and a newline to the output." + self.output.write(line + '\n') + + def addToPath(self, location): + "Add a point to travel and maybe extrusion." + if self.oldLocation != None: + travel = location.distance( self.oldLocation ) + if self.feedRateMinute > 0.0: + self.totalBuildTime += 60.0 * travel / self.feedRateMinute + self.totalDistanceTraveled += travel + if self.extruderActive: + self.totalDistanceExtruded += travel + self.cornerMaximum.maximize(location) + self.cornerMinimum.minimize(location) + self.oldLocation = location + + def extruderSet( self, active ): + "Maybe increment the number of times the extruder was toggled." + if self.extruderActive != active: + self.extruderToggled += 1 + self.extruderActive = active + + def getCraftedGcode(self, gcodeText, repository): + "Parse gcode text and store the statistics." + self.absoluteEdgeWidth = 0.4 + self.characters = 0 + self.cornerMaximum = Vector3(-987654321.0, -987654321.0, -987654321.0) + self.cornerMinimum = Vector3(987654321.0, 987654321.0, 987654321.0) + self.extruderActive = False + self.extruderSpeed = None + self.extruderToggled = 0 + self.feedRateMinute = 600.0 + self.layerHeight = 0.4 + self.numberOfLines = 0 + self.procedures = [] + self.repository = repository + self.totalBuildTime = 0.0 + self.totalDistanceExtruded = 0.0 + self.totalDistanceTraveled = 0.0 + lines = archive.getTextLines(gcodeText) + for line in lines: + self.parseLine(line) + averageFeedRate = self.totalDistanceTraveled / self.totalBuildTime + self.characters += self.numberOfLines + kilobytes = round( self.characters / 1024.0 ) + halfEdgeWidth = 0.5 * self.absoluteEdgeWidth + halfExtrusionCorner = Vector3( halfEdgeWidth, halfEdgeWidth, halfEdgeWidth ) + self.cornerMaximum += halfExtrusionCorner + self.cornerMinimum -= halfExtrusionCorner + extent = self.cornerMaximum - self.cornerMinimum + roundedHigh = euclidean.getRoundedPoint( self.cornerMaximum ) + roundedLow = euclidean.getRoundedPoint( self.cornerMinimum ) + roundedExtent = euclidean.getRoundedPoint( extent ) + axisString = " axis extrusion starts at " + crossSectionArea = 0.9 * self.absoluteEdgeWidth * self.layerHeight # 0.9 if from the typical fill density + if self.extrusionDiameter != None: + crossSectionArea = math.pi / 4.0 * self.extrusionDiameter * self.extrusionDiameter + volumeExtruded = 0.001 * crossSectionArea * self.totalDistanceExtruded + mass = volumeExtruded / repository.density.value + machineTimeCost = repository.machineTime.value * self.totalBuildTime / 3600.0 + materialCost = repository.material.value * mass + self.addLine(' ') + self.addLine('Cost') + self.addLine( "Machine time cost is %s$." % round( machineTimeCost, 2 ) ) + self.addLine( "Material cost is %s$." % round( materialCost, 2 ) ) + self.addLine( "Total cost is %s$." % round( machineTimeCost + materialCost, 2 ) ) + self.addLine(' ') + self.addLine('Extent') + self.addLine( "X%s%s mm and ends at %s mm, for a width of %s mm." % ( axisString, int( roundedLow.x ), int( roundedHigh.x ), int( extent.x ) ) ) + self.addLine( "Y%s%s mm and ends at %s mm, for a depth of %s mm." % ( axisString, int( roundedLow.y ), int( roundedHigh.y ), int( extent.y ) ) ) + self.addLine( "Z%s%s mm and ends at %s mm, for a height of %s mm." % ( axisString, int( roundedLow.z ), int( roundedHigh.z ), int( extent.z ) ) ) + self.addLine(' ') + self.addLine('Extruder') + self.addLine( "Build time is %s." % euclidean.getDurationString( self.totalBuildTime ) ) + self.addLine( "Distance extruded is %s mm." % euclidean.getThreeSignificantFigures( self.totalDistanceExtruded ) ) + self.addLine( "Distance traveled is %s mm." % euclidean.getThreeSignificantFigures( self.totalDistanceTraveled ) ) + if self.extruderSpeed != None: + self.addLine( "Extruder speed is %s" % euclidean.getThreeSignificantFigures( self.extruderSpeed ) ) + self.addLine( "Extruder was extruding %s percent of the time." % euclidean.getThreeSignificantFigures( 100.0 * self.totalDistanceExtruded / self.totalDistanceTraveled ) ) + self.addLine( "Extruder was toggled %s times." % self.extruderToggled ) + if self.operatingFeedRatePerSecond != None: + flowRate = crossSectionArea * self.operatingFeedRatePerSecond + self.addLine( "Operating flow rate is %s mm3/s." % euclidean.getThreeSignificantFigures( flowRate ) ) + self.addLine( "Feed rate average is %s mm/s, (%s mm/min)." % ( euclidean.getThreeSignificantFigures( averageFeedRate ), euclidean.getThreeSignificantFigures( 60.0 * averageFeedRate ) ) ) + self.addLine(' ') + self.addLine('Filament') + self.addLine( "Cross section area is %s mm2." % euclidean.getThreeSignificantFigures( crossSectionArea ) ) + if self.extrusionDiameter != None: + self.addLine( "Extrusion diameter is %s mm." % euclidean.getThreeSignificantFigures( self.extrusionDiameter ) ) + self.addLine('Extrusion fill density ratio is %s' % euclidean.getThreeSignificantFigures( crossSectionArea / self.absoluteEdgeWidth / self.layerHeight ) ) + self.addLine(' ') + self.addLine('Material') + self.addLine( "Mass extruded is %s grams." % euclidean.getThreeSignificantFigures( 1000.0 * mass ) ) + self.addLine( "Volume extruded is %s cc." % euclidean.getThreeSignificantFigures( volumeExtruded ) ) + self.addLine(' ') + self.addLine('Meta') + self.addLine( "Text has %s lines and a size of %s KB." % ( self.numberOfLines, kilobytes ) ) + if self.version != None: + self.addLine( "Version is " + self.version ) + self.addLine(' ') + self.addLine( "Procedures" ) + for procedure in self.procedures: + self.addLine(procedure) + if self.profileName != None: + self.addLine(' ') + self.addLine( 'Profile' ) + self.addLine(self.profileName) + self.addLine(' ') + self.addLine('Slice') + self.addLine( "Edge width is %s mm." % euclidean.getThreeSignificantFigures( self.absoluteEdgeWidth ) ) + self.addLine( "Layer height is %s mm." % euclidean.getThreeSignificantFigures( self.layerHeight ) ) + self.addLine(' ') + return self.output.getvalue() + + def getLocationSetFeedRateToSplitLine( self, splitLine ): + "Get location ans set feed rate to the plsit line." + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + indexOfF = gcodec.getIndexOfStartingWithSecond( "F", splitLine ) + if indexOfF > 0: + self.feedRateMinute = gcodec.getDoubleAfterFirstLetter( splitLine[indexOfF] ) + return location + + def helicalMove( self, isCounterclockwise, splitLine ): + "Get statistics for a helical move." + if self.oldLocation == None: + return + location = self.getLocationSetFeedRateToSplitLine(splitLine) + location += self.oldLocation + center = self.oldLocation.copy() + indexOfR = gcodec.getIndexOfStartingWithSecond( "R", splitLine ) + if indexOfR > 0: + radius = gcodec.getDoubleAfterFirstLetter( splitLine[ indexOfR ] ) + halfLocationMinusOld = location - self.oldLocation + halfLocationMinusOld *= 0.5 + halfLocationMinusOldLength = halfLocationMinusOld.magnitude() + centerMidpointDistanceSquared = radius * radius - halfLocationMinusOldLength * halfLocationMinusOldLength + centerMidpointDistance = math.sqrt( max( centerMidpointDistanceSquared, 0.0 ) ) + centerMinusMidpoint = euclidean.getRotatedWiddershinsQuarterAroundZAxis( halfLocationMinusOld ) + centerMinusMidpoint.normalize() + centerMinusMidpoint *= centerMidpointDistance + if isCounterclockwise: + center.setToVector3( halfLocationMinusOld + centerMinusMidpoint ) + else: + center.setToVector3( halfLocationMinusOld - centerMinusMidpoint ) + else: + center.x = gcodec.getDoubleForLetter( "I", splitLine ) + center.y = gcodec.getDoubleForLetter( "J", splitLine ) + curveSection = 0.5 + center += self.oldLocation + afterCenterSegment = location - center + beforeCenterSegment = self.oldLocation - center + afterCenterDifferenceAngle = euclidean.getAngleAroundZAxisDifference( afterCenterSegment, beforeCenterSegment ) + absoluteDifferenceAngle = abs( afterCenterDifferenceAngle ) + steps = int( round( 0.5 + max( absoluteDifferenceAngle * 2.4, absoluteDifferenceAngle * beforeCenterSegment.magnitude() / curveSection ) ) ) + stepPlaneAngle = euclidean.getWiddershinsUnitPolar( afterCenterDifferenceAngle / steps ) + zIncrement = ( afterCenterSegment.z - beforeCenterSegment.z ) / float( steps ) + for step in xrange( 1, steps ): + beforeCenterSegment = euclidean.getRoundZAxisByPlaneAngle( stepPlaneAngle, beforeCenterSegment ) + beforeCenterSegment.z += zIncrement + arcPoint = center + beforeCenterSegment + self.addToPath( arcPoint ) + self.addToPath( location ) + + def linearMove( self, splitLine ): + "Get statistics for a linear move." + location = self.getLocationSetFeedRateToSplitLine(splitLine) + self.addToPath( location ) + + def parseLine(self, line): + "Parse a gcode line and add it to the statistics." + self.characters += len(line) + self.numberOfLines += 1 + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + self.linearMove(splitLine) + elif firstWord == 'G2': + self.helicalMove( False, splitLine ) + elif firstWord == 'G3': + self.helicalMove( True, splitLine ) + elif firstWord == 'M101': + self.extruderSet( True ) + elif firstWord == 'M102': + self.extruderSet( False ) + elif firstWord == 'M103': + self.extruderSet( False ) + elif firstWord == 'M108': + self.extruderSpeed = gcodec.getDoubleAfterFirstLetter(splitLine[1]) + elif firstWord == '(': + self.layerHeight = float(splitLine[1]) + self.extrusionDiameter = self.repository.extrusionDiameterOverThickness.value * self.layerHeight + elif firstWord == '(': + self.operatingFeedRatePerSecond = float(splitLine[1]) + elif firstWord == '(': + self.absoluteEdgeWidth = abs(float(splitLine[1])) + elif firstWord == '(': + self.procedures.append(splitLine[1]) + elif firstWord == '(': + self.profileName = line.replace('(', '').replace(')', '').strip() + elif firstWord == '(': + self.version = splitLine[1] + + +def main(): + "Display the statistics dialog." + if len(sys.argv) > 1: + getWindowAnalyzeFile(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/synopsis.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/synopsis.py new file mode 100644 index 0000000..220478e --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/synopsis.py @@ -0,0 +1,204 @@ +""" +This page is in the table of contents. +Synopsis is an analyze plugin to export the profile from a skeinforge gcode file as a csv or zip file. + +The synopsis manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Synopsis + +==Operation== +The default 'Activate Synopsis' checkbox is off. When it is on, the functions described below will work when called from the skeinforge toolchain, when it is off, the functions will not be called from the toolchain. The functions will still be called, whether or not the 'Activate Synopsis' checkbox is on, when synopsis is run directly. + +==Settings== +===Export Profile As CSV File=== +Default is on. + +If 'Export Profile As CSV File' is selected, the profile from a skeinforge gcode file with comments will be exported as a csv (comma separated values) file. + +===Export Profile As Zip File=== +Default is off. + +If 'Export Profile As Zip File' is selected, the profile from a skeinforge gcode file with comments will be exported as a zip file. + +==Examples== +Below are examples of synopsis being used. These examples are run in a terminal in the folder which contains Screw Holder_penultimate.gcode and synopsis.py. + +> python synopsis.py +This brings up the synopsis dialog. + +> python synopsis.py Screw Holder_penultimate.gcode +The synopsis file is saved as Screw_Holder_penultimate_synopsis.csv + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import os +import sys +import time +import zipfile + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Gary Hodgson ' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def addAbridgedSettings(abridgedSettings, repositoryWriter): + 'Add the abridged settings to a repository writer.' + for abridgedSetting in abridgedSettings: + repositoryWriter.write('%s\n' % abridgedSetting.__repr__()) + +def exportProfileAsCSVFile(abridgedSettings, suffixFileNameWithoutExtension): + 'Export the profile from the gcode text as a csv file.' + if len(abridgedSettings) < 1: + print('Warning, the synopsis csv file could not be generated because there are no setting comments in the file.') + return + repositoryWriter = settings.getRepositoryWriter('profile') + suffixFileName = suffixFileNameWithoutExtension + 'csv' + addAbridgedSettings(abridgedSettings, repositoryWriter) + archive.writeFileText(suffixFileName, repositoryWriter.getvalue()) + print('The synopsis csv file is saved as ' + archive.getSummarizedFileName(suffixFileName)) + +def exportProfileAsZipFile(abridgedSettings, suffixDirectoryPath, suffixFileNameWithoutExtension): + 'Export the profile from the gcode text as a zip file.' + if len(abridgedSettings) < 1: + print('Warning, the synopsis zip file could not be generated because there are no setting comments in the file.') + return + suffixFileName = suffixFileNameWithoutExtension + 'zip' + abridgedSettingsDictionary = {} + for abridgedSetting in abridgedSettings: + euclidean.addElementToListDictionary(abridgedSetting, abridgedSetting.procedure, abridgedSettingsDictionary) + abridgedSettingFileNamePaths = [] + for abridgedSettingsKey in abridgedSettingsDictionary: + abridgedSettings = abridgedSettingsDictionary[abridgedSettingsKey] + repositoryWriter = settings.getRepositoryWriter(abridgedSettingsKey) + addAbridgedSettings(abridgedSettings, repositoryWriter) + abridgedSettingFileNamePath = FileNamePath(suffixDirectoryPath, abridgedSettingsKey + '.csv') + abridgedSettingFileNamePaths.append(abridgedSettingFileNamePath) + archive.writeFileText(abridgedSettingFileNamePath.path, repositoryWriter.getvalue()) + time.sleep(0.2) # the sleep is so that the file system is sure to be consistent + zipArchive = zipfile.ZipFile(suffixFileName, 'w', compression=zipfile.ZIP_DEFLATED) + for abridgedSettingFileNamePath in abridgedSettingFileNamePaths: + zipArchive.write(abridgedSettingFileNamePath.path, abridgedSettingFileNamePath.fileName) + zipArchive.close() + time.sleep(0.2) # the sleep is so that the file system is sure to be consistent + for abridgedSettingFileNamePath in abridgedSettingFileNamePaths: + os.remove(abridgedSettingFileNamePath.path) + print('The synopsis zip file is saved as ' + archive.getSummarizedFileName(suffixFileName)) + +def getAbridgedSettings(gcodeText): + 'Get the abridged settings from the gcode text.' + abridgedSettings = [] + lines = archive.getTextLines(gcodeText) + settingsStart = False + for line in lines: + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == '(' and settingsStart: + if len(splitLine) > 4: + abridgedSettings.append(AbridgedSetting(splitLine)) + elif firstWord == '()': + settingsStart = True + elif firstWord == '()': + return abridgedSettings + return [] + +def getNewRepository(): + 'Get new repository.' + return SynopsisRepository() + +def getWindowAnalyzeFile(fileName): + 'Write scalable vector graphics for a gcode file.' + gcodeText = archive.getFileText(fileName) + return getWindowAnalyzeFileGivenText(fileName, gcodeText) + +def getWindowAnalyzeFileGivenText(fileName, gcodeText, repository=None): + 'Write scalable vector graphics for a gcode file given the settings.' + if gcodeText == '': + return None + if repository == None: + repository = settings.getReadRepository(SynopsisRepository()) + startTime = time.time() + suffixFileNameWithoutExtension = fileName[: fileName.rfind('.')] + '_synopsis.' + suffixDirectoryPath = os.path.dirname(suffixFileNameWithoutExtension) + suffixReplacedBaseNameWithoutExtension = os.path.basename(suffixFileNameWithoutExtension).replace(' ', '_') + suffixFileNameWithoutExtension = os.path.join(suffixDirectoryPath, suffixReplacedBaseNameWithoutExtension) + abridgedSettings = getAbridgedSettings(gcodeText) + if repository.exportProfileAsCSVFile.value: + exportProfileAsCSVFile(abridgedSettings, suffixFileNameWithoutExtension) + if repository.exportProfileAsZipFile.value: + exportProfileAsZipFile(abridgedSettings, suffixDirectoryPath, suffixFileNameWithoutExtension) + print('It took %s for synopsis to analyze the file.' % euclidean.getDurationString(time.time() - startTime)) + +def writeOutput(fileName, fileNamePenultimate, fileNameSuffix, filePenultimateWritten, gcodeText=''): + 'Write scalable vector graphics for a skeinforge gcode file, if activate synopsis is selected.' + repository = settings.getReadRepository( SynopsisRepository() ) + if not repository.activateSynopsis.value: + return + gcodeText = archive.getTextIfEmpty( fileNameSuffix, gcodeText ) + getWindowAnalyzeFileGivenText( fileNameSuffix, gcodeText, repository ) + + +class AbridgedSetting: + 'A class to handle an abridged setting.' + def __init__(self, splitLine): + 'Initialize.' + self.procedure = splitLine[1] + self.name = splitLine[2].replace('_', ' ') + self.value = ' '.join(splitLine[3 : -1]) + + def __repr__(self): + 'Get the tab separated representation of this AbridgedSetting.' + return '%s\t%s\t%s' % (self.procedure, self.name, self.value) + + +class FileNamePath: + 'A class to handle a file name and path.' + def __init__(self, directoryName, fileName): + 'Initialize.' + self.fileName = fileName + self.path = os.path.join(directoryName, fileName) + + def __repr__(self): + 'Get the tab separated representation of this FileNamePath.' + return '%s\t%s' % (self.fileName, self.path) + + +class SynopsisRepository: + 'A class to handle the synopsis settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.analyze_plugins.synopsis.html', self ) + self.activateSynopsis = settings.BooleanSetting().getFromValue('Activate Synopsis', self, False ) + self.fileNameInput = settings.FileNameInput().getFromFileName( [ ('Gcode text files', '*.gcode') ], 'Open File to Write Synopsis for', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Synopsis') + self.exportProfileAsCSVFile = settings.BooleanSetting().getFromValue('Export Profile As CSV File', self, True) + self.exportProfileAsZipFile = settings.BooleanSetting().getFromValue('Export Profile As Zip File', self, False) + self.executeTitle = 'Synopsis' + + def execute(self): + 'Write button has been clicked.' + fileNames = skeinforge_polyfile.getFileOrGcodeDirectory( self.fileNameInput.value, self.fileNameInput.wasCancelled ) + for fileName in fileNames: + getWindowAnalyzeFile(fileName) + + +def main(): + 'Display the synopsis dialog.' + if len(sys.argv) > 1: + getWindowAnalyzeFile(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/vectorwrite.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/vectorwrite.py new file mode 100644 index 0000000..ab1a903 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/analyze_plugins/vectorwrite.py @@ -0,0 +1,359 @@ +""" +This page is in the table of contents. +Vectorwrite is a very interesting analyze plugin that will create an SVG vector image for each layer that you can then use in some other printing system. + +The Scalable Vector Graphics file can be opened by an SVG viewer or an SVG capable browser like Mozilla: +http://www.mozilla.com/firefox/ + +The vectorwrite manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Vectorwrite + +==Operation== +The default 'Activate Vectorwrite' checkbox is off. When it is on, the functions described below will work when called from the skeinforge toolchain, when it is off, the functions will not be called from the toolchain. The functions will still be called, whether or not the 'Activate Vectorwrite' checkbox is on, when vectorwrite is run directly. + +==Settings== +===Add Loops=== +Default is on. + +If 'Add Loops' is selected, the loops will be added in yellow to the the scalable vector graphics output. + +===Add Paths=== +Default is on. + +If 'Add Paths' is selected, the paths will be added in pink to the the scalable vector graphics output. + +===Add Perimeters=== +Default is on. + +If 'Add Perimeters' is selected, the edges will be added to the the scalable vector graphics output. The outer edges will be red and the inner edges will be orange. + +===Layers=== +====Layers From==== +Default is zero. + +The "Layers From" is the index of the bottom layer that will be displayed. If the layer from is the default zero, the display will start from the lowest layer. If the the layer from index is negative, then the display will start from the layer from index below the top layer. + +====Layers To==== +Default is a huge number, which will be limited to the highest index layer. + +The "Layers To" is the index of the top layer that will be displayed. If the layer to index is a huge number like the default, the display will go to the top of the model, at least until we model habitats:) If the layer to index is negative, then the display will go to the layer to index below the top layer. The layer from until layer to index is a python slice. + +===SVG Viewer=== +Default is webbrowser. + +If the 'SVG Viewer' is set to the default 'webbrowser', the scalable vector graphics file will be sent to the default browser to be opened. If the 'SVG Viewer' is set to a program name, the scalable vector graphics file will be sent to that program to be opened. + +==Examples== +Below are examples of vectorwrite being used. These examples are run in a terminal in the folder which contains Screw Holder_penultimate.gcode and vectorwrite.py. + +> python vectorwrite.py +This brings up the vectorwrite dialog. + +> python vectorwrite.py Screw Holder_penultimate.gcode +The vectorwrite file is saved as Screw_Holder_penultimate_vectorwrite.svg + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from fabmetheus_utilities import svg_writer +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import cStringIO +import os +import sys +import time + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Nophead ' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getNewRepository(): + 'Get new repository.' + return VectorwriteRepository() + +def getWindowAnalyzeFile(fileName): + 'Write scalable vector graphics for a gcode file.' + gcodeText = archive.getFileText(fileName) + return getWindowAnalyzeFileGivenText(fileName, gcodeText) + +def getWindowAnalyzeFileGivenText( fileName, gcodeText, repository=None): + 'Write scalable vector graphics for a gcode file given the settings.' + if gcodeText == '': + return None + if repository == None: + repository = settings.getReadRepository( VectorwriteRepository() ) + startTime = time.time() + vectorwriteGcode = VectorwriteSkein().getCarvedSVG( fileName, gcodeText, repository ) + if vectorwriteGcode == '': + return None + suffixFileName = fileName[ : fileName.rfind('.') ] + '_vectorwrite.svg' + suffixDirectoryName = os.path.dirname(suffixFileName) + suffixReplacedBaseName = os.path.basename(suffixFileName).replace(' ', '_') + suffixFileName = os.path.join( suffixDirectoryName, suffixReplacedBaseName ) + archive.writeFileText( suffixFileName, vectorwriteGcode ) + print('The vectorwrite file is saved as ' + archive.getSummarizedFileName(suffixFileName) ) + print('It took %s to vectorwrite the file.' % euclidean.getDurationString( time.time() - startTime ) ) + settings.openSVGPage( suffixFileName, repository.svgViewer.value ) + +def writeOutput(fileName, fileNamePenultimate, fileNameSuffix, filePenultimateWritten, gcodeText=''): + 'Write scalable vector graphics for a skeinforge gcode file, if activate vectorwrite is selected.' + repository = settings.getReadRepository( VectorwriteRepository() ) + if not repository.activateVectorwrite.value: + return + gcodeText = archive.getTextIfEmpty( fileNameSuffix, gcodeText ) + getWindowAnalyzeFileGivenText( fileNameSuffix, gcodeText, repository ) + + +class SVGWriterVectorwrite( svg_writer.SVGWriter ): + 'A class to vectorwrite a carving.' + def addPaths( self, colorName, paths, transformString ): + 'Add paths to the output.' + pathString = '' + for path in paths: + pathString += self.getSVGStringForPath(path) + ' ' + if len( pathString ) < 1: + return + pathElementNodeCopy = self.pathElementNode.getCopy('', self.pathElementNode.parentNode ) + pathCopyDictionary = pathElementNodeCopy.attributes + pathCopyDictionary['d'] = pathString[ : - 1 ] + pathCopyDictionary['fill'] = 'none' + pathCopyDictionary['stroke'] = colorName + pathCopyDictionary['transform'] = transformString + + def addLoopLayerToOutput( self, layerIndex, threadLayer ): + 'Add rotated boundary layer to the output.' + settings.printProgress(self.layerIndex, 'vectorwrite') + self.addLayerBegin( layerIndex, threadLayer ) + transformString = self.getTransformString() + self.pathDictionary['d'] = self.getSVGStringForLoops( threadLayer.boundaryLoops ) + self.pathDictionary['transform'] = transformString + self.addPaths('#fa0', threadLayer.innerPerimeters, transformString ) #orange + self.addPaths('#ff0', threadLayer.loops, transformString ) #yellow + self.addPaths('#f00', threadLayer.outerPerimeters, transformString ) #red + self.addPaths('#f5c', threadLayer.paths, transformString ) #light violetred + + +class ThreadLayer: + 'Threads with a z.' + def __init__( self, z ): + self.boundaryLoops = [] + self.innerPerimeters = [] + self.loops = [] + self.outerPerimeters = [] + self.paths = [] + self.z = z + + def __repr__(self): + 'Get the string representation of this loop layer.' + return str(self.__dict__) + + def getTotalNumberOfThreads(self): + 'Get the total number of loops, paths and edges.' + return len(self.boundaryLoops) + len(self.innerPerimeters) + len(self.loops) + len(self.outerPerimeters) + len(self.paths) + + def maximize(self, vector3): + 'Maximize the vector3 over the loops, paths and edges.' + pointComplex = vector3.dropAxis() + pointComplex = euclidean.getMaximum(euclidean.getMaximumByComplexPaths(self.boundaryLoops), pointComplex) + pointComplex = euclidean.getMaximum(euclidean.getMaximumByComplexPaths(self.innerPerimeters), pointComplex) + pointComplex = euclidean.getMaximum(euclidean.getMaximumByComplexPaths(self.loops), pointComplex) + pointComplex = euclidean.getMaximum(euclidean.getMaximumByComplexPaths(self.outerPerimeters), pointComplex) + pointComplex = euclidean.getMaximum(euclidean.getMaximumByComplexPaths(self.paths), pointComplex) + vector3.setToXYZ(pointComplex.real, pointComplex.imag, max(self.z, vector3.z)) + + def minimize(self, vector3): + 'Minimize the vector3 over the loops, paths and edges.' + pointComplex = vector3.dropAxis() + pointComplex = euclidean.getMinimum(euclidean.getMinimumByComplexPaths(self.boundaryLoops), pointComplex) + pointComplex = euclidean.getMinimum(euclidean.getMinimumByComplexPaths(self.innerPerimeters), pointComplex) + pointComplex = euclidean.getMinimum(euclidean.getMinimumByComplexPaths(self.loops), pointComplex) + pointComplex = euclidean.getMinimum(euclidean.getMinimumByComplexPaths(self.outerPerimeters), pointComplex) + pointComplex = euclidean.getMinimum(euclidean.getMinimumByComplexPaths(self.paths), pointComplex) + vector3.setToXYZ(pointComplex.real, pointComplex.imag, min(self.z, vector3.z)) + + +class VectorwriteRepository: + 'A class to handle the vectorwrite settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.analyze_plugins.vectorwrite.html', self ) + self.activateVectorwrite = settings.BooleanSetting().getFromValue('Activate Vectorwrite', self, False ) + self.fileNameInput = settings.FileNameInput().getFromFileName( [ ('Gcode text files', '*.gcode') ], 'Open File to Write Vector Graphics for', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Vectorwrite') + self.addLoops = settings.BooleanSetting().getFromValue('Add Loops', self, True) + self.addPaths = settings.BooleanSetting().getFromValue('Add Paths', self, True) + self.addPerimeters = settings.BooleanSetting().getFromValue('Add Perimeters', self, True) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Layers -', self ) + self.layersFrom = settings.IntSpin().getFromValue( 0, 'Layers From (index):', self, 20, 0 ) + self.layersTo = settings.IntSpin().getSingleIncrementFromValue( 0, 'Layers To (index):', self, 912345678, 912345678 ) + settings.LabelSeparator().getFromRepository(self) + self.svgViewer = settings.StringSetting().getFromValue('SVG Viewer:', self, 'webbrowser') + settings.LabelSeparator().getFromRepository(self) + self.executeTitle = 'Vectorwrite' + + def execute(self): + 'Write button has been clicked.' + fileNames = skeinforge_polyfile.getFileOrGcodeDirectory( self.fileNameInput.value, self.fileNameInput.wasCancelled ) + for fileName in fileNames: + getWindowAnalyzeFile(fileName) + + +class VectorwriteSkein: + 'A class to vectorwrite a carving.' + def __init__(self): + 'Initialize.' + self.layerCount = settings.LayerCount() + + def addLoopLayer(self, z): + 'Add loop layer.' + self.layerCount.printProgressIncrement('vectorwrite') + self.threadLayer = ThreadLayer(z) + self.threadLayers.append(self.threadLayer) + + def addToLoops(self): + 'Add the thread to the loops.' + self.isLoop = False + if len(self.thread) < 1: + return + if self.repository.addLoops.value: + self.threadLayer.loops.append(self.thread) + self.thread = [] + + def addToPerimeters(self): + 'Add the thread to the edges.' + self.isEdge = False + if len(self.thread) < 1: + return + if self.repository.addPerimeters.value: + if self.isOuter: + self.threadLayer.outerPerimeters.append(self.thread) + else: + self.threadLayer.innerPerimeters.append(self.thread) + self.thread = [] + + def getCarvedSVG(self, fileName, gcodeText, repository): + 'Parse gnu triangulated surface text and store the vectorwrite gcode.' + cornerMaximum = Vector3(-987654321.0, -987654321.0, -987654321.0) + cornerMinimum = Vector3(987654321.0, 987654321.0, 987654321.0) + self.boundaryLoop = None + self.extruderActive = False + self.isEdge = False + self.isLoop = False + self.isOuter = False + self.lines = archive.getTextLines(gcodeText) + self.oldLocation = None + self.thread = [] + self.threadLayers = [] + self.repository = repository + self.parseInitialization() + for line in self.lines[self.lineIndex :]: + self.parseLine(line) + self.removeEmptyLayers() + for threadLayer in self.threadLayers: + threadLayer.maximize(cornerMaximum) + threadLayer.minimize(cornerMinimum) + halfLayerThickness = 0.5 * self.layerHeight + cornerMaximum.z += halfLayerThickness + cornerMinimum.z -= halfLayerThickness + svgWriter = SVGWriterVectorwrite( + True, cornerMaximum, cornerMinimum, self.decimalPlacesCarried, self.layerHeight, self.edgeWidth) + return svgWriter.getReplacedSVGTemplate(fileName, 'vectorwrite', self.threadLayers) + + def getCarveLayerHeight(self): + 'Get the layer height.' + return self.layerHeight + + def linearMove( self, splitLine ): + 'Get statistics for a linear move.' + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + if self.extruderActive: + if len(self.thread) == 0: + self.thread = [ self.oldLocation.dropAxis() ] + self.thread.append(location.dropAxis()) + self.oldLocation = location + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == '(': + self.decimalPlacesCarried = int(splitLine[1]) + elif firstWord == '(': + self.layerHeight = float(splitLine[1]) + elif firstWord == '()': + return + elif firstWord == '(': + self.edgeWidth = float(splitLine[1]) + + def parseLine(self, line): + 'Parse a gcode line and add it to the outset skein.' + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + self.linearMove(splitLine) + elif firstWord == 'M101': + self.extruderActive = True + elif firstWord == 'M103': + self.extruderActive = False + if self.isLoop: + self.addToLoops() + return + if self.isEdge: + self.addToPerimeters() + return + if self.repository.addPaths.value: + self.threadLayer.paths.append(self.thread) + self.thread = [] + elif firstWord == '()': + self.boundaryLoop = None + elif firstWord == '(': + location = gcodec.getLocationFromSplitLine(None, splitLine) + if self.boundaryLoop == None: + self.boundaryLoop = [] + self.threadLayer.boundaryLoops.append( self.boundaryLoop ) + self.boundaryLoop.append(location.dropAxis()) + elif firstWord == '(': + self.addLoopLayer(float(splitLine[1])) + elif firstWord == '()': + self.addToLoops() + elif firstWord == '(': + self.isLoop = True + elif firstWord == '(': + self.isEdge = True + self.isOuter = ( splitLine[1] == 'outer') + elif firstWord == '()': + self.addToPerimeters() + + def removeEmptyLayers(self): + 'Remove empty layers.' + for threadLayerIndex, threadLayer in enumerate(self.threadLayers): + if threadLayer.getTotalNumberOfThreads() > 0: + self.threadLayers = self.threadLayers[threadLayerIndex :] + return + + +def main(): + 'Display the vectorwrite dialog.' + if len(sys.argv) > 1: + getWindowAnalyzeFile(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft.py new file mode 100644 index 0000000..61b8103 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft.py @@ -0,0 +1,128 @@ +""" +This page is in the table of contents. +Craft is a script to access the plugins which craft a gcode file. + +The plugin buttons which are commonly used are bolded and the ones which are rarely used have normal font weight. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import os +import sys + + +__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 addSubmenus( menu, pluginFileName, pluginFolderPath, pluginPath ): + "Add a tool plugin menu." + submenu = settings.Tkinter.Menu( menu, tearoff = 0 ) + menu.add_cascade( label = pluginFileName.capitalize(), menu = submenu ) + settings.ToolDialog().addPluginToMenu( submenu, pluginPath ) + submenu.add_separator() + submenuFileNames = archive.getPluginFileNamesFromDirectoryPath( pluginFolderPath ) + for submenuFileName in submenuFileNames: + settings.ToolDialog().addPluginToMenu( submenu, os.path.join( pluginFolderPath, submenuFileName ) ) + +def addToCraftMenu( menu ): + "Add a craft plugin menu." + settings.ToolDialog().addPluginToMenu(menu, archive.getUntilDot(archive.getSkeinforgePluginsPath('craft.py'))) + menu.add_separator() + directoryPath = skeinforge_craft.getPluginsDirectoryPath() + directoryFolders = settings.getFolders(directoryPath) + pluginFileNames = skeinforge_craft.getPluginFileNames() + for pluginFileName in pluginFileNames: + pluginFolderName = pluginFileName + '_plugins' + pluginPath = os.path.join( directoryPath, pluginFileName ) + if pluginFolderName in directoryFolders: + addSubmenus( menu, pluginFileName, os.path.join( directoryPath, pluginFolderName ), pluginPath ) + else: + settings.ToolDialog().addPluginToMenu( menu, pluginPath ) + +def addToMenu( master, menu, repository, window ): + "Add a tool plugin menu." + CraftMenuSaveListener( menu, window ) + +def getNewRepository(): + 'Get new repository.' + return skeinforge_craft.CraftRepository() + +def writeOutput(fileName): + "Craft a gcode file." + return skeinforge_craft.writeOutput(fileName) + + +class CraftMenuSaveListener: + "A class to update a craft menu." + def __init__( self, menu, window ): + "Set the menu." + self.menu = menu + addToCraftMenu( menu ) + euclidean.addElementToListDictionaryIfNotThere( self, window, settings.globalProfileSaveListenerListTable ) + + def save(self): + "Profile has been saved and profile menu should be updated." + settings.deleteMenuItems( self.menu ) + addToCraftMenu( self.menu ) + + +class CraftRadioButtonsSaveListener: + "A class to update the craft radio buttons." + def addToDialog( self, gridPosition ): + "Add this to the dialog." + euclidean.addElementToListDictionaryIfNotThere( self, self.repository.repositoryDialog, settings.globalProfileSaveListenerListTable ) + self.gridPosition = gridPosition.getCopy() + self.gridPosition.increment() + self.gridPosition.row = gridPosition.rowStart + self.setRadioButtons() + + def getFromRadioPlugins( self, radioPlugins, repository ): + "Initialize." + self.name = 'CraftRadioButtonsSaveListener' + self.radioPlugins = radioPlugins + self.repository = repository + repository.displayEntities.append(self) + return self + + def save(self): + "Profile has been saved and craft radio plugins should be updated." + self.setRadioButtons() + + def setRadioButtons(self): + "Profile has been saved and craft radio plugins should be updated." + craftSequence = skeinforge_profile.getCraftTypePluginModule().getCraftSequence() + gridPosition = self.gridPosition.getCopy() + maximumValue = False + activeRadioPlugins = [] + for radioPlugin in self.radioPlugins: + if radioPlugin.name in craftSequence: + activeRadioPlugins.append( radioPlugin ) + radioPlugin.incrementGridPosition(gridPosition) + maximumValue = max( radioPlugin.value, maximumValue ) + else: + radioPlugin.radiobutton.grid_remove() + if not maximumValue: + selectedRadioPlugin = settings.getSelectedRadioPlugin( self.repository.importantFileNames + [ activeRadioPlugins[0].name ], activeRadioPlugins ).setSelect() + self.repository.pluginFrame.update() + + +def main(): + "Display the craft dialog." + if len(sys.argv) > 1: + settings.startMainLoopFromWindow(writeOutput(' '.join(sys.argv[1 :]))) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/__init__.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/__init__.py new file mode 100644 index 0000000..1121e8a --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 3 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/alteration.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/alteration.py new file mode 100644 index 0000000..be5eb4f --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/alteration.py @@ -0,0 +1,260 @@ +#! /usr/bin/env python +""" +This page is in the table of contents. +The alteration plugin adds the start and end files to the gcode. + +This plugin also removes the alteration prefix tokens from the alteration lines. Alteration lines have a prefix token so they can go through the craft plugins without being modified. However, the tokens are not recognized by the firmware so they have to be removed before export. The alteration token is: +() + +The alteration manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Alteration + +==Operation== +The default 'Activate Alteration' checkbox is on. When it is on, the functions described below will work, when it is off, nothing will be done. + +==Settings== +Alteration looks for alteration files in the alterations folder in the .skeinforge folder in the home directory. Alteration does not care if the text file names are capitalized, but some file systems do not handle file name cases properly, so to be on the safe side you should give them lower case names. If it doesn't find the file it then looks in the alterations folder in the skeinforge_plugins folder. + +===Name of End File=== +Default is 'end.gcode'. + +If there is a file with the name of the "Name of End File" setting, it will be added to the very end of the gcode. + +===Name of Start File=== +Default is 'start.gcode'. + +If there is a file with the name of the "Name of Start File" setting, it will be added to the very beginning of the gcode. + +===Remove Redundant Mcode=== +Default: True + +If 'Remove Redundant Mcode' is selected then M104 and M108 lines which are followed by a different value before there is a movement will be removed. For example, if there is something like: +M113 S1.0 +M104 S60.0 +( 0.72 ) +M104 S200.0 +() + +with Remove Redundant Mcode selected, that snippet would become: +M113 S1.0 +M104 S200.0 +( 0.72 ) +() + +This is a relatively safe procedure, the only reason it is optional is because someone might make an alteration file which, for some unknown reason, requires the redundant mcode. + +===Replace Variable with Setting=== +Default: True + +If 'Replace Variable with Setting' is selected and there is an alteration line with a setting token, the token will be replaced by the value. + +For example, if there is an alteration line like: + +M140 S + +the token would be replaced with the value and assuming the bed chamber was 60.0, the output would be: + +M140 S60.0 + +==Examples== +The following examples add the alteration information to the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and alteration.py. + +> python alteration.py +This brings up the alteration dialog. + +> python alteration.py Screw Holder Bottom.stl +The alteration tool is parsing the file: +Screw Holder Bottom.stl +.. +The alteration tool has created the file: +.. Screw Holder Bottom_alteration.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import archive +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import cStringIO +import sys + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCraftedText(fileName, text='', repository=None): + 'Alteration a gcode linear move text.' + return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository) + +def getCraftedTextFromText(gcodeText, repository=None): + 'Alteration a gcode linear move text.' + if gcodec.isProcedureDoneOrFileIsEmpty(gcodeText, 'alteration'): + return gcodeText + if repository == None: + repository = settings.getReadRepository(AlterationRepository()) + if not repository.activateAlteration.value: + return gcodeText + return AlterationSkein().getCraftedGcode(gcodeText, repository) + +def getGcodeTextWithoutRedundantMcode(gcodeText): + 'Get gcode text without redundant M104 and M108.' + lines = archive.getTextLines(gcodeText) + lines = getLinesWithoutRedundancy('M104', lines) + lines = getLinesWithoutRedundancy('M108', lines) + output = cStringIO.StringIO() + gcodec.addLinesToCString(output, lines) + return output.getvalue() + +def getLinesWithoutRedundancy(duplicateWord, lines): + 'Get gcode lines without redundant first words.' + oldDuplicationIndex = None + for lineIndex, line in enumerate(lines): + firstWord = gcodec.getFirstWordFromLine(line) + if firstWord == duplicateWord: + if oldDuplicationIndex == None: + oldDuplicationIndex = lineIndex + else: + lines[oldDuplicationIndex] = line + lines[lineIndex] = '' + elif firstWord.startswith('G') or firstWord == 'M101' or firstWord == 'M103': + oldDuplicationIndex = None + return lines + +def getNewRepository(): + 'Get new repository.' + return AlterationRepository() + +def writeOutput(fileName, shouldAnalyze=True): + 'Alteration a gcode linear move file. Chain alteration the gcode if the alteration procedure has not been done.' + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'alteration', shouldAnalyze) + + +class AlterationRepository: + "A class to handle the alteration settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.alteration.html', self ) + self.baseNameSynonym = 'bookend.csv' + self.fileNameInput = settings.FileNameInput().getFromFileName(fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Alteration', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Alteration') + self.activateAlteration = settings.BooleanSetting().getFromValue('Activate Alteration', self, True) + self.nameOfEndFile = settings.StringSetting().getFromValue('Name of End File:', self, 'end.gcode') + self.nameOfStartFile = settings.StringSetting().getFromValue('Name of Start File:', self, 'start.gcode') + self.removeRedundantMcode = settings.BooleanSetting().getFromValue('Remove Redundant Mcode', self, True) + self.replaceVariableWithSetting = settings.BooleanSetting().getFromValue('Replace Variable with Setting', self, True) + self.executeTitle = 'Alteration' + + def execute(self): + 'Alteration button has been clicked.' + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class AlterationSkein: + "A class to alteration a skein of extrusions." + def __init__(self): + 'Initialize.' + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.lineIndex = 0 + self.settingDictionary = None + + def addFromUpperLowerFile(self, fileName): + "Add lines of text from the fileName or the lowercase fileName, if there is no file by the original fileName in the directory." + alterationFileLines = settings.getAlterationFileLines(fileName) + self.distanceFeedRate.addLinesSetAbsoluteDistanceMode(alterationFileLines) + + def getCraftedGcode(self, gcodeText, repository): + "Parse gcode text and store the bevel gcode." + self.lines = archive.getTextLines(gcodeText) + if repository.replaceVariableWithSetting.value: + self.setSettingDictionary() + self.addFromUpperLowerFile(repository.nameOfStartFile.value) # Add a start file if it exists. + self.parseInitialization() + for self.lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[self.lineIndex] + self.distanceFeedRate.addLine(line) + self.addFromUpperLowerFile(repository.nameOfEndFile.value) # Add an end file if it exists. + gcodeText = self.getReplacedAlterationText() + if repository.removeRedundantMcode.value: + gcodeText = getGcodeTextWithoutRedundantMcode(gcodeText) + return gcodeText + + def getReplacedAlterationLine(self, alterationFileLine, searchIndex=0): + 'Get the alteration file line with variables replaced with the settings.' + settingIndex = alterationFileLine.find('setting.', searchIndex) + beginIndex = settingIndex - 1 + if beginIndex < 0: + return alterationFileLine + endBracketIndex = alterationFileLine.find('>', settingIndex) + if alterationFileLine[beginIndex] != '<' or endBracketIndex == -1: + return alterationFileLine + endIndex = endBracketIndex + 1 + innerToken = alterationFileLine[settingIndex + len('setting.'): endIndex].replace('>', '').replace(' ', '').replace('_', '').lower() + if innerToken in self.settingDictionary: + replacedSetting = self.settingDictionary[innerToken] + replacedAlterationLine = alterationFileLine[: beginIndex] + replacedSetting + alterationFileLine[endIndex :] + return self.getReplacedAlterationLine(replacedAlterationLine, beginIndex + len(replacedSetting)) + return alterationFileLine + + def getReplacedAlterationText(self): + 'Replace the alteration lines if there are settings.' + if self.settingDictionary == None: + return self.distanceFeedRate.output.getvalue().replace('()', '') + lines = archive.getTextLines(self.distanceFeedRate.output.getvalue()) + distanceFeedRate = gcodec.DistanceFeedRate() + for line in lines: + if line.startswith('()'): + line = self.getReplacedAlterationLine(line[len('()') :]) + distanceFeedRate.addLine(line) + return distanceFeedRate.output.getvalue() + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('alteration') + return + self.distanceFeedRate.addLine(line) + + def setSettingDictionary(self): + 'Set the setting dictionary from the gcode text.' + for line in self.lines: + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == '(' and self.settingDictionary != None: + if len(splitLine) > 4: + procedure = splitLine[1] + name = splitLine[2].replace('_', ' ').replace(' ', '') + if '(' in name: + name = name[: name.find('(')] + value = ' '.join(splitLine[3 : -1]) + self.settingDictionary[(procedure + '.' + name).lower()] = value + elif firstWord == '()': + self.settingDictionary = {} + elif firstWord == '()': + return + + +def main(): + "Display the alteration dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/bottom.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/bottom.py new file mode 100644 index 0000000..25e02ba --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/bottom.py @@ -0,0 +1,159 @@ +#! /usr/bin/env python +""" +This page is in the table of contents. +Bottom sets the bottom of the carving to the defined altitude. + +The bottom manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Bottom + +==Operation== +The default 'Activate Bottom' checkbox is on. When it is on, the functions described below will work, when it is off, the functions will not be called. + +==Settings== +===Additional Height over Layer Thickness=== +Default is half. + +The layers will start at the altitude plus the 'Additional Height over Layer Thickness' times the layer height. The default value of half means that the bottom layer is at the height of the bottom slice, because each slice is made through the middle of each layer. Raft expects the layers to start at an additional half layer height. You should only change 'Additional Height over Layer Thickness' if you are manipulating the skeinforge output with your own program which does not use the raft tool. + +===Altitude=== +Default is zero. + +Defines the altitude of the bottom of the model. The bottom slice has a z of the altitude plus the 'Additional Height over Layer Thickness' times the layer height. + +===SVG Viewer=== +Default is webbrowser. + +If the 'SVG Viewer' is set to the default 'webbrowser', the scalable vector graphics file will be sent to the default browser to be opened. If the 'SVG Viewer' is set to a program name, the scalable vector graphics file will be sent to that program to be opened. + +==Examples== +The following examples bottom the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and bottom.py. + +> python bottom.py +This brings up the bottom dialog. + +> python bottom.py Screw Holder Bottom.stl +The bottom tool is parsing the file: +Screw Holder Bottom.stl +.. +The bottom tool has created the file: +.. Screw Holder Bottom_bottom.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from datetime import date +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities.svg_reader import SVGReader +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from fabmetheus_utilities import svg_writer +from fabmetheus_utilities import xml_simple_writer +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import cStringIO +import os +import sys +import time + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCraftedText(fileName, svgText='', repository=None): + "Bottom and convert an svg file or svgText." + return getCraftedTextFromText(fileName, archive.getTextIfEmpty(fileName, svgText), repository) + +def getCraftedTextFromText(fileName, svgText, repository=None): + "Bottom and convert an svgText." + if gcodec.isProcedureDoneOrFileIsEmpty(svgText, 'bottom'): + return svgText + if repository == None: + repository = settings.getReadRepository(BottomRepository()) + if not repository.activateBottom.value: + return svgText + return BottomSkein().getCraftedGcode(fileName, repository, svgText) + +def getNewRepository(): + 'Get new repository.' + return BottomRepository() + +def writeOutput(fileName, shouldAnalyze=True): + 'Bottom the carving.' + skeinforge_craft.writeSVGTextWithNounMessage(fileName, BottomRepository(), shouldAnalyze) + + +class BottomRepository: + "A class to handle the bottom settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository( + 'skeinforge_application.skeinforge_plugins.craft_plugins.bottom.html', self) + self.fileNameInput = settings.FileNameInput().getFromFileName( + fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Bottom', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Bottom') + self.activateBottom = settings.BooleanSetting().getFromValue('Activate Bottom', self, True) + self.additionalHeightOverLayerThickness = settings.FloatSpin().getFromValue( + 0.0, 'Additional Height over Layer Thickness (ratio):', self, 1.0, 0.5) + self.altitude = settings.FloatSpin().getFromValue(-1.0, 'Altitude (mm):', self, 1.0, 0.0) + self.svgViewer = settings.StringSetting().getFromValue('SVG Viewer:', self, 'webbrowser') + self.executeTitle = 'Bottom' + + def execute(self): + "Bottom button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class BottomSkein: + "A class to bottom a skein of extrusions." + def getCraftedGcode(self, fileName, repository, svgText): + "Parse svgText and store the bottom svgText." + svgReader = SVGReader() + svgReader.parseSVG('', svgText) + if svgReader.sliceDictionary == None: + print('Warning, nothing will be done because the sliceDictionary could not be found getCraftedGcode in preface.') + return '' + decimalPlacesCarried = int(svgReader.sliceDictionary['decimalPlacesCarried']) + edgeWidth = float(svgReader.sliceDictionary['edgeWidth']) + layerHeight = float(svgReader.sliceDictionary['layerHeight']) + loopLayers = svgReader.loopLayers + zMinimum = 987654321.0 + for loopLayer in loopLayers: + zMinimum = min(loopLayer.z, zMinimum) + deltaZ = repository.altitude.value + repository.additionalHeightOverLayerThickness.value * layerHeight - zMinimum + for loopLayer in loopLayers: + loopLayer.z += deltaZ + cornerMaximum = Vector3(-912345678.0, -912345678.0, -912345678.0) + cornerMinimum = Vector3(912345678.0, 912345678.0, 912345678.0) + svg_writer.setSVGCarvingCorners(cornerMaximum, cornerMinimum, layerHeight, loopLayers) + svgWriter = svg_writer.SVGWriter( + True, + cornerMaximum, + cornerMinimum, + decimalPlacesCarried, + layerHeight, + edgeWidth) + commentElement = svg_writer.getCommentElement(svgReader.documentElement) + procedureNameString = svgReader.sliceDictionary['procedureName'] + ',bottom' + return svgWriter.getReplacedSVGTemplate(fileName, loopLayers, procedureNameString, commentElement) + + +def main(): + "Display the bottom dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/carve.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/carve.py new file mode 100644 index 0000000..7114caa --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/carve.py @@ -0,0 +1,224 @@ +""" +This page is in the table of contents. +Carve is the most important plugin to define for your printer. + +It carves a shape into svg slice layers. It also sets the layer height and edge width for the rest of the tool chain. + +The carve manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Carve + +On the Arcol Blog a method of deriving the layer height is posted. That article "Machine Calibrating" is at: +http://blog.arcol.hu/?p=157 + +==Settings== +===Add Layer Template to SVG=== +Default is on. + +When selected, the layer template will be added to the svg output, which adds javascript control boxes. So 'Add Layer Template to SVG' should be selected when the svg will be viewed in a browser. + +When off, no controls will be added, the svg output will only include the fabrication paths. So 'Add Layer Template to SVG' should be deselected when the svg will be used by other software, like Inkscape. + +===Edge Width over Height=== +Default is 1.8. + +Defines the ratio of the extrusion edge width to the layer height. This parameter tells skeinforge how wide the edge wall is expected to be in relation to the layer height. Default value of 1.8 for the default layer height of 0.4 states that a single filament edge wall should be 0.4 mm * 1.8 = 0.72 mm wide. The higher the value the more the edge will be inset. A ratio of one means the extrusion is a circle, the default ratio of 1.8 means the extrusion is a wide oval. + +This is an important value because if you are calibrating your machine you need to ensure that the speed of the head and the extrusion rate in combination produce a wall that is 'Layer Height' * 'Edge Width over Height' wide. To start with 'Edge Width over Height' is probably best left at the default of 1.8 and the extrusion rate adjusted to give the correct calculated wall thickness. + +Adjustment is in the 'Speed' section with 'Feed Rate' controlling speed of the head in X & Y and 'Flow Rate' controlling the extrusion rate. Initially it is probably easier to start adjusting the flow rate only a little at a time until you get a single filament of the correct width. If you change too many parameters at once you can get in a right mess. + +===Extra Decimal Places=== +Default is two. + +Defines the number of extra decimal places export will output compared to the number of decimal places in the layer height. The higher the 'Extra Decimal Places', the more significant figures the output numbers will have. + +===Import Coarseness=== +Default is one. + +When a triangle mesh has holes in it, the triangle mesh slicer switches over to a slow algorithm that spans gaps in the mesh. The higher the 'Import Coarseness' setting, the wider the gaps in the mesh it will span. An import coarseness of one means it will span gaps of the edge width. + +===Layer Height=== +Default is 0.4 mm. + +Defines the the height of the layers skeinforge will cut your object into, in the z direction. This is the most important carve setting, many values in the toolchain are derived from the layer height. + +For a 0.5 mm nozzle usable values are 0.3 mm to 0.5 mm. Note; if you are using thinner layers make sure to adjust the extrusion speed as well. + +===Layers=== +Carve slices from bottom to top. To get a single layer, set the "Layers From" to zero and the "Layers To" to one. The 'Layers From' until 'Layers To' range is a python slice. + +====Layers From==== +Default is zero. + +Defines the index of the bottom layer that will be carved. If the 'Layers From' is the default zero, the carving will start from the lowest layer. If the 'Layers From' index is negative, then the carving will start from the 'Layers From' index below the top layer. + +For example if your object is 5 mm tall and your layer thicknes is 1 mm if you set layers from to 3 you will ignore the first 3 mm and start from 3 mm. + +====Layers To==== +Default is a huge number, which will be limited to the highest index layer. + +Defines the index of the top layer that will be carved. If the 'Layers To' index is a huge number like the default, the carving will go to the top of the model. If the 'Layers To' index is negative, then the carving will go to the 'Layers To' index below the top layer. + +This is the same as layers from, only it defines when to end the generation of gcode. + +===Mesh Type=== +Default is 'Correct Mesh'. + +====Correct Mesh==== +When selected, the mesh will be accurately carved, and if a hole is found, carve will switch over to the algorithm that spans gaps. + +====Unproven Mesh==== +When selected, carve will use the gap spanning algorithm from the start. The problem with the gap spanning algothm is that it will span gaps, even if there is not actually a gap in the model. + +===SVG Viewer=== +Default is webbrowser. + +If the 'SVG Viewer' is set to the default 'webbrowser', the scalable vector graphics file will be sent to the default browser to be opened. If the 'SVG Viewer' is set to a program name, the scalable vector graphics file will be sent to that program to be opened. + +==Examples== +The following examples carve the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and carve.py. + +> python carve.py +This brings up the carve dialog. + +> python carve.py Screw Holder Bottom.stl +The carve tool is parsing the file: +Screw Holder Bottom.stl +.. +The carve tool has created the file: +.. Screw Holder Bottom_carve.svg + +""" + +from __future__ import absolute_import +try: + import psyco + psyco.full() +except: + pass +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from fabmetheus_utilities import svg_writer +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import os +import sys +import time + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCraftedText( fileName, gcodeText = '', repository=None): + "Get carved text." + if fileName.endswith('.svg'): + gcodeText = archive.getTextIfEmpty(fileName, gcodeText) + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'carve'): + return gcodeText + carving = svg_writer.getCarving(fileName) + if carving == None: + return '' + if repository == None: + repository = CarveRepository() + settings.getReadRepository(repository) + return CarveSkein().getCarvedSVG( carving, fileName, repository ) + +def getNewRepository(): + 'Get new repository.' + return CarveRepository() + +def writeOutput(fileName, shouldAnalyze=True): + "Carve a GNU Triangulated Surface file." + startTime = time.time() + print('File ' + archive.getSummarizedFileName(fileName) + ' is being carved.') + repository = CarveRepository() + settings.getReadRepository(repository) + carveGcode = getCraftedText(fileName, '', repository) + if carveGcode == '': + return + suffixFileName = archive.getFilePathWithUnderscoredBasename(fileName, '_carve.svg') + archive.writeFileText(suffixFileName, carveGcode) + print('The carved file is saved as ' + archive.getSummarizedFileName(suffixFileName)) + print('It took %s to carve the file.' % euclidean.getDurationString(time.time() - startTime)) + if shouldAnalyze: + settings.openSVGPage(suffixFileName, repository.svgViewer.value) + + +class CarveRepository: + "A class to handle the carve settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.carve.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getTranslatorFileTypeTuples(), 'Open File for Carve', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Carve') + self.addLayerTemplateToSVG = settings.BooleanSetting().getFromValue('Add Layer Template to SVG', self, True) + self.edgeWidth = settings.FloatSpin().getFromValue( 0.1, 'Edge Width (mm):', self, 2.2, 0.4 ) + self.extraDecimalPlaces = settings.FloatSpin().getFromValue(0.0, 'Extra Decimal Places (float):', self, 3.0, 2.0) + self.importCoarseness = settings.FloatSpin().getFromValue( 0.5, 'Import Coarseness (ratio):', self, 2.0, 1.0 ) + self.layerHeight = settings.FloatSpin().getFromValue( 0.1, 'Layer Height (mm):', self, 1.0, 0.2 ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Layers -', self ) + self.layersFrom = settings.IntSpin().getFromValue( 0, 'Layers From (index):', self, 20, 0 ) + self.layersTo = settings.IntSpin().getSingleIncrementFromValue( 0, 'Layers To (index):', self, 912345678, 912345678 ) + settings.LabelSeparator().getFromRepository(self) + self.meshTypeLabel = settings.LabelDisplay().getFromName('Mesh Type: ', self ) + importLatentStringVar = settings.LatentStringVar() + self.correctMesh = settings.Radio().getFromRadio( importLatentStringVar, 'Correct Mesh', self, True ) + self.unprovenMesh = settings.Radio().getFromRadio( importLatentStringVar, 'Unproven Mesh', self, False ) + self.svgViewer = settings.StringSetting().getFromValue('SVG Viewer:', self, 'webbrowser') + settings.LabelSeparator().getFromRepository(self) + self.executeTitle = 'Carve' + + def execute(self): + "Carve button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypes(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class CarveSkein: + "A class to carve a carving." + def getCarvedSVG(self, carving, fileName, repository): + "Parse gnu triangulated surface text and store the carved gcode." + layerHeight = repository.layerHeight.value + edgeWidth = repository.edgeWidth.value + carving.setCarveLayerHeight(layerHeight) + importRadius = 0.5 * repository.importCoarseness.value * abs(edgeWidth) + carving.setCarveImportRadius(max(importRadius, 0.001 * layerHeight)) + carving.setCarveIsCorrectMesh(repository.correctMesh.value) + loopLayers = carving.getCarveBoundaryLayers() + if len(loopLayers) < 1: + print('Warning, there are no slices for the model, this could be because the model is too small for the Layer Height.') + return '' + layerHeight = carving.getCarveLayerHeight() + decimalPlacesCarried = euclidean.getDecimalPlacesCarried(repository.extraDecimalPlaces.value, layerHeight) + edgeWidth = repository.edgeWidth.value + svgWriter = svg_writer.SVGWriter( + repository.addLayerTemplateToSVG.value, + carving.getCarveCornerMaximum(), + carving.getCarveCornerMinimum(), + decimalPlacesCarried, + carving.getCarveLayerHeight(), + edgeWidth) + truncatedRotatedBoundaryLayers = svg_writer.getTruncatedRotatedBoundaryLayers(loopLayers, repository) + return svgWriter.getReplacedSVGTemplate(fileName, truncatedRotatedBoundaryLayers, 'carve', carving.getFabmetheusXML()) + + +def main(): + "Display the carve dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/chamber.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/chamber.py new file mode 100644 index 0000000..ce54b11 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/chamber.py @@ -0,0 +1,300 @@ +""" +This page is in the table of contents. +Some filaments contract too much and warp the extruded object. To prevent this you have to print the object in a temperature regulated chamber and/or on a temperature regulated bed. The chamber tool allows you to control the bed and chamber temperature and the holding pressure. + +The chamber gcodes are also described at: + +http://reprap.org/wiki/Mendel_User_Manual:_RepRapGCodes + +The chamber manual page is at: + +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Chamber + +==Operation== +The default 'Activate Chamber' checkbox is on. When it is on, the functions described below will work, when it is off, nothing will be done. + +==Settings== +===Bed=== +The initial bed temperature is defined by 'Bed Temperature'. If the 'Bed Temperature End Change Height' is greater or equal to the 'Bed Temperature Begin Change Height' and the 'Bed Temperature Begin Change Height' is greater or equal to zero, then the temperature will be ramped toward the 'Bed Temperature End'. The ramp will start once the extruder reaches the 'Bed Temperature Begin Change Height', then the bed temperature will approach the 'Bed Temperature End' as the extruder reaches the 'Bed Temperature End Change Height', finally the bed temperature will stay at the 'Bed Temperature End' for the remainder of the build. + +====Bed Temperature==== +Default: 60C + +Defines the initial print bed temperature in Celcius by adding an M140 command. + +====Bed Temperature Begin Change Height==== +Default: -1 mm + +Defines the height of the beginning of the temperature ramp. If the 'Bed Temperature End Change Height' is less than zero, the bed temperature will remain at the initial 'Bed Temperature'. + +====Bed Temperature End Change Height==== +Default: -1 mm + +Defines the height of the end of the temperature ramp. If the 'Bed Temperature End Change Height' is less than zero or less than the 'Bed Temperature Begin Change Height', the bed temperature will remain at the initial 'Bed Temperature'. + +====Bed Temperature End==== +Default: 20C + +Defines the end bed temperature if there is a temperature ramp. + +===Chamber Temperature=== +Default: 30C + +Defines the chamber temperature in Celcius by adding an M141 command. + +===Holding Force=== +Default: 0 + +Defines the holding pressure of a mechanism, like a vacuum table or electromagnet, to hold the bed surface or object, by adding an M142 command. The holding pressure is in bars. For hardware which only has on/off holding, when the holding pressure is zero, turn off holding, when the holding pressure is greater than zero, turn on holding. + +==Heated Beds== +===Bothacker=== +A resistor heated aluminum plate by Bothacker: + +http://bothacker.com + +with an article at: + +http://bothacker.com/2009/12/18/heated-build-platform/ + +===Domingo=== +A heated copper build plate by Domingo: + +http://casainho-emcrepstrap.blogspot.com/ + +with articles at: + +http://casainho-emcrepstrap.blogspot.com/2010/01/first-time-with-pla-testing-it-also-on.html + +http://casainho-emcrepstrap.blogspot.com/2010/01/call-for-helpideas-to-develop-heated.html + +http://casainho-emcrepstrap.blogspot.com/2010/01/new-heated-build-platform.html + +http://casainho-emcrepstrap.blogspot.com/2010/01/no-acrylic-and-instead-kapton-tape-on.html + +http://casainho-emcrepstrap.blogspot.com/2010/01/problems-with-heated-build-platform-and.html + +http://casainho-emcrepstrap.blogspot.com/2010/01/perfect-build-platform.html + +http://casainho-emcrepstrap.blogspot.com/2009/12/almost-no-warp.html + +http://casainho-emcrepstrap.blogspot.com/2009/12/heated-base-plate.html + +===Jmil=== +A heated build stage by jmil, over at: + +http://www.hive76.org + +with articles at: + +http://www.hive76.org/handling-hot-build-surfaces + +http://www.hive76.org/heated-build-stage-success + +===Metalab=== +A heated base by the Metalab folks: + +http://reprap.soup.io + +with information at: + +http://reprap.soup.io/?search=heated%20base + +===Nophead=== +A resistor heated aluminum bed by Nophead: + +http://hydraraptor.blogspot.com + +with articles at: + +http://hydraraptor.blogspot.com/2010/01/will-it-stick.html + +http://hydraraptor.blogspot.com/2010/01/hot-metal-and-serendipity.html + +http://hydraraptor.blogspot.com/2010/01/new-year-new-plastic.html + +http://hydraraptor.blogspot.com/2010/01/hot-bed.html + +===Prusajr=== +A resistive wire heated plexiglass plate by prusajr: + +http://prusadjs.cz/ + +with articles at: + +http://prusadjs.cz/2010/01/heated-reprap-print-bed-mk2/ + +http://prusadjs.cz/2009/11/look-ma-no-warping-heated-reprap-print-bed/ + +===Zaggo=== +A resistor heated aluminum plate by Zaggo at Pleasant Software: + +http://pleasantsoftware.com/developer/3d/ + +with articles at: + +http://pleasantsoftware.com/developer/3d/2009/12/05/raftless/ + +http://pleasantsoftware.com/developer/3d/2009/11/15/living-in-times-of-warp-free-printing/ + +http://pleasantsoftware.com/developer/3d/2009/11/12/canned-heat/ + +==Examples== +The following examples chamber the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and chamber.py. + +> python chamber.py +This brings up the chamber dialog. + +> python chamber.py Screw Holder Bottom.stl +The chamber tool is parsing the file: +Screw Holder Bottom.stl +.. +The chamber tool has created the file: +Screw Holder Bottom_chamber.gcode + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import sys + + +__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 getCraftedText(fileName, text='', repository=None): + "Chamber the file or text." + return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository) + +def getCraftedTextFromText(gcodeText, repository=None): + "Chamber a gcode linear move text." + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'chamber'): + return gcodeText + if repository == None: + repository = settings.getReadRepository(ChamberRepository()) + if not repository.activateChamber.value: + return gcodeText + return ChamberSkein().getCraftedGcode(gcodeText, repository) + +def getNewRepository(): + 'Get new repository.' + return ChamberRepository() + +def writeOutput(fileName, shouldAnalyze=True): + "Chamber a gcode linear move file." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'chamber', shouldAnalyze) + + +class ChamberRepository: + "A class to handle the chamber settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.chamber.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Chamber', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Chamber') + self.activateChamber = settings.BooleanSetting().getFromValue('Activate Chamber', self, False ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Bed -', self ) + self.bedTemperature = settings.FloatSpin().getFromValue(20.0, 'Bed Temperature (Celcius):', self, 90.0, 60.0) + self.bedTemperatureBeginChangeHeight = settings.FloatSpin().getFromValue(-1.0, 'Bed Temperature Begin Change Height (mm):', self, 20.0, -1.0) + self.bedTemperatureEndChangeHeight = settings.FloatSpin().getFromValue(-1.0, 'Bed Temperature End Change Height (mm):', self, 40.0, -1.0) + self.bedTemperatureEnd = settings.FloatSpin().getFromValue(20.0, 'Bed Temperature End (Celcius):', self, 90.0, 20.0) + settings.LabelSeparator().getFromRepository(self) + self.chamberTemperature = settings.FloatSpin().getFromValue( 20.0, 'Chamber Temperature (Celcius):', self, 90.0, 30.0 ) + self.holdingForce = settings.FloatSpin().getFromValue( 0.0, 'Holding Force (bar):', self, 100.0, 0.0 ) + self.executeTitle = 'Chamber' + + def execute(self): + "Chamber button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + + +class ChamberSkein: + "A class to chamber a skein of extrusions." + def __init__(self): + 'Initialize.' + self.changeWidth = None + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.lineIndex = 0 + self.lines = None + self.oldBedTemperature = None + + def addBedTemperature(self, bedTemperature): + 'Add bed temperature if it is different from the old.' + if bedTemperature != self.oldBedTemperature: + self.distanceFeedRate.addParameter('M140', bedTemperature) + self.oldBedTemperature = bedTemperature + + def getCraftedGcode(self, gcodeText, repository): + "Parse gcode text and store the chamber gcode." + endAtLeastBegin = repository.bedTemperatureEndChangeHeight.value >= repository.bedTemperatureBeginChangeHeight.value + if endAtLeastBegin and repository.bedTemperatureBeginChangeHeight.value >= 0.0: + self.changeWidth = repository.bedTemperatureEndChangeHeight.value - repository.bedTemperatureBeginChangeHeight.value + self.repository = repository + self.lines = archive.getTextLines(gcodeText) + self.parseInitialization() + for line in self.lines[self.lineIndex :]: + self.parseLine(line) + return self.distanceFeedRate.output.getvalue() + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('chamber') + return + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + "Parse a gcode line and add it to the chamber skein." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == '()': + self.distanceFeedRate.addLine(line) + self.addBedTemperature(self.repository.bedTemperature.value) + self.distanceFeedRate.addParameter('M141', self.repository.chamberTemperature.value) # Set chamber temperature. + self.distanceFeedRate.addParameter('M142', self.repository.holdingForce.value) # Set holding pressure. + return + self.distanceFeedRate.addLine(line) + if firstWord == '(' and self.changeWidth != None: + z = float(splitLine[1]) + if z >= self.repository.bedTemperatureEndChangeHeight.value: + self.addBedTemperature(self.repository.bedTemperatureEnd.value) + return + if z <= self.repository.bedTemperatureBeginChangeHeight.value: + return + along = (z - self.repository.bedTemperatureBeginChangeHeight.value) / self.changeWidth + self.addBedTemperature(self.repository.bedTemperature.value * (1 - along) + self.repository.bedTemperatureEnd.value * along) + + +def main(): + "Display the chamber dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/chop.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/chop.py new file mode 100644 index 0000000..8b0e556 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/chop.py @@ -0,0 +1,222 @@ +""" +This page is in the table of contents. +Chop is a script to chop a shape into svg slice layers. + +==Settings== +===Add Layer Template to SVG=== +Default is on. + +When selected, the layer template will be added to the svg output, which adds javascript control boxes. So 'Add Layer Template to SVG' should be selected when the svg will be viewed in a browser. + +When off, no controls will be added, the svg output will only include the fabrication paths. So 'Add Layer Template to SVG' should be deselected when the svg will be used by other software, like Inkscape. + +===Add Extra Top Layer if Necessary=== +Default is on. + +When selected, chop will add an extra layer at the very top of the object if the top of the object is more than half the layer height above the first slice. This is so the cutting tool doesn't cut too deeply through the top of the object on its first pass. + +===Extra Decimal Places=== +Default is two. + +Defines the number of extra decimal places export will output compared to the number of decimal places in the layer height. The higher the 'Extra Decimal Places', the more significant figures the output numbers will have. + +===Import Coarseness=== +Default is one. + +When a triangle mesh has holes in it, the triangle mesh slicer switches over to a slow algorithm that spans gaps in the mesh. The higher the 'Import Coarseness' setting, the wider the gaps in the mesh it will span. An import coarseness of one means it will span gaps of the edge width. + +===Layer Height=== +Default is 0.4 mm. + +Defines the height of the layer, this is the most important chop setting. + +===Layers=== +Chop slices from top to bottom. To get only the bottom layer, set the "Layers From" to minus one. The 'Layers From' until 'Layers To' range is a python slice. + +====Layers From==== +Default is zero. + +Defines the index of the top layer that will be chopped. If the 'Layers From' is the default zero, the carving will start from the top layer. If the 'Layers From' index is negative, then the carving will start from the 'Layers From' index above the bottom layer. + +====Layers To==== +Default is a huge number, which will be limited to the highest index number. + +Defines the index of the bottom layer that will be chopped. If the 'Layers To' index is a huge number like the default, the carving will go to the bottom of the model. If the 'Layers To' index is negative, then the carving will go to the 'Layers To' index above the bottom layer. + +===Mesh Type=== +Default is 'Correct Mesh'. + +====Correct Mesh==== +When selected, the mesh will be accurately chopped, and if a hole is found, chop will switch over to the algorithm that spans gaps. + +====Unproven Mesh==== +When selected, chop will use the gap spanning algorithm from the start. The problem with the gap spanning algothm is that it will span gaps, even if there is not actually a gap in the model. + +===Perimeter Width=== +Default is 2 mm. + +Defines the width of the edge. + +===SVG Viewer=== +Default is webbrowser. + +If the 'SVG Viewer' is set to the default 'webbrowser', the scalable vector graphics file will be sent to the default browser to be opened. If the 'SVG Viewer' is set to a program name, the scalable vector graphics file will be sent to that program to be opened. + +==Examples== +The following examples chop the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and chop.py. + +> python chop.py +This brings up the chop dialog. + +> python chop.py Screw Holder Bottom.stl +The chop tool is parsing the file: +Screw Holder Bottom.stl +.. +The chop tool has created the file: +.. Screw Holder Bottom_chop.svg + +""" + +from __future__ import absolute_import +try: + import psyco + psyco.full() +except: + pass +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from fabmetheus_utilities import svg_writer +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import os +import sys +import time + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCraftedText( fileName, gcodeText = '', repository=None): + "Get chopped text." + if fileName.endswith('.svg'): + gcodeText = archive.getTextIfEmpty(fileName, gcodeText) + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'chop'): + return gcodeText + carving = svg_writer.getCarving(fileName) + if carving == None: + return '' + if repository == None: + repository = ChopRepository() + settings.getReadRepository(repository) + return ChopSkein().getCarvedSVG( carving, fileName, repository ) + +def getNewRepository(): + 'Get new repository.' + return ChopRepository() + +def writeOutput(fileName, shouldAnalyze=True): + "Chop a GNU Triangulated Surface file. If no fileName is specified, chop the first GNU Triangulated Surface file in this folder." + startTime = time.time() + print('File ' + archive.getSummarizedFileName(fileName) + ' is being chopped.') + repository = ChopRepository() + settings.getReadRepository(repository) + chopGcode = getCraftedText( fileName, '', repository ) + if chopGcode == '': + return + suffixFileName = fileName[ : fileName.rfind('.') ] + '_chop.svg' + suffixDirectoryName = os.path.dirname(suffixFileName) + suffixReplacedBaseName = os.path.basename(suffixFileName).replace(' ', '_') + suffixFileName = os.path.join( suffixDirectoryName, suffixReplacedBaseName ) + archive.writeFileText( suffixFileName, chopGcode ) + print('The chopped file is saved as ' + archive.getSummarizedFileName(suffixFileName) ) + print('It took %s to chop the file.' % euclidean.getDurationString( time.time() - startTime ) ) + if shouldAnalyze: + settings.openSVGPage( suffixFileName, repository.svgViewer.value ) + + +class ChopRepository: + "A class to handle the chop settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.chop.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getTranslatorFileTypeTuples(), 'Open File to be Chopped', self, '') + self.addExtraTopLayerIfNecessary = settings.BooleanSetting().getFromValue('Add Extra Top Layer if Necessary', self, True ) + self.addLayerTemplateToSVG = settings.BooleanSetting().getFromValue('Add Layer Template to SVG', self, True) + self.edgeWidth = settings.FloatSpin().getFromValue( 0.4, 'Edge Width (mm):', self, 4.0, 2.0 ) + self.extraDecimalPlaces = settings.FloatSpin().getFromValue(0.0, 'Extra Decimal Places (float):', self, 3.0, 2.0) + self.importCoarseness = settings.FloatSpin().getFromValue( 0.5, 'Import Coarseness (ratio):', self, 2.0, 1.0 ) + self.layerHeight = settings.FloatSpin().getFromValue( 0.1, 'Layer Height (mm):', self, 1.0, 0.4 ) + self.layersFrom = settings.IntSpin().getFromValue( 0, 'Layers From (index):', self, 20, 0 ) + self.layersTo = settings.IntSpin().getSingleIncrementFromValue( 0, 'Layers To (index):', self, 912345678, 912345678 ) + self.meshTypeLabel = settings.LabelDisplay().getFromName('Mesh Type: ', self, ) + importLatentStringVar = settings.LatentStringVar() + self.correctMesh = settings.Radio().getFromRadio( importLatentStringVar, 'Correct Mesh', self, True ) + self.unprovenMesh = settings.Radio().getFromRadio( importLatentStringVar, 'Unproven Mesh', self, False ) + self.svgViewer = settings.StringSetting().getFromValue('SVG Viewer:', self, 'webbrowser') + settings.LabelSeparator().getFromRepository(self) + self.executeTitle = 'Chop' + + def execute(self): + "Chop button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypes(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class ChopSkein: + "A class to chop a carving." + def addExtraTopLayerIfNecessary( self, carving, layerHeight, loopLayers ): + "Add extra top layer if necessary." + topRotatedBoundaryLayer = loopLayers[-1] + cuttingSafeHeight = topRotatedBoundaryLayer.z + 0.5001 * layerHeight + if cuttingSafeHeight > carving.getCarveCornerMaximum().z: + return + extraTopRotatedBoundaryLayer = topRotatedBoundaryLayer.getCopyAtZ( topRotatedBoundaryLayer.z + layerHeight ) + loopLayers.append( extraTopRotatedBoundaryLayer ) + + def getCarvedSVG( self, carving, fileName, repository ): + "Parse gnu triangulated surface text and store the chopped gcode." + layerHeight = repository.layerHeight.value + edgeWidth = repository.edgeWidth.value + carving.setCarveLayerHeight( layerHeight ) + importRadius = 0.5 * repository.importCoarseness.value * abs(edgeWidth) + carving.setCarveImportRadius(max(importRadius, 0.001 * layerHeight)) + carving.setCarveIsCorrectMesh( repository.correctMesh.value ) + loopLayers = carving.getCarveBoundaryLayers() + if len( loopLayers ) < 1: + print('Warning, there are no slices for the model, this could be because the model is too small for the Layer Height.') + return '' + if repository.addExtraTopLayerIfNecessary.value: + self.addExtraTopLayerIfNecessary( carving, layerHeight, loopLayers ) + loopLayers.reverse() + layerHeight = carving.getCarveLayerHeight() + decimalPlacesCarried = euclidean.getDecimalPlacesCarried(repository.extraDecimalPlaces.value, layerHeight) + svgWriter = svg_writer.SVGWriter( + repository.addLayerTemplateToSVG.value, + carving.getCarveCornerMaximum(), + carving.getCarveCornerMinimum(), + decimalPlacesCarried, + carving.getCarveLayerHeight(), + edgeWidth) + truncatedRotatedBoundaryLayers = svg_writer.getTruncatedRotatedBoundaryLayers(loopLayers, repository) + return svgWriter.getReplacedSVGTemplate( fileName, truncatedRotatedBoundaryLayers, 'chop', carving.getFabmetheusXML()) + + +def main(): + "Display the chop dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/cleave.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/cleave.py new file mode 100644 index 0000000..9ae1ef6 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/cleave.py @@ -0,0 +1,204 @@ +""" +This page is in the table of contents. +Cleave is a script to cleave a shape into svg slice layers. + +==Settings== +===Add Layer Template to SVG=== +Default is on. + +When selected, the layer template will be added to the svg output, which adds javascript control boxes. So 'Add Layer Template to SVG' should be selected when the svg will be viewed in a browser. + +When off, no controls will be added, the svg output will only include the fabrication paths. So 'Add Layer Template to SVG' should be deselected when the svg will be used by other software, like Inkscape. + +===Extra Decimal Places=== +Default is two. + +Defines the number of extra decimal places export will output compared to the number of decimal places in the layer height. The higher the 'Extra Decimal Places', the more significant figures the output numbers will have. + +===Import Coarseness=== +Default is one. + +When a triangle mesh has holes in it, the triangle mesh slicer switches over to a slow algorithm that spans gaps in the mesh. The higher the 'Import Coarseness' setting, the wider the gaps in the mesh it will span. An import coarseness of one means it will span gaps of the edge width. + +===Layer Height=== +Default is 0.4 mm. + +Defines the height of the layer, this is the most important cleave setting. + +===Layers=== +Cleave slices from bottom to top. To get a single layer, set the "Layers From" to zero and the "Layers To" to one. The layer from until layer to range is a python slice. + +====Layers From==== +Default is zero. + +Defines the index of the bottom layer that will be cleaved. If the layer from is the default zero, the carving will start from the lowest layer. If the 'Layers From' index is negative, then the carving will start from the 'Layers From' index below the top layer. + +====Layers To==== +Default is a huge number, which will be limited to the highest index layer. + +Defines the index of the top layer that will be cleaved. If the 'Layers To' index is a huge number like the default, the carving will go to the top of the model. If the 'Layers To' index is negative, then the carving will go to the 'Layers To' index below the top layer. + +===Mesh Type=== +Default is 'Correct Mesh'. + +====Correct Mesh==== +When selected, the mesh will be accurately cleaved, and if a hole is found, cleave will switch over to the algorithm that spans gaps. + +====Unproven Mesh==== +When selected, cleave will use the gap spanning algorithm from the start. The problem with the gap spanning algothm is that it will span gaps, even if there is not actually a gap in the model. + +===Perimeter Width=== +Default is two millimeters. + +Defines the width of the edge. + +===SVG Viewer=== +Default is webbrowser. + +If the 'SVG Viewer' is set to the default 'webbrowser', the scalable vector graphics file will be sent to the default browser to be opened. If the 'SVG Viewer' is set to a program name, the scalable vector graphics file will be sent to that program to be opened. + +==Examples== +The following examples cleave the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and cleave.py. + +> python cleave.py +This brings up the cleave dialog. + +> python cleave.py Screw Holder Bottom.stl +The cleave tool is parsing the file: +Screw Holder Bottom.stl +.. +The cleave tool has created the file: +.. Screw Holder Bottom_cleave.svg + +""" + +from __future__ import absolute_import +try: + import psyco + psyco.full() +except: + pass +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from fabmetheus_utilities import svg_writer +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import os +import sys +import time + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCraftedText( fileName, gcodeText = '', repository=None): + "Get cleaved text." + if fileName.endswith('.svg'): + gcodeText = archive.getTextIfEmpty(fileName, gcodeText) + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'cleave'): + return gcodeText + carving = svg_writer.getCarving(fileName) + if carving == None: + return '' + if repository == None: + repository = CleaveRepository() + settings.getReadRepository(repository) + return CleaveSkein().getCarvedSVG( carving, fileName, repository ) + +def getNewRepository(): + 'Get new repository.' + return CleaveRepository() + +def writeOutput(fileName, shouldAnalyze=True): + "Cleave a GNU Triangulated Surface file." + startTime = time.time() + print('File ' + archive.getSummarizedFileName(fileName) + ' is being cleaved.') + repository = CleaveRepository() + settings.getReadRepository(repository) + cleaveGcode = getCraftedText( fileName, '', repository ) + if cleaveGcode == '': + return + suffixFileName = fileName[ : fileName.rfind('.') ] + '_cleave.svg' + suffixDirectoryName = os.path.dirname(suffixFileName) + suffixReplacedBaseName = os.path.basename(suffixFileName).replace(' ', '_') + suffixFileName = os.path.join( suffixDirectoryName, suffixReplacedBaseName ) + archive.writeFileText( suffixFileName, cleaveGcode ) + print('The cleaved file is saved as ' + archive.getSummarizedFileName(suffixFileName) ) + print('It took %s to cleave the file.' % euclidean.getDurationString( time.time() - startTime ) ) + if shouldAnalyze: + settings.openSVGPage( suffixFileName, repository.svgViewer.value ) + + +class CleaveRepository: + "A class to handle the cleave settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.cleave.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getTranslatorFileTypeTuples(), 'Open File to be Cleaved', self, '') + self.addLayerTemplateToSVG = settings.BooleanSetting().getFromValue('Add Layer Template to SVG', self, True) + self.edgeWidth = settings.FloatSpin().getFromValue( 0.4, 'Edge Width (mm):', self, 4.0, 2.0 ) + self.extraDecimalPlaces = settings.FloatSpin().getFromValue(0.0, 'Extra Decimal Places (float):', self, 3.0, 2.0) + self.importCoarseness = settings.FloatSpin().getFromValue( 0.5, 'Import Coarseness (ratio):', self, 2.0, 1.0 ) + self.layerHeight = settings.FloatSpin().getFromValue( 0.1, 'Layer Height (mm):', self, 1.0, 0.4 ) + self.layersFrom = settings.IntSpin().getFromValue( 0, 'Layers From (index):', self, 20, 0 ) + self.layersTo = settings.IntSpin().getSingleIncrementFromValue( 0, 'Layers To (index):', self, 912345678, 912345678 ) + self.meshTypeLabel = settings.LabelDisplay().getFromName('Mesh Type: ', self, ) + importLatentStringVar = settings.LatentStringVar() + self.correctMesh = settings.Radio().getFromRadio( importLatentStringVar, 'Correct Mesh', self, True ) + self.unprovenMesh = settings.Radio().getFromRadio( importLatentStringVar, 'Unproven Mesh', self, False ) + self.svgViewer = settings.StringSetting().getFromValue('SVG Viewer:', self, 'webbrowser') + settings.LabelSeparator().getFromRepository(self) + self.executeTitle = 'Cleave' + + def execute(self): + "Cleave button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypes(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class CleaveSkein: + "A class to cleave a carving." + def getCarvedSVG( self, carving, fileName, repository ): + "Parse gnu triangulated surface text and store the cleaved gcode." + edgeWidth = repository.edgeWidth.value + layerHeight = repository.layerHeight.value + carving.setCarveLayerHeight( layerHeight ) + importRadius = 0.5 * repository.importCoarseness.value * abs(edgeWidth) + carving.setCarveImportRadius(max(importRadius, 0.001 * layerHeight)) + carving.setCarveIsCorrectMesh( repository.correctMesh.value ) + loopLayers = carving.getCarveBoundaryLayers() + if len( loopLayers ) < 1: + print('Warning, there are no slices for the model, this could be because the model is too small for the Layer Height.') + return '' + layerThickness = carving.getCarveLayerHeight() + decimalPlacesCarried = euclidean.getDecimalPlacesCarried(repository.extraDecimalPlaces.value, layerHeight) + svgWriter = svg_writer.SVGWriter( + repository.addLayerTemplateToSVG.value, + carving.getCarveCornerMaximum(), + carving.getCarveCornerMinimum(), + decimalPlacesCarried, + carving.getCarveLayerHeight(), + edgeWidth) + truncatedRotatedBoundaryLayers = svg_writer.getTruncatedRotatedBoundaryLayers(loopLayers, repository) + return svgWriter.getReplacedSVGTemplate( fileName, truncatedRotatedBoundaryLayers, 'cleave', carving.getFabmetheusXML()) + + +def main(): + "Display the cleave dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/clip.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/clip.py new file mode 100644 index 0000000..8e5dd7a --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/clip.py @@ -0,0 +1,342 @@ +""" +This page is in the table of contents. +The clip plugin clips the loop ends to prevent bumps from forming, and connects loops. + +The clip manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Clip + +==Operation== +The default 'Activate Clip' checkbox is on. When it is on, the functions described below will work, when it is off, the functions will not be called. + +==Settings== +===Clip Over Perimeter Width=== +Default is 0.2. + +Defines the ratio of the amount each end of the loop is clipped over the edge width. The total gap will therefore be twice the clip. If the ratio is too high loops will have a gap, if the ratio is too low there will be a bulge at the loop ends. + +This setting will affect the output of clip, and the output of the skin. In skin the half width edges will be clipped by according to this setting. + +===Maximum Connection Distance Over Perimeter Width=== +Default is ten. + +Defines the ratio of the maximum connection distance between loops over the edge width. + +Clip will attempt to connect loops that end close to each other, combining them into a spiral, so that the extruder does not stop and restart. This setting sets the maximum gap size to connect. This feature can reduce the amount of extra material or gaps formed at the loop end. + +Setting this to zero disables this feature, preventing the loops from being connected. + +==Examples== +The following examples clip the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and clip.py. + +> python clip.py +This brings up the clip dialog. + +> python clip.py Screw Holder Bottom.stl +The clip tool is parsing the file: +Screw Holder Bottom.stl +.. +The clip tool has created the file: +.. Screw Holder Bottom_clip.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import sys + + +__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 getCraftedText( fileName, text, clipRepository = None ): + "Clip a gcode linear move file or text." + return getCraftedTextFromText( archive.getTextIfEmpty(fileName, text), clipRepository ) + +def getCraftedTextFromText( gcodeText, clipRepository = None ): + "Clip a gcode linear move text." + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'clip'): + return gcodeText + if clipRepository == None: + clipRepository = settings.getReadRepository( ClipRepository() ) + if not clipRepository.activateClip.value: + return gcodeText + return ClipSkein().getCraftedGcode( clipRepository, gcodeText ) + +def getNewRepository(): + 'Get new repository.' + return ClipRepository() + +def writeOutput(fileName, shouldAnalyze=True): + "Clip a gcode linear move file. Chain clip the gcode if it is not already clipped." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'clip', shouldAnalyze) + + +class ClipRepository: + "A class to handle the clip settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.clip.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Clip', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Clip') + self.activateClip = settings.BooleanSetting().getFromValue('Activate Clip', self, False ) + self.clipOverEdgeWidth = settings.FloatSpin().getFromValue( 0.1, 'Clip Over Perimeter Width (ratio):', self, 0.8, 0.5 ) + self.maximumConnectionDistanceOverEdgeWidth = settings.FloatSpin().getFromValue( 1.0, 'Maximum Connection Distance Over Perimeter Width (ratio):', self, 20.0, 10.0 ) + self.executeTitle = 'Clip' + + def execute(self): + "Clip button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class ClipSkein: + "A class to clip a skein of extrusions." + def __init__(self): + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.extruderActive = False + self.feedRateMinute = None + self.isEdge = False + self.isLoop = False + self.layerCount = settings.LayerCount() + self.loopPath = None + self.lineIndex = 0 + self.oldConnectionPoint = None + self.oldLocation = None + self.oldWiddershins = None + self.travelFeedRateMinute = None + + def addGcodeFromThreadZ( self, thread, z ): + "Add a gcode thread to the output." + if len(thread) > 0: + self.distanceFeedRate.addGcodeMovementZWithFeedRate( self.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 clip, this should never happen") + print(thread) + return + self.distanceFeedRate.addLine('M101') + for point in thread[1 :]: + self.distanceFeedRate.addGcodeMovementZWithFeedRate( self.feedRateMinute, point, z ) + + def addSegmentToPixelTables(self, location, oldLocation): + "Add the segment to the layer and mask table." + euclidean.addValueSegmentToPixelTable(oldLocation, location, self.layerPixelTable, None, self.layerPixelWidth) + + def addTailoredLoopPath(self, line): + "Add a clipped loop path." + if self.clipLength > 0.0: + removeTable = {} + euclidean.addLoopToPixelTable(self.loopPath.path, removeTable, self.layerPixelWidth) + euclidean.removePixelTableFromPixelTable( removeTable, self.layerPixelTable ) + self.loopPath.path = euclidean.getClippedSimplifiedLoopPath(self.clipLength, self.loopPath.path, self.edgeWidth) + euclidean.addLoopToPixelTable( self.loopPath.path, self.layerPixelTable, self.layerPixelWidth ) + if self.oldWiddershins == None: + self.addGcodeFromThreadZ( self.loopPath.path, self.loopPath.z ) + else: + if self.oldWiddershins != euclidean.isWiddershins( self.loopPath.path ): + self.loopPath.path.reverse() + for point in self.loopPath.path: + self.distanceFeedRate.addGcodeMovementZWithFeedRate( self.feedRateMinute, point, self.loopPath.z ) + if self.getNextThreadIsACloseLoop(self.loopPath.path): + self.oldConnectionPoint = self.loopPath.path[-1] + self.oldWiddershins = euclidean.isWiddershins(self.loopPath.path) + else: + self.oldConnectionPoint = None + self.oldWiddershins = None + self.distanceFeedRate.addLine(line) + self.loopPath = None + + def getConnectionIsCloseWithoutOverlap( self, location, path ): + "Determine if the connection is close enough and does not overlap another thread." + if len(path) < 1: + return False + locationComplex = location.dropAxis() + segment = locationComplex - path[-1] + segmentLength = abs(segment) + if segmentLength <= 0.0: + return True + if segmentLength > self.maximumConnectionDistance: + return False + segmentTable = {} + euclidean.addSegmentToPixelTable( path[-1], locationComplex, segmentTable, 2.0, 2.0, self.layerPixelWidth ) + if euclidean.isPixelTableIntersecting( self.layerPixelTable, segmentTable, {} ): + return False + euclidean.addValueSegmentToPixelTable( path[-1], locationComplex, self.layerPixelTable, None, self.layerPixelWidth ) + return True + + def getCraftedGcode( self, clipRepository, gcodeText ): + "Parse gcode text and store the clip gcode." + self.lines = archive.getTextLines(gcodeText) + self.parseInitialization( clipRepository ) + for self.lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[self.lineIndex] + self.parseLine(line) + return self.distanceFeedRate.output.getvalue() + + def getNextThreadIsACloseLoop(self, path): + "Determine if the next thread is a loop." + if self.oldLocation == None or self.maximumConnectionDistance <= 0.0: + return False + isEdge = False + isLoop = False + location = self.oldLocation + for afterIndex in xrange(self.lineIndex + 1, len(self.lines)): + line = self.lines[afterIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'G1': + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + elif firstWord == '(': + isLoop = True + elif firstWord == '(': + isEdge = True + elif firstWord == 'M101': + if isLoop != self.isLoop or isEdge != self.isEdge: + return False + return self.getConnectionIsCloseWithoutOverlap(location, path) + elif firstWord == '(': + return False + return False + + def isNextExtruderOn(self): + "Determine if there is an extruder on command before a move command." + for afterIndex in xrange(self.lineIndex + 1, len(self.lines)): + line = self.lines[afterIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'G1' or firstWord == 'M103': + return False + elif firstWord == 'M101': + return True + return False + + def linearMove(self, splitLine): + "Add to loop path if this is a loop or path." + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + self.feedRateMinute = gcodec.getFeedRateMinute(self.feedRateMinute, splitLine) + if self.isLoop or self.isEdge: + if self.isNextExtruderOn(): + self.loopPath = euclidean.PathZ(location.z) + if self.loopPath == None: + if self.extruderActive: + self.oldWiddershins = None + else: + if self.oldConnectionPoint != None: + self.addSegmentToPixelTables(self.oldConnectionPoint, location.dropAxis()) + self.oldConnectionPoint = None + self.loopPath.path.append(location.dropAxis()) + self.oldLocation = location + + def parseInitialization(self, clipRepository): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('clip') + return + elif firstWord == '(': + self.distanceFeedRate.addTagBracketedLine('clipOverEdgeWidth', clipRepository.clipOverEdgeWidth.value) + self.edgeWidth = float(splitLine[1]) + absoluteEdgeWidth = abs(self.edgeWidth) + self.clipLength = clipRepository.clipOverEdgeWidth * self.edgeWidth + self.connectingStepLength = 0.5 * absoluteEdgeWidth + self.layerPixelWidth = 0.34321 * absoluteEdgeWidth + self.maximumConnectionDistance = clipRepository.maximumConnectionDistanceOverEdgeWidth.value * absoluteEdgeWidth + elif firstWord == '(': + self.travelFeedRateMinute = 60.0 * float(splitLine[1]) + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + "Parse a gcode line and add it to the clip skein." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + self.linearMove(splitLine) + elif firstWord == '(': + self.setLayerPixelTable() + elif firstWord == '(': + self.isLoop = True + elif firstWord == '()': + self.isLoop = False + elif firstWord == 'M101': + self.extruderActive = True + elif firstWord == 'M103': + self.extruderActive = False + if self.loopPath != None: + self.addTailoredLoopPath(line) + return + elif firstWord == '(': + self.isEdge = True + elif firstWord == '()': + self.isEdge = False + if self.loopPath == None: + self.distanceFeedRate.addLine(line) + + def setLayerPixelTable(self): + "Set the layer pixel table." + self.layerCount.printProgressIncrement('clip') + boundaryLoop = None + extruderActive = False + self.lastInactiveLocation = None + self.layerPixelTable = {} + oldLocation = self.oldLocation + for afterIndex in xrange(self.lineIndex + 1, len(self.lines)): + line = self.lines[ afterIndex ] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'G1': + location = gcodec.getLocationFromSplitLine(oldLocation, splitLine) + if extruderActive and oldLocation != None: + self.addSegmentToPixelTables(location.dropAxis(), oldLocation.dropAxis()) + if extruderActive: + if self.lastInactiveLocation != None: + self.addSegmentToPixelTables(self.lastInactiveLocation.dropAxis(), location.dropAxis()) + self.lastInactiveLocation = None + else: + self.lastInactiveLocation = location + oldLocation = location + elif firstWord == 'M101': + extruderActive = True + elif firstWord == 'M103': + extruderActive = False + elif firstWord == '()': + euclidean.addLoopToPixelTable(boundaryLoop, self.layerPixelTable, self.layerPixelWidth) + boundaryLoop = None + elif firstWord == '(': + if boundaryLoop == None: + boundaryLoop = [] + location = gcodec.getLocationFromSplitLine(None, splitLine) + boundaryLoop.append(location.dropAxis()) + elif firstWord == '()': + return + +def main(): + "Display the clip dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/coil.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/coil.py new file mode 100644 index 0000000..f49760f --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/coil.py @@ -0,0 +1,252 @@ +""" +This page is in the table of contents. +Coil is a script to coil wire or filament around an object. + +==Operation== +The default 'Activate Coil' checkbox is on. When it is on, the functions described below will work, when it is off, the functions will not be called. + +==Settings== +===Minimum Tool Distance=== +Default is twenty millimeters. + +Defines the minimum distance between the wire dispenser and the object. The 'Minimum Tool Distance' should be set to the maximum radius of the wire dispenser, times at least 1.3 to get a reasonable safety margin. + +==Examples== +The following examples coil the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and coil.py. + +> python coil.py +This brings up the coil dialog. + +> python coil.py Screw Holder Bottom.stl +The coil tool is parsing the file: +Screw Holder Bottom.stl +.. +The coil tool has created the file: +Screw Holder Bottom_coil.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import os +import sys + + +__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 getCraftedText( fileName, gcodeText = '', repository=None): + "Coil the file or gcodeText." + return getCraftedTextFromText( archive.getTextIfEmpty(fileName, gcodeText), repository ) + +def getCraftedTextFromText(gcodeText, repository=None): + "Coil a gcode linear move gcodeText." + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'coil'): + return gcodeText + if repository == None: + repository = settings.getReadRepository( CoilRepository() ) + if not repository.activateCoil.value: + return gcodeText + return CoilSkein().getCraftedGcode(gcodeText, repository) + +def getNewRepository(): + 'Get new repository.' + return CoilRepository() + +def writeOutput(fileName, shouldAnalyze=True): + "Coil a gcode linear move file." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'coil', shouldAnalyze) + + +class CoilRepository: + "A class to handle the coil settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.coil.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Coil', self, '') + self.activateCoil = settings.BooleanSetting().getFromValue('Activate Coil', self, True ) + self.minimumToolDistance = settings.FloatSpin().getFromValue( 10.0, 'Minimum Tool Distance (millimeters):', self, 50.0, 20.0 ) + self.executeTitle = 'Coil' + + def execute(self): + "Coil button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + + +class CoilSkein: + "A class to coil a skein of extrusions." + def __init__(self): + self.boundaryLayers = [] + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.edgeWidth = 0.6 + self.lineIndex = 0 + self.lines = None + self.oldLocationComplex = complex() + self.shutdownLines = [] + + def addCoilLayer( self, boundaryLayers, radius, z ): + "Add a coil layer." + self.distanceFeedRate.addLine('( %s )' % z ) # Indicate that a new layer is starting. + self.distanceFeedRate.addLine('()') + thread = [] + for boundaryLayerIndex in xrange(1, len(boundaryLayers) - 1): + boundaryLayer = boundaryLayers[boundaryLayerIndex] + boundaryLayerBegin = boundaryLayers[boundaryLayerIndex - 1] + boundaryLayerEnd = boundaryLayers[boundaryLayerIndex + 1] + beginLocation = Vector3(0.0, 0.0, 0.5 * (boundaryLayerBegin.z + boundaryLayer.z)) + outsetLoop = intercircle.getLargestInsetLoopFromLoop(boundaryLayer.loops[0], - radius) + self.addCoilToThread(beginLocation, 0.5 * (boundaryLayer.z + boundaryLayerEnd.z), outsetLoop, thread) + self.addGcodeFromThread(thread) + self.distanceFeedRate.addLine('()') + self.distanceFeedRate.addLine('()') + + def addCoilLayers(self): + "Add the coil layers." + numberOfLayersFloat = round( self.edgeWidth / self.layerHeight ) + numberOfLayers = int( numberOfLayersFloat ) + halfLayerThickness = 0.5 * self.layerHeight + startOutset = self.repository.minimumToolDistance.value + halfLayerThickness + startZ = self.boundaryLayers[0].z + halfLayerThickness + zRange = self.boundaryLayers[-1].z - self.boundaryLayers[0].z + zIncrement = 0.0 + if zRange >= 0.0: + zIncrement = zRange / numberOfLayersFloat + for layerIndex in xrange( numberOfLayers ): + settings.printProgressByNumber(layerIndex, numberOfLayers, 'coil') + boundaryLayers = self.boundaryLayers + if layerIndex % 2 == 1: + boundaryLayers = self.boundaryReverseLayers + radius = startOutset + layerIndex * self.layerHeight + z = startZ + layerIndex * zIncrement + self.addCoilLayer( boundaryLayers, radius, z ) + + def addCoilToThread(self, beginLocation, endZ, loop, thread): + "Add a coil to the thread." + if len(loop) < 1: + return + loop = euclidean.getLoopStartingClosest(self.halfEdgeWidth, self.oldLocationComplex, loop) + length = euclidean.getLoopLength(loop) + if length <= 0.0: + return + oldPoint = loop[0] + pathLength = 0.0 + for point in loop[1 :]: + pathLength += abs(point - oldPoint) + along = pathLength / length + z = (1.0 - along) * beginLocation.z + along * endZ + location = Vector3(point.real, point.imag, z) + thread.append(location) + oldPoint = point + self.oldLocationComplex = loop[-1] + + def addGcodeFromThread( self, thread ): + "Add a thread to the output." + if len(thread) > 0: + firstLocation = thread[0] + self.distanceFeedRate.addGcodeMovementZ( firstLocation.dropAxis(), firstLocation.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 addGcodeFromThread in coil, this should never happen") + print(thread) + return + self.distanceFeedRate.addLine('M101') # Turn extruder on. + for location in thread[1 :]: + self.distanceFeedRate.addGcodeMovementZ( location.dropAxis(), location.z ) + self.distanceFeedRate.addLine('M103') # Turn extruder off. + + def getCraftedGcode(self, gcodeText, repository): + "Parse gcode text and store the coil gcode." + self.repository = repository + self.lines = archive.getTextLines(gcodeText) + self.parseInitialization() + self.parseBoundaries() + self.parseUntilLayer() + self.addCoilLayers() + self.distanceFeedRate.addLines( self.shutdownLines ) + return self.distanceFeedRate.output.getvalue() + + def parseBoundaries(self): + "Parse the boundaries and add them to the boundary layers." + boundaryLoop = None + boundaryLayer = None + for line in self.lines[self.lineIndex :]: + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if len( self.shutdownLines ) > 0: + self.shutdownLines.append(line) + if firstWord == '()': + boundaryLoop = None + elif firstWord == '(': + location = gcodec.getLocationFromSplitLine(None, splitLine) + if boundaryLoop == None: + boundaryLoop = [] + boundaryLayer.loops.append(boundaryLoop) + boundaryLoop.append(location.dropAxis()) + elif firstWord == '(': + boundaryLayer = euclidean.LoopLayer(float(splitLine[1])) + self.boundaryLayers.append(boundaryLayer) + elif firstWord == '()': + self.shutdownLines = [ line ] + for boundaryLayer in self.boundaryLayers: + if not euclidean.isWiddershins( boundaryLayer.loops[0] ): + boundaryLayer.loops[0].reverse() + self.boundaryReverseLayers = self.boundaryLayers[:] + self.boundaryReverseLayers.reverse() + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('coil') + return + elif firstWord == '(': + self.layerHeight = float(splitLine[1]) + elif firstWord == '(': + self.edgeWidth = float(splitLine[1]) + self.halfEdgeWidth = 0.5 * self.edgeWidth + self.distanceFeedRate.addLine(line) + + def parseUntilLayer(self): + "Parse until the layer line and add it to the coil skein." + for self.lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '(': + return + self.distanceFeedRate.addLine(line) + + +def main(): + "Display the coil dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/comb.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/comb.py new file mode 100644 index 0000000..7fc7872 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/comb.py @@ -0,0 +1,500 @@ +""" +This page is in the table of contents. +Comb is a craft plugin to bend the extruder travel paths around holes in the slices, to avoid stringers. + +The comb manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Comb + +==Operation== +The default 'Activate Comb' checkbox is off. When it is on, the functions described below will work, when it is off, nothing will be done. + +==Settings== +===Running Jump Space=== +Default: 2 mm + +Defines the running jump space that is added before going from one island to another. If the running jump space is greater than zero, the departure from the island will also be brought closer to the arrival point on the next island so that the stringer between islands will be shorter. For an extruder with acceleration code, an extra space before leaving the island means that it will be going at high speed as it exits the island, which means the stringer between islands will be thinner. + +==Examples== +The following examples comb the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and comb.py. + +> python comb.py +This brings up the comb dialog. + +> python comb.py Screw Holder Bottom.stl +The comb tool is parsing the file: +Screw Holder Bottom.stl +.. +The comb tool has created the file: +.. Screw Holder Bottom_comb.gcode + +""" + +from __future__ import absolute_import +try: + import psyco + psyco.full() +except: + pass +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import sys + + +__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 getCraftedText(fileName, text, repository=None): + "Comb a gcode linear move text." + return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository) + +def getCraftedTextFromText(gcodeText, repository=None): + "Comb a gcode linear move text." + if gcodec.isProcedureDoneOrFileIsEmpty(gcodeText, 'comb'): + return gcodeText + if repository == None: + repository = settings.getReadRepository(CombRepository()) + if not repository.activateComb.value: + return gcodeText + return CombSkein().getCraftedGcode(gcodeText, repository) + +def getJumpPoint(begin, end, loop, runningJumpSpace): + 'Get running jump point inside loop.' + segment = begin - end + segmentLength = abs(segment) + if segmentLength == 0.0: + return begin + segment /= segmentLength + distancePoint = DistancePoint(begin, loop, runningJumpSpace, segment) + if distancePoint.distance == runningJumpSpace: + return distancePoint.point + effectiveDistance = distancePoint.distance + jumpPoint = distancePoint.point + segmentLeft = complex(0.70710678118654757, -0.70710678118654757) + distancePoint = DistancePoint(begin, loop, runningJumpSpace, segmentLeft) + distancePoint.distance *= 0.5 + if distancePoint.distance > effectiveDistance: + effectiveDistance = distancePoint.distance + jumpPoint = distancePoint.point + segmentRight = complex(0.70710678118654757, 0.70710678118654757) + distancePoint = DistancePoint(begin, loop, runningJumpSpace, segmentRight) + distancePoint.distance *= 0.5 + if distancePoint.distance > effectiveDistance: + effectiveDistance = distancePoint.distance + jumpPoint = distancePoint.point + return jumpPoint + +def getJumpPointIfInside(boundary, otherPoint, edgeWidth, runningJumpSpace): + 'Get the jump point if it is inside the boundary, otherwise return None.' + insetBoundary = intercircle.getSimplifiedInsetFromClockwiseLoop(boundary, -edgeWidth) + closestJumpDistanceIndex = euclidean.getClosestDistanceIndexToLine(otherPoint, insetBoundary) + jumpIndex = (closestJumpDistanceIndex.index + 1) % len(insetBoundary) + jumpPoint = euclidean.getClosestPointOnSegment(insetBoundary[closestJumpDistanceIndex.index], insetBoundary[jumpIndex], otherPoint) + jumpPoint = getJumpPoint(jumpPoint, otherPoint, boundary, runningJumpSpace) + if euclidean.isPointInsideLoop(boundary, jumpPoint): + return jumpPoint + return None + +def getNewRepository(): + 'Get new repository.' + return CombRepository() + +def getPathsByIntersectedLoop(begin, end, loop): + 'Get both paths along the loop from the point closest to the begin to the point closest to the end.' + closestBeginDistanceIndex = euclidean.getClosestDistanceIndexToLine(begin, loop) + closestEndDistanceIndex = euclidean.getClosestDistanceIndexToLine(end, loop) + beginIndex = (closestBeginDistanceIndex.index + 1) % len(loop) + endIndex = (closestEndDistanceIndex.index + 1) % len(loop) + closestBegin = euclidean.getClosestPointOnSegment(loop[closestBeginDistanceIndex.index], loop[beginIndex], begin) + closestEnd = euclidean.getClosestPointOnSegment(loop[closestEndDistanceIndex.index], loop[endIndex], end) + clockwisePath = [closestBegin] + widdershinsPath = [closestBegin] + if closestBeginDistanceIndex.index != closestEndDistanceIndex.index: + widdershinsPath += euclidean.getAroundLoop(beginIndex, endIndex, loop) + clockwisePath += euclidean.getAroundLoop(endIndex, beginIndex, loop)[: : -1] + clockwisePath.append(closestEnd) + widdershinsPath.append(closestEnd) + return [clockwisePath, widdershinsPath] + +def writeOutput(fileName, shouldAnalyze=True): + "Comb a gcode linear move file." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'comb', shouldAnalyze) + + +class BoundarySegment: + 'A boundary and segment.' + def __init__(self, begin): + 'Initialize' + self.segment = [begin] + + def getSegment(self, boundarySegmentIndex, boundarySegments, edgeWidth, runningJumpSpace): + 'Get both paths along the loop from the point closest to the begin to the point closest to the end.' + negativeEdgeWidth = -edgeWidth + nextBoundarySegment = boundarySegments[boundarySegmentIndex + 1] + nextBegin = nextBoundarySegment.segment[0] + end = getJumpPointIfInside(self.boundary, nextBegin, edgeWidth, runningJumpSpace) + if end == None: + end = self.boundary.segment[1] + nextBegin = getJumpPointIfInside(nextBoundarySegment.boundary, end, edgeWidth, runningJumpSpace) + if nextBegin != None: + nextBoundarySegment.segment[0] = nextBegin + return (self.segment[0], end) + + +class CombRepository: + "A class to handle the comb settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.comb.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Comb', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Comb') + self.activateComb = settings.BooleanSetting().getFromValue('Activate Comb', self, True ) + self.runningJumpSpace = settings.FloatSpin().getFromValue(0.0, 'Running Jump Space (mm):', self, 5.0, 2.0) + self.executeTitle = 'Comb' + + def execute(self): + "Comb button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class CombSkein: + "A class to comb a skein of extrusions." + def __init__(self): + 'Initialize' + self.betweenTable = {} + self.boundaryLoop = None + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.extruderActive = False + self.layer = None + self.layerCount = settings.LayerCount() + self.layerTable = {} + self.layerZ = None + self.lineIndex = 0 + self.lines = None + self.nextLayerZ = None + self.oldLocation = None + self.oldZ = None + self.operatingFeedRatePerMinute = None + self.travelFeedRateMinute = None + self.widdershinTable = {} + + def addGcodePathZ( self, feedRateMinute, path, z ): + "Add a gcode path, without modifying the extruder, to the output." + for point in path: + self.distanceFeedRate.addGcodeMovementZWithFeedRate(feedRateMinute, point, z) + + def addIfTravel(self, splitLine): + "Add travel move around loops if the extruder is off." + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + if not self.extruderActive and self.oldLocation != None: + if len(self.getBoundaries()) > 0: + highestZ = max(location.z, self.oldLocation.z) + self.addGcodePathZ(self.travelFeedRateMinute, self.getAroundBetweenPath(self.oldLocation.dropAxis(), location.dropAxis()), highestZ) + self.oldLocation = location + + def addToLoop(self, location): + "Add a location to loop." + if self.layer == None: + if not self.oldZ in self.layerTable: + self.layerTable[self.oldZ] = [] + self.layer = self.layerTable[self.oldZ] + if self.boundaryLoop == None: + self.boundaryLoop = [] + self.layer.append(self.boundaryLoop) + self.boundaryLoop.append(location.dropAxis()) + + def getAroundBetweenLineSegment(self, begin, boundaries, end): + 'Get the path around the loops in the way of the original line segment.' + aroundBetweenLineSegment = [] + boundaries = self.getBoundaries() + points = [] + boundaryIndexes = self.getBoundaryIndexes(begin, boundaries, end, points) + boundaryIndexesIndex = 0 + while boundaryIndexesIndex < len(boundaryIndexes) - 1: + if boundaryIndexes[boundaryIndexesIndex + 1] == boundaryIndexes[boundaryIndexesIndex]: + loopFirst = boundaries[boundaryIndexes[boundaryIndexesIndex]] + pathBetween = self.getPathBetween(loopFirst, points[boundaryIndexesIndex : boundaryIndexesIndex + 4]) + begin = points[boundaryIndexesIndex] + end = points[boundaryIndexesIndex + 3] + pathBetween = self.getInsidePointsAlong(begin, pathBetween[0], points) + pathBetween + pathBetween += self.getInsidePointsAlong(end, pathBetween[-1], points) + aroundBetweenLineSegment += pathBetween + boundaryIndexesIndex += 2 + else: + boundaryIndexesIndex += 1 + return aroundBetweenLineSegment + + def getAroundBetweenPath(self, begin, end): + 'Get the path around the loops in the way of the original line segment.' + aroundBetweenPath = [] + betweens = self.getBetweens() + boundaries = self.getBoundaries() + boundarySegments = self.getBoundarySegments(begin, boundaries, end) + for boundarySegmentIndex, boundarySegment in enumerate(boundarySegments): + segment = boundarySegment.segment + if boundarySegmentIndex < len(boundarySegments) - 1 and self.runningJumpSpace > 0.0: + segment = boundarySegment.getSegment(boundarySegmentIndex, boundarySegments, self.edgeWidth, self.runningJumpSpace) + aroundBetweenPath += self.getAroundBetweenLineSegment(segment[0], boundaries, segment[1]) + if boundarySegmentIndex < len(boundarySegments) - 1: + aroundBetweenPath.append(segment[1]) + aroundBetweenPath.append(boundarySegments[boundarySegmentIndex + 1].segment[0]) + for pointIndex in xrange(len(aroundBetweenPath) - 1, -1, -1): + pointBefore = begin + beforeIndex = pointIndex - 1 + if beforeIndex >= 0: + pointBefore = aroundBetweenPath[beforeIndex] + pointAfter = end + afterIndex = pointIndex + 1 + if afterIndex < len(aroundBetweenPath): + pointAfter = aroundBetweenPath[afterIndex] + if not euclidean.isLineIntersectingLoops(betweens, pointBefore, pointAfter): + del aroundBetweenPath[pointIndex] + return aroundBetweenPath + + def getBetweens(self): + 'Get betweens for the layer.' + if not self.layerZ in self.betweenTable: + self.betweenTable[self.layerZ] = [] + for boundary in self.getBoundaries(): + self.betweenTable[self.layerZ] += intercircle.getInsetLoopsFromLoop(boundary, self.betweenInset) + return self.betweenTable[self.layerZ] + + def getBoundaries(self): + "Get boundaries for the layer." + if self.layerZ in self.layerTable: + return self.layerTable[self.layerZ] + return [] + + def getBoundaryIndexes(self, begin, boundaries, end, points): + 'Get boundary indexes and set the points in the way of the original line segment.' + boundaryIndexes = [] + points.append(begin) + switchX = [] + segment = euclidean.getNormalized(end - begin) + segmentYMirror = complex(segment.real, - segment.imag) + beginRotated = segmentYMirror * begin + endRotated = segmentYMirror * end + y = beginRotated.imag + for boundaryIndex in xrange(len(boundaries)): + boundary = boundaries[boundaryIndex] + boundaryRotated = euclidean.getRotatedComplexes(segmentYMirror, boundary) + euclidean.addXIntersectionIndexesFromLoopY(boundaryRotated, boundaryIndex, switchX, y) + switchX.sort() + maximumX = max(beginRotated.real, endRotated.real) + minimumX = min(beginRotated.real, endRotated.real) + for xIntersection in switchX: + if xIntersection.x > minimumX and xIntersection.x < maximumX: + point = segment * complex(xIntersection.x, y) + points.append(point) + boundaryIndexes.append(xIntersection.index) + points.append(end) + return boundaryIndexes + + def getBoundarySegments(self, begin, boundaries, end): + 'Get the path broken into boundary segments whenever a different boundary is crossed.' + boundarySegments = [] + boundarySegment = BoundarySegment(begin) + boundarySegments.append(boundarySegment) + points = [] + boundaryIndexes = self.getBoundaryIndexes(begin, boundaries, end, points) + boundaryIndexesIndex = 0 + while boundaryIndexesIndex < len(boundaryIndexes) - 1: + if boundaryIndexes[boundaryIndexesIndex + 1] != boundaryIndexes[boundaryIndexesIndex]: + boundarySegment.boundary = boundaries[boundaryIndexes[boundaryIndexesIndex]] + nextBoundary = boundaries[boundaryIndexes[boundaryIndexesIndex + 1]] + if euclidean.isWiddershins(boundarySegment.boundary) and euclidean.isWiddershins(nextBoundary): + boundarySegment.segment.append(points[boundaryIndexesIndex + 1]) + boundarySegment = BoundarySegment(points[boundaryIndexesIndex + 2]) + boundarySegment.boundary = nextBoundary + boundarySegments.append(boundarySegment) + boundaryIndexesIndex += 1 + boundaryIndexesIndex += 1 + boundarySegment.segment.append(points[-1]) + return boundarySegments + + def getCraftedGcode(self, gcodeText, repository): + "Parse gcode text and store the comb gcode." + self.runningJumpSpace = repository.runningJumpSpace.value + self.repository = repository + self.lines = archive.getTextLines(gcodeText) + self.parseInitialization() + for lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[lineIndex] + self.parseBoundariesLayers(line) + for lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[lineIndex] + self.parseLine(line) + return self.distanceFeedRate.output.getvalue() + + def getInsidePointsAlong(self, begin, end, points): + 'Get the points along the segment if it is required to keep the path inside the widdershin boundaries.' + segment = end - begin + segmentLength = abs(segment) + if segmentLength < self.quadrupleEdgeWidth: + return [] + segmentHalfPerimeter = self.halfEdgeWidth / segmentLength * segment + justAfterBegin = begin + segmentHalfPerimeter + justBeforeEnd = end - segmentHalfPerimeter + widdershins = self.getWiddershins() + if not euclidean.isLineIntersectingLoops(widdershins, justAfterBegin, justBeforeEnd): + return [] + numberOfSteps = 10 + stepLength = (segmentLength - self.doubleEdgeWidth) / float(numberOfSteps) + for step in xrange(1, numberOfSteps + 1): + along = begin + stepLength * step + if not euclidean.isLineIntersectingLoops(widdershins, along, justBeforeEnd): + return [along] + return [] + + def getPathBetween(self, loop, points): + "Add a path between the edge and the fill." + paths = getPathsByIntersectedLoop(points[1], points[2], loop) + shortestPath = paths[int(euclidean.getPathLength(paths[1]) < euclidean.getPathLength(paths[0]))] + if len(shortestPath) < 2: + return shortestPath + if abs(points[1] - shortestPath[0]) > abs(points[1] - shortestPath[-1]): + shortestPath.reverse() + loopWiddershins = euclidean.isWiddershins(loop) + pathBetween = [] + for pointIndex in xrange(len(shortestPath)): + center = shortestPath[pointIndex] + centerPerpendicular = None + beginIndex = pointIndex - 1 + if beginIndex >= 0: + begin = shortestPath[beginIndex] + centerPerpendicular = intercircle.getWiddershinsByLength(center, begin, self.edgeWidth) + centerEnd = None + endIndex = pointIndex + 1 + if endIndex < len(shortestPath): + end = shortestPath[endIndex] + centerEnd = intercircle.getWiddershinsByLength(end, center, self.edgeWidth) + if centerPerpendicular == None: + centerPerpendicular = centerEnd + elif centerEnd != None: + centerPerpendicular = 0.5 * (centerPerpendicular + centerEnd) + between = None + if centerPerpendicular == None: + between = center + if between == None: + centerSideWiddershins = center + centerPerpendicular + if euclidean.isPointInsideLoop(loop, centerSideWiddershins) == loopWiddershins: + between = centerSideWiddershins + if between == None: + centerSideClockwise = center - centerPerpendicular + if euclidean.isPointInsideLoop(loop, centerSideClockwise) == loopWiddershins: + between = centerSideClockwise + if between == None: + between = center + pathBetween.append(between) + return pathBetween + + def getWiddershins(self): + 'Get widdershins for the layer.' + if self.layerZ in self.widdershinTable: + return self.widdershinTable[self.layerZ] + self.widdershinTable[self.layerZ] = [] + for boundary in self.getBoundaries(): + if euclidean.isWiddershins(boundary): + self.widdershinTable[self.layerZ].append(boundary) + return self.widdershinTable[self.layerZ] + + def parseBoundariesLayers(self, line): + "Parse a gcode line." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'M103': + self.boundaryLoop = None + elif firstWord == '(': + location = gcodec.getLocationFromSplitLine(None, splitLine) + self.addToLoop(location) + elif firstWord == '(': + self.boundaryLoop = None + self.layer = None + self.oldZ = float(splitLine[1]) + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('comb') + return + elif firstWord == '(': + self.edgeWidth = float(splitLine[1]) + self.betweenInset = 0.7 * self.edgeWidth + self.doubleEdgeWidth = self.edgeWidth + self.edgeWidth + self.halfEdgeWidth = 0.5 * self.edgeWidth + self.quadrupleEdgeWidth = self.doubleEdgeWidth + self.doubleEdgeWidth + elif firstWord == '(': + self.travelFeedRateMinute = 60.0 * float(splitLine[1]) + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + "Parse a gcode line and add it to the comb skein." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if self.distanceFeedRate.getIsAlteration(line): + return + if firstWord == 'G1': + self.addIfTravel(splitLine) + self.layerZ = self.nextLayerZ + elif firstWord == 'M101': + self.extruderActive = True + elif firstWord == 'M103': + self.extruderActive = False + elif firstWord == '(': + self.layerCount.printProgressIncrement('comb') + self.nextLayerZ = float(splitLine[1]) + if self.layerZ == None: + self.layerZ = self.nextLayerZ + self.distanceFeedRate.addLineCheckAlteration(line) + + +class DistancePoint: + 'A class to get the distance of the point along a segment inside a loop.' + def __init__(self, begin, loop, runningJumpSpace, segment): + 'Initialize' + self.distance = 0.0 + self.point = begin + steps = 10 + spaceOverSteps = runningJumpSpace / float(steps) + for numerator in xrange(1, steps + 1): + distance = float(numerator) * spaceOverSteps + point = begin + segment * distance + if euclidean.isPointInsideLoop(loop, point): + self.distance = distance + self.point = point + else: + return + + +def main(): + "Display the comb dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py new file mode 100644 index 0000000..e70d078 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py @@ -0,0 +1,410 @@ +""" +This page is in the table of contents. +Cool is a craft tool to cool the shape. + +Cool works well with a stepper extruder, it does not work well with a DC motor extruder. + +If enabled, before each layer that takes less then "Minimum Layer Time" to print the tool head will orbit around the printed area for 'Minimum Layer Time' minus 'the time it takes to print the layer' before it starts printing the layer. This is great way to let layers with smaller area cool before you start printing on top of them (so you do not overheat the area). + +The cool manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Cool + +Allan Ecker aka The Masked Retriever's has written the "Skeinforge Quicktip: Cool" at: +http://blog.thingiverse.com/2009/07/28/skeinforge-quicktip-cool/ + +==Operation== +The default 'Activate Cool' checkbox is on. When it is on, the functions described below will work, when it is off, the functions will not be called. + +==Settings== +===Bridge Cool=== +Default is one degree Celcius. + +If the layer is a bridge layer, then cool will lower the temperature by 'Bridge Cool' degrees Celcius. + +===Cool Type=== +Default is 'Slow Down'. + +====Orbit==== +When selected, cool will add orbits with the extruder off to give the layer time to cool, so that the next layer is not extruded on a molten base. The orbits will be around the largest island on that layer. Orbit should only be chosen if you can not upgrade to a stepper extruder. + +====Slow Down==== +When selected, cool will slow down the extruder so that it will take the minimum layer time to extrude the layer. DC motors do not operate properly at slow flow rates, so if you have a DC motor extruder, you should upgrade to a stepper extruder, but if you can't do that, you can try using the 'Orbit' option. + +===Maximum Cool=== +Default is 2 degrees Celcius. + +If it takes less time to extrude the layer than the minimum layer time, then cool will lower the temperature by the 'Maximum Cool' setting times the layer time over the minimum layer time. + +===Minimum Layer Time=== +Default is 60 seconds. + +Defines the minimum amount of time the extruder will spend on a layer, this is an important setting. + +===Minimum Orbital Radius=== +Default is 10 millimeters. + +When the orbit cool type is selected, if the area of the largest island is as large as the square of the "Minimum Orbital Radius" then the orbits will be just within the island. If the island is smaller, then the orbits will be in a square of the "Minimum Orbital Radius" around the center of the island. This is so that the hot extruder does not stay too close to small islands. + +===Name of Alteration Files=== +Cool looks for alteration files in the alterations folder in the .skeinforge folder in the home directory. Cool does not care if the text file names are capitalized, but some file systems do not handle file name cases properly, so to be on the safe side you should give them lower case names. If it doesn't find the file it then looks in the alterations folder in the skeinforge_plugins folder. The cool start and end text idea is from: +http://makerhahn.blogspot.com/2008/10/yay-minimug.html + +====Name of Cool End File==== +Default is cool_end.gcode. + +If there is a file with the name of the "Name of Cool End File" setting, it will be added to the end of the orbits. + +====Name of Cool Start File==== +Default is cool_start.gcode. + +If there is a file with the name of the "Name of Cool Start File" setting, it will be added to the start of the orbits. + +===Orbital Outset=== +Default is 2 millimeters. + +When the orbit cool type is selected, the orbits will be outset around the largest island by 'Orbital Outset' millimeters. If 'Orbital Outset' is negative, the orbits will be inset instead. + +===Turn Fan On at Beginning=== +Default is on. + +When selected, cool will turn the fan on at the beginning of the fabrication by adding the M106 command. + +===Turn Fan Off at Ending=== +Default is on. + +When selected, cool will turn the fan off at the ending of the fabrication by adding the M107 command. + +==Examples== +The following examples cool the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and cool.py. + +> python cool.py +This brings up the cool dialog. + +> python cool.py Screw Holder Bottom.stl +The cool tool is parsing the file: +Screw Holder Bottom.stl +.. +The cool tool has created the file: +.. Screw Holder Bottom_cool.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import os +import sys + + +__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 getCraftedText(fileName, text, repository=None): + 'Cool a gcode linear move text.' + return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository) + +def getCraftedTextFromText(gcodeText, repository=None): + 'Cool a gcode linear move text.' + if gcodec.isProcedureDoneOrFileIsEmpty(gcodeText, 'cool'): + return gcodeText + if repository == None: + repository = settings.getReadRepository(CoolRepository()) + if not repository.activateCool.value: + return gcodeText + return CoolSkein().getCraftedGcode(gcodeText, repository) + +def getNewRepository(): + 'Get new repository.' + return CoolRepository() + +def writeOutput(fileName, shouldAnalyze=True): + 'Cool a gcode linear move file. Chain cool the gcode if it is not already cooled.' + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'cool', shouldAnalyze) + + +class CoolRepository: + 'A class to handle the cool settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.cool.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( + fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Cool', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute( + 'http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Cool') + self.activateCool = settings.BooleanSetting().getFromValue('Activate Cool', self, True) + self.bridgeCool = settings.FloatSpin().getFromValue(0.0, 'Bridge Cool (Celcius):', self, 10.0, 1.0) + self.coolType = settings.MenuButtonDisplay().getFromName('Cool Type:', self) + self.orbit = settings.MenuRadio().getFromMenuButtonDisplay(self.coolType, 'Orbit', self, False) + self.slowDown = settings.MenuRadio().getFromMenuButtonDisplay(self.coolType, 'Slow Down', self, True) + self.maximumCool = settings.FloatSpin().getFromValue(0.0, 'Maximum Cool (Celcius):', self, 10.0, 2.0) + self.minimumLayerTime = settings.FloatSpin().getFromValue(0.0, 'Minimum Layer Time (seconds):', self, 120.0, 10.0) + self.minimumOrbitalRadius = settings.FloatSpin().getFromValue( + 0.0, 'Minimum Orbital Radius (millimeters):', self, 20.0, 10.0) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Name of Alteration Files -', self ) + self.nameOfCoolEndFile = settings.StringSetting().getFromValue('Name of Cool End File:', self, 'cool_end.gcode') + self.nameOfCoolStartFile = settings.StringSetting().getFromValue('Name of Cool Start File:', self, 'cool_start.gcode') + settings.LabelSeparator().getFromRepository(self) + self.orbitalOutset = settings.FloatSpin().getFromValue(1.0, 'Orbital Outset (millimeters):', self, 5.0, 2.0) + self.turnFanOnAtBeginning = settings.BooleanSetting().getFromValue('Turn Fan On at Beginning', self, True) + self.turnFanOffAtEnding = settings.BooleanSetting().getFromValue('Turn Fan Off at Ending', self, True) + self.executeTitle = 'Cool' + + def execute(self): + 'Cool button has been clicked.' + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode( + self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class CoolSkein: + 'A class to cool a skein of extrusions.' + def __init__(self): + self.boundaryLayer = None + self.coolTemperature = None + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.feedRateMinute = 960.0 + self.highestZ = 1.0 + self.isBridgeLayer = False + self.isExtruderActive = False + self.layerCount = settings.LayerCount() + self.lineIndex = 0 + self.lines = None + self.multiplier = 1.0 + self.oldFlowRate = None + self.oldFlowRateString = None + self.oldLocation = None + self.oldTemperature = None + + def addCoolOrbits(self, remainingOrbitTime): + 'Add the minimum radius cool orbits.' + if len(self.boundaryLayer.loops) < 1: + return + insetBoundaryLoops = self.boundaryLayer.loops + if abs(self.repository.orbitalOutset.value) > 0.1 * abs(self.edgeWidth): + insetBoundaryLoops = intercircle.getInsetLoopsFromLoops(self.boundaryLayer.loops, -self.repository.orbitalOutset.value) + if len(insetBoundaryLoops) < 1: + insetBoundaryLoops = self.boundaryLayer.loops + largestLoop = euclidean.getLargestLoop(insetBoundaryLoops) + loopArea = euclidean.getAreaLoopAbsolute(largestLoop) + if loopArea < self.minimumArea: + center = 0.5 * (euclidean.getMaximumByComplexPath(largestLoop) + euclidean.getMinimumByComplexPath(largestLoop)) + centerXBounded = max(center.real, self.boundingRectangle.cornerMinimum.real) + centerXBounded = min(centerXBounded, self.boundingRectangle.cornerMaximum.real) + centerYBounded = max(center.imag, self.boundingRectangle.cornerMinimum.imag) + centerYBounded = min(centerYBounded, self.boundingRectangle.cornerMaximum.imag) + center = complex(centerXBounded, centerYBounded) + maximumCorner = center + self.halfCorner + minimumCorner = center - self.halfCorner + largestLoop = euclidean.getSquareLoopWiddershins(minimumCorner, maximumCorner) + pointComplex = euclidean.getXYComplexFromVector3(self.oldLocation) + if pointComplex != None: + largestLoop = euclidean.getLoopStartingClosest(self.edgeWidth, pointComplex, largestLoop) + intercircle.addOrbitsIfLarge( + self.distanceFeedRate, largestLoop, self.orbitalFeedRatePerSecond, remainingOrbitTime, self.highestZ) + + def addCoolTemperature(self, remainingOrbitTime): + 'Parse a gcode line and add it to the cool skein.' + layerCool = self.repository.maximumCool.value * remainingOrbitTime / self.repository.minimumLayerTime.value + if self.isBridgeLayer: + layerCool = max(self.repository.bridgeCool.value, layerCool) + if self.oldTemperature != None and layerCool != 0.0: + self.coolTemperature = self.oldTemperature - layerCool + self.addTemperature(self.coolTemperature) + + def addFlowRate(self, flowRate): + 'Add a multipled line of flow rate if different.' + self.distanceFeedRate.addLine('M108 S' + euclidean.getFourSignificantFigures(flowRate)) + + def addGcodeFromFeedRateMovementZ(self, feedRateMinute, point, z): + 'Add a movement to the output.' + self.distanceFeedRate.addLine(self.distanceFeedRate.getLinearGcodeMovementWithFeedRate(feedRateMinute, point, z)) + + def addOrbitsIfNecessary(self, remainingOrbitTime): + 'Parse a gcode line and add it to the cool skein.' + if remainingOrbitTime > 0.0 and self.boundaryLayer != None: + self.addCoolOrbits(remainingOrbitTime) + + def addTemperature(self, temperature): + 'Add a line of temperature.' + self.distanceFeedRate.addLine('M104 S' + euclidean.getRoundedToThreePlaces(temperature)) + + def getCoolMove(self, line, location, splitLine): + 'Get cool line according to time spent on layer.' + self.feedRateMinute = gcodec.getFeedRateMinute(self.feedRateMinute, splitLine) + return self.distanceFeedRate.getLineWithFeedRate(self.multiplier * self.feedRateMinute, line, splitLine) + + def getCraftedGcode(self, gcodeText, repository): + 'Parse gcode text and store the cool gcode.' + self.repository = repository + self.coolEndLines = settings.getAlterationFileLines(repository.nameOfCoolEndFile.value) + self.coolStartLines = settings.getAlterationFileLines(repository.nameOfCoolStartFile.value) + self.halfCorner = complex(repository.minimumOrbitalRadius.value, repository.minimumOrbitalRadius.value) + self.lines = archive.getTextLines(gcodeText) + self.minimumArea = 4.0 * repository.minimumOrbitalRadius.value * repository.minimumOrbitalRadius.value + self.parseInitialization() + self.boundingRectangle = gcodec.BoundingRectangle().getFromGcodeLines(self.lines[self.lineIndex :], 0.5 * self.edgeWidth) + margin = 0.2 * self.edgeWidth + halfCornerMargin = self.halfCorner + complex(margin, margin) + self.boundingRectangle.cornerMaximum -= halfCornerMargin + self.boundingRectangle.cornerMinimum += halfCornerMargin + for self.lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[self.lineIndex] + self.parseLine(line) + if repository.turnFanOffAtEnding.value: + self.distanceFeedRate.addLine('M107') + return gcodec.getGcodeWithoutDuplication('M108', self.distanceFeedRate.output.getvalue()) + + def getLayerTime(self): + 'Get the time the extruder spends on the layer.' + feedRateMinute = self.feedRateMinute + layerTime = 0.0 + lastThreadLocation = self.oldLocation + for lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'G1': + location = gcodec.getLocationFromSplitLine(lastThreadLocation, splitLine) + feedRateMinute = gcodec.getFeedRateMinute(feedRateMinute, splitLine) + if lastThreadLocation != None: + feedRateSecond = feedRateMinute / 60.0 + layerTime += location.distance(lastThreadLocation) / feedRateSecond + lastThreadLocation = location + elif firstWord == '(': + self.isBridgeLayer = True + elif firstWord == '()': + return layerTime + return layerTime + + def getLayerTimeActive(self): + 'Get the time the extruder spends on the layer while active.' + feedRateMinute = self.feedRateMinute + isExtruderActive = self.isExtruderActive + layerTime = 0.0 + lastThreadLocation = self.oldLocation + for lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'G1': + location = gcodec.getLocationFromSplitLine(lastThreadLocation, splitLine) + feedRateMinute = gcodec.getFeedRateMinute(feedRateMinute, splitLine) + if lastThreadLocation != None and isExtruderActive: + feedRateSecond = feedRateMinute / 60.0 + layerTime += location.distance(lastThreadLocation) / feedRateSecond + lastThreadLocation = location + elif firstWord == 'M101': + isExtruderActive = True + elif firstWord == 'M103': + isExtruderActive = False + elif firstWord == '(': + self.isBridgeLayer = True + elif firstWord == '()': + return layerTime + return layerTime + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == 'M108': + self.oldFlowRate = float(splitLine[1][1 :]) + elif firstWord == '(': + self.edgeWidth = float(splitLine[1]) + if self.repository.turnFanOnAtBeginning.value: + self.distanceFeedRate.addLine('M106') + elif firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('cool') + return + elif firstWord == '(': + self.oldFlowRate = float(splitLine[1]) + elif firstWord == '(': + self.orbitalFeedRatePerSecond = float(splitLine[1]) + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + 'Parse a gcode line and add it to the cool skein.' + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + self.highestZ = max(location.z, self.highestZ) + if self.isExtruderActive: + line = self.getCoolMove(line, location, splitLine) + self.oldLocation = location + elif firstWord == 'M101': + self.isExtruderActive = True + elif firstWord == 'M103': + self.isExtruderActive = False + elif firstWord == 'M104': + self.oldTemperature = gcodec.getDoubleAfterFirstLetter(splitLine[1]) + elif firstWord == 'M108': + self.oldFlowRate = float(splitLine[1][1 :]) + self.addFlowRate(self.multiplier * self.oldFlowRate) + return + elif firstWord == '(': + self.boundaryLoop.append(gcodec.getLocationFromSplitLine(None, splitLine).dropAxis()) + elif firstWord == '(': + self.layerCount.printProgressIncrement('cool') + self.distanceFeedRate.addLine(line) + self.distanceFeedRate.addLinesSetAbsoluteDistanceMode(self.coolStartLines) + layerTime = self.getLayerTime() + remainingOrbitTime = max(self.repository.minimumLayerTime.value - layerTime, 0.0) + self.addCoolTemperature(remainingOrbitTime) + if self.repository.orbit.value: + self.addOrbitsIfNecessary(remainingOrbitTime) + else: + self.setMultiplier(remainingOrbitTime) + self.addFlowRate(self.multiplier * self.oldFlowRate) + z = float(splitLine[1]) + self.boundaryLayer = euclidean.LoopLayer(z) + self.highestZ = max(z, self.highestZ) + self.distanceFeedRate.addLinesSetAbsoluteDistanceMode(self.coolEndLines) + return + elif firstWord == '()': + self.isBridgeLayer = False + self.multiplier = 1.0 + if self.coolTemperature != None: + self.addTemperature(self.oldTemperature) + self.coolTemperature = None + self.addFlowRate(self.oldFlowRate) + elif firstWord == '()': + self.boundaryLoop = [] + self.boundaryLayer.loops.append(self.boundaryLoop) + self.distanceFeedRate.addLine(line) + + def setMultiplier(self, remainingOrbitTime): + 'Set the feed and flow rate multiplier.' + layerTimeActive = self.getLayerTimeActive() + self.multiplier = min(1.0, layerTimeActive / (remainingOrbitTime + layerTimeActive)) + + + +def main(): + 'Display the cool dialog.' + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/dimension.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/dimension.py new file mode 100644 index 0000000..713ad4d --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/dimension.py @@ -0,0 +1,409 @@ +#! /usr/bin/env python +""" +This page is in the table of contents. +Dimension adds Adrian's extruder distance E value so firmware does not have to calculate it on it's own and can set the extruder speed in relation to the distance that needs to be extruded. Some printers don't support this. Extruder distance is described at: + +http://blog.reprap.org/2009/05/4d-printing.html + +and in Erik de Bruijn's conversion script page at: + +http://objects.reprap.org/wiki/3D-to-5D-Gcode.php + +The dimension manual page is at: + +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Dimension + +Nophead wrote an excellent article on how to set the filament parameters: + +http://hydraraptor.blogspot.com/2011/03/spot-on-flow-rate.html + +==Operation== +The default 'Activate Dimension' checkbox is off. When it is on, the functions described below will work, when it is off, the functions will not be called. + +==Settings== +===Extrusion Distance Format Choice=== +Default is 'Absolute Extrusion Distance' because in Adrian's description the distance is absolute. In future, because the relative distances are smaller than the cumulative absolute distances, hopefully the firmware will be able to use relative distance. + +====Absolute Extrusion Distance==== +When selected, the extrusion distance output will be the total extrusion distance to that gcode line. + +====Relative Extrusion Distance==== +When selected, the extrusion distance output will be the extrusion distance from the last gcode line. + +===Extruder Retraction Speed=== +Default is 13.3 mm/s. + +Defines the extruder retraction feed rate. A high value will allow the retraction operation to complete before much material oozes out. If your extruder can handle it, this value should be much larger than your feed rate. + +As an example, I have a feed rate of 48 mm/s and a 'Extruder Retraction Speed' of 150 mm/s. + +===Filament=== +====Filament Diameter==== +Default is 2.8 millimeters. + +Defines the filament diameter. + +====Filament Packing Density==== +Default is 0.85. This is for ABS. + +Defines the effective filament packing density. + +The default value is so low for ABS because ABS is relatively soft and with a pinch wheel extruder the teeth of the pinch dig in farther, so it sees a smaller effective diameter. With a hard plastic like PLA the teeth of the pinch wheel don't dig in as far, so it sees a larger effective diameter, so feeds faster, so for PLA the value should be around 0.97. This is with Wade's hobbed bolt. The effect is less significant with larger pinch wheels. + +Overall, you'll have to find the optimal filament packing density by experiment. + +===Maximum E Value before Reset=== +Default: 91234.0 + +Defines the maximum E value before it is reset with the 'G92 E0' command line. The reason it is reset only after the maximum E value is reached is because at least one firmware takes time to reset. The problem with waiting until the E value is high before resetting is that more characters are sent. So if your firmware takes a lot of time to reset, set this parameter to a high value, if it doesn't set this parameter to a low value or even zero. + +===Minimum Travel for Retraction=== +Default: 1.0 millimeter + +Defines the minimum distance that the extruder head has to travel from the end of one thread to the beginning of another, in order to trigger the extruder retraction. Setting this to a high value means the extruder will retract only occasionally, setting it to a low value means the extruder will retract most of the time. + +===Retract Within Island=== +Default is off. + +When selected, retraction will work even when the next thread is within the same island. If it is not selected, retraction will only work when crossing a boundary. + +===Retraction Distance=== +Default is zero. + +Defines the amount the extruder retracts (sucks back) the extruded filament whenever an extruder stop is commanded. Using this seems to help prevent stringing. e.g. If set to 10 the extruder reverses the distance required to pull back 10mm of filament. In fact this does not actually happen but if you set this distance by trial and error you can get to a point where there is very little ooze from the extruder when it stops which is not normally the case. + +===Restart Extra Distance=== +Default is zero. + +Defines the restart extra distance when the thread restarts. The restart distance will be the retraction distance plus the restart extra distance. + +If this is greater than zero when the extruder starts this distance is added to the retract value giving extra filament. It can be a negative value in which case it is subtracted from the retraction distance. On some Repstrap machines a negative value can stop the build up of plastic that can occur at the start of edges. + +==Examples== +The following examples dimension the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and dimension.py. + +> python dimension.py +This brings up the dimension dialog. + +> python dimension.py Screw Holder Bottom.stl +The dimension tool is parsing the file: +Screw Holder Bottom.stl +.. +The dimension tool has created the file: +.. Screw Holder Bottom_dimension.gcode + +""" + +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from datetime import date +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import os +import sys + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCraftedText( fileName, gcodeText = '', repository=None): + 'Dimension a gcode file or text.' + return getCraftedTextFromText( archive.getTextIfEmpty(fileName, gcodeText), repository ) + +def getCraftedTextFromText(gcodeText, repository=None): + 'Dimension a gcode text.' + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'dimension'): + return gcodeText + if repository == None: + repository = settings.getReadRepository( DimensionRepository() ) + if not repository.activateDimension.value: + return gcodeText + return DimensionSkein().getCraftedGcode(gcodeText, repository) + +def getNewRepository(): + 'Get new repository.' + return DimensionRepository() + +def writeOutput(fileName, shouldAnalyze=True): + 'Dimension a gcode file.' + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'dimension', shouldAnalyze) + + +class DimensionRepository: + 'A class to handle the dimension settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.dimension.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Dimension', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Dimension') + self.activateDimension = settings.BooleanSetting().getFromValue('Activate Dimension', self, True ) + extrusionDistanceFormatLatentStringVar = settings.LatentStringVar() + self.extrusionDistanceFormatChoiceLabel = settings.LabelDisplay().getFromName('Extrusion Distance Format Choice: ', self ) + settings.Radio().getFromRadio( extrusionDistanceFormatLatentStringVar, 'Absolute Extrusion Distance', self, True ) + self.relativeExtrusionDistance = settings.Radio().getFromRadio( extrusionDistanceFormatLatentStringVar, 'Relative Extrusion Distance', self, False ) + self.extruderRetractionSpeed = settings.FloatSpin().getFromValue( 4.0, 'Extruder Retraction Speed (mm/s):', self, 34.0, 13.3 ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Filament -', self ) + self.filamentDiameter = settings.FloatSpin().getFromValue(1.0, 'Filament Diameter (mm):', self, 6.0, 2.89) + self.filamentPackingDensity = settings.FloatSpin().getFromValue(0.7, 'Filament Packing Density (ratio):', self, 1.0, 1.0) + settings.LabelSeparator().getFromRepository(self) + self.maximumEValueBeforeReset = settings.FloatSpin().getFromValue(0.0, 'Maximum E Value before Reset (float):', self, 999999.9, 91234.0) + self.minimumTravelForRetraction = settings.FloatSpin().getFromValue(0.0, 'Minimum Travel for Retraction (millimeters):', self, 2.0, 1.0) + self.retractWithinIsland = settings.BooleanSetting().getFromValue('Retract Within Island', self, False) + self.retractionDistance = settings.FloatSpin().getFromValue( 0.0, 'Retraction Distance (millimeters):', self, 100.0, 0.0 ) + self.restartExtraDistance = settings.FloatSpin().getFromValue( 0.0, 'Restart Extra Distance (millimeters):', self, 100.0, 0.0 ) + self.executeTitle = 'Dimension' + + def execute(self): + 'Dimension button has been clicked.' + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class DimensionSkein: + 'A class to dimension a skein of extrusions.' + def __init__(self): + 'Initialize.' + self.absoluteDistanceMode = True + self.boundaryLayers = [] + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.feedRateMinute = None + self.isExtruderActive = False + self.layerIndex = -1 + self.lineIndex = 0 + self.maximumZFeedRatePerSecond = None + self.oldLocation = None + self.operatingFlowRate = None + self.retractionRatio = 1.0 + self.totalExtrusionDistance = 0.0 + self.travelFeedRatePerSecond = None + self.zDistanceRatio = 5.0 + + def addLinearMoveExtrusionDistanceLine(self, extrusionDistance): + 'Get the extrusion distance string from the extrusion distance.' + if self.repository.extruderRetractionSpeed.value != 0.0: + self.distanceFeedRate.output.write('G1 F%s\n' % self.extruderRetractionSpeedMinuteString) + self.distanceFeedRate.output.write('G1%s\n' % self.getExtrusionDistanceStringFromExtrusionDistance(extrusionDistance)) + self.distanceFeedRate.output.write('G1 F%s\n' % self.distanceFeedRate.getRounded(self.feedRateMinute)) + + def getCraftedGcode(self, gcodeText, repository): + 'Parse gcode text and store the dimension gcode.' + self.repository = repository + filamentRadius = 0.5 * repository.filamentDiameter.value + filamentPackingArea = math.pi * filamentRadius * filamentRadius * repository.filamentPackingDensity.value + self.minimumTravelForRetraction = self.repository.minimumTravelForRetraction.value + self.doubleMinimumTravelForRetraction = self.minimumTravelForRetraction + self.minimumTravelForRetraction + self.lines = archive.getTextLines(gcodeText) + self.parseInitialization() + if not self.repository.retractWithinIsland.value: + self.parseBoundaries() + self.flowScaleSixty = 60.0 * self.layerHeight * self.edgeWidth / filamentPackingArea + if self.operatingFlowRate == None: + print('There is no operatingFlowRate so dimension will do nothing.') + return gcodeText + self.restartDistance = self.repository.retractionDistance.value + self.repository.restartExtraDistance.value + self.extruderRetractionSpeedMinuteString = self.distanceFeedRate.getRounded(60.0 * self.repository.extruderRetractionSpeed.value) + if self.maximumZFeedRatePerSecond != None and self.travelFeedRatePerSecond != None: + self.zDistanceRatio = self.travelFeedRatePerSecond / self.maximumZFeedRatePerSecond + for lineIndex in xrange(self.lineIndex, len(self.lines)): + self.parseLine( lineIndex ) + return self.distanceFeedRate.output.getvalue() + + def getDimensionedArcMovement(self, line, splitLine): + 'Get a dimensioned arc movement.' + if self.oldLocation == None: + return line + relativeLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + self.oldLocation += relativeLocation + distance = gcodec.getArcDistance(relativeLocation, splitLine) + return line + self.getExtrusionDistanceString(distance, splitLine) + + def getDimensionedLinearMovement( self, line, splitLine ): + 'Get a dimensioned linear movement.' + distance = 0.0 + if self.absoluteDistanceMode: + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + if self.oldLocation != None: + distance = abs( location - self.oldLocation ) + self.oldLocation = location + else: + if self.oldLocation == None: + print('Warning: There was no absolute location when the G91 command was parsed, so the absolute location will be set to the origin.') + self.oldLocation = Vector3() + location = gcodec.getLocationFromSplitLine(None, splitLine) + distance = abs( location ) + self.oldLocation += location + return line + self.getExtrusionDistanceString( distance, splitLine ) + + def getDistanceToNextThread(self, lineIndex): + 'Get the travel distance to the next thread.' + if self.oldLocation == None: + return None + isActive = False + location = self.oldLocation + for afterIndex in xrange(lineIndex + 1, len(self.lines)): + line = self.lines[afterIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'G1': + if isActive: + if not self.repository.retractWithinIsland.value: + locationEnclosureIndex = self.getSmallestEnclosureIndex(location.dropAxis()) + if locationEnclosureIndex != self.getSmallestEnclosureIndex(self.oldLocation.dropAxis()): + return None + locationMinusOld = location - self.oldLocation + xyTravel = abs(locationMinusOld.dropAxis()) + zTravelMultiplied = locationMinusOld.z * self.zDistanceRatio + return math.sqrt(xyTravel * xyTravel + zTravelMultiplied * zTravelMultiplied) + location = gcodec.getLocationFromSplitLine(location, splitLine) + elif firstWord == 'M101': + isActive = True + elif firstWord == 'M103': + isActive = False + return None + + def getExtrusionDistanceString( self, distance, splitLine ): + 'Get the extrusion distance string.' + self.feedRateMinute = gcodec.getFeedRateMinute( self.feedRateMinute, splitLine ) + if not self.isExtruderActive: + return '' + if distance == 0.0: + return '' + if distance < 0.0: + print('Warning, the distance is less than zero in getExtrusionDistanceString in dimension; so there will not be an E value') + print(distance) + print(splitLine) + return '' + scaledFlowRate = self.flowRate * self.flowScaleSixty + return self.getExtrusionDistanceStringFromExtrusionDistance(scaledFlowRate / self.feedRateMinute * distance) + + def getExtrusionDistanceStringFromExtrusionDistance(self, extrusionDistance): + 'Get the extrusion distance string from the extrusion distance.' + if self.repository.relativeExtrusionDistance.value: + return ' E' + self.distanceFeedRate.getRounded(extrusionDistance) + self.totalExtrusionDistance += extrusionDistance + return ' E' + self.distanceFeedRate.getRounded(self.totalExtrusionDistance) + + def getRetractionRatio(self, lineIndex): + 'Get the retraction ratio.' + distanceToNextThread = self.getDistanceToNextThread(lineIndex) + if distanceToNextThread == None: + return 1.0 + if distanceToNextThread >= self.doubleMinimumTravelForRetraction: + return 1.0 + if distanceToNextThread <= self.minimumTravelForRetraction: + return 0.0 + return (distanceToNextThread - self.minimumTravelForRetraction) / self.minimumTravelForRetraction + + def getSmallestEnclosureIndex(self, point): + 'Get the index of the smallest boundary loop which encloses the point.' + boundaryLayer = self.boundaryLayers[self.layerIndex] + for loopIndex, loop in enumerate(boundaryLayer.loops): + if euclidean.isPointInsideLoop(loop, point): + return loopIndex + return None + + def parseBoundaries(self): + 'Parse the boundaries and add them to the boundary layers.' + boundaryLoop = None + boundaryLayer = None + for line in self.lines[self.lineIndex :]: + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == '()': + boundaryLoop = None + elif firstWord == '(': + location = gcodec.getLocationFromSplitLine(None, splitLine) + if boundaryLoop == None: + boundaryLoop = [] + boundaryLayer.loops.append(boundaryLoop) + boundaryLoop.append(location.dropAxis()) + elif firstWord == '(': + boundaryLayer = euclidean.LoopLayer(float(splitLine[1])) + self.boundaryLayers.append(boundaryLayer) + for boundaryLayer in self.boundaryLayers: + triangle_mesh.sortLoopsInOrderOfArea(False, boundaryLayer.loops) + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('dimension') + return + elif firstWord == '(': + self.layerHeight = float(splitLine[1]) + elif firstWord == '(': + self.maximumZFeedRatePerSecond = float(splitLine[1]) + elif firstWord == '(': + self.maximumZFeedRatePerSecond = float(splitLine[1]) + elif firstWord == '(': + self.feedRateMinute = 60.0 * float(splitLine[1]) + elif firstWord == '(': + self.operatingFlowRate = float(splitLine[1]) + self.flowRate = self.operatingFlowRate + elif firstWord == '(': + self.edgeWidth = float(splitLine[1]) + elif firstWord == '(': + self.travelFeedRatePerSecond = float(splitLine[1]) + self.distanceFeedRate.addLine(line) + + def parseLine( self, lineIndex ): + 'Parse a gcode line and add it to the dimension skein.' + line = self.lines[lineIndex].lstrip() + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G2' or firstWord == 'G3': + line = self.getDimensionedArcMovement( line, splitLine ) + if firstWord == 'G1': + line = self.getDimensionedLinearMovement( line, splitLine ) + if firstWord == 'G90': + self.absoluteDistanceMode = True + elif firstWord == 'G91': + self.absoluteDistanceMode = False + elif firstWord == '(': + self.layerIndex += 1 + settings.printProgress(self.layerIndex, 'dimension') + elif firstWord == 'M101': + self.addLinearMoveExtrusionDistanceLine(self.restartDistance * self.retractionRatio) + if self.totalExtrusionDistance > self.repository.maximumEValueBeforeReset.value: + if not self.repository.relativeExtrusionDistance.value: + self.distanceFeedRate.addLine('G92 E0') + self.totalExtrusionDistance = 0.0 + self.isExtruderActive = True + elif firstWord == 'M103': + self.retractionRatio = self.getRetractionRatio(lineIndex) + self.addLinearMoveExtrusionDistanceLine(-self.repository.retractionDistance.value * self.retractionRatio) + self.isExtruderActive = False + elif firstWord == 'M108': + self.flowRate = float( splitLine[1][1 :] ) + self.distanceFeedRate.addLine(line) + + +def main(): + 'Display the dimension dialog.' + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/drill.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/drill.py new file mode 100644 index 0000000..6efeec6 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/drill.py @@ -0,0 +1,258 @@ +""" +This page is in the table of contents. +Drill is a script to drill down small holes. + +==Operation== +The default 'Activate Drill' checkbox is on. When it is on, the functions described below will work, when it is off, the functions will not be called. + +==Settings== +===Drilling Margin=== +The drill script will move the tool from the top of the hole plus the 'Drilling Margin on Top', to the bottom of the hole minus the 'Drilling Margin on Bottom'. + +===Drilling Margin on Top=== +Default is three millimeters. + +===Drilling Margin on Bottom=== +Default is one millimeter. + +==Examples== +The following examples drill the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and drill.py. + +> python drill.py +This brings up the drill dialog. + +> python drill.py Screw Holder Bottom.stl +The drill tool is parsing the file: +Screw Holder Bottom.stl +.. +The drill tool has created the file: +.. Screw Holder Bottom_drill.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import sys + + +__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 getCraftedText( fileName, text, repository=None): + "Drill a gcode linear move file or text." + return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository) + +def getCraftedTextFromText(gcodeText, repository=None): + "Drill a gcode linear move text." + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'drill'): + return gcodeText + if repository == None: + repository = settings.getReadRepository( DrillRepository() ) + if not repository.activateDrill.value: + return gcodeText + return DrillSkein().getCraftedGcode(gcodeText, repository) + +def getNewRepository(): + 'Get new repository.' + return DrillRepository() + +def getPolygonCenter( polygon ): + "Get the centroid of a polygon." + pointSum = complex() + areaSum = 0.0 + for pointIndex in xrange( len( polygon ) ): + pointBegin = polygon[pointIndex] + pointEnd = polygon[ (pointIndex + 1) % len( polygon ) ] + area = pointBegin.real * pointEnd.imag - pointBegin.imag * pointEnd.real + areaSum += area + pointSum += complex( pointBegin.real + pointEnd.real, pointBegin.imag + pointEnd.imag ) * area + return pointSum / 3.0 / areaSum + +def writeOutput(fileName, shouldAnalyze=True): + "Drill a gcode linear move file." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'drill', shouldAnalyze) + + +class ThreadLayer: + "A layer of loops and paths." + def __init__( self, z ): + "Thread layer constructor." + self.points = [] + self.z = z + + def __repr__(self): + "Get the string representation of this thread layer." + return '%s, %s' % ( self.z, self.points ) + + +class DrillRepository: + "A class to handle the drill settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.drill.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Drill', self, '') + self.activateDrill = settings.BooleanSetting().getFromValue('Activate Drill', self, True ) + self.drillingMarginOnBottom = settings.FloatSpin().getFromValue( 0.0, 'Drilling Margin on Bottom (millimeters):', self, 5.0, 1.0 ) + self.drillingMarginOnTop = settings.FloatSpin().getFromValue( 0.0, 'Drilling Margin on Top (millimeters):', self, 20.0, 3.0 ) + self.executeTitle = 'Drill' + + def execute(self): + "Drill button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class DrillSkein: + "A class to drill a skein of extrusions." + def __init__(self): + self.boundary = None + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.extruderActive = False + self.halfLayerThickness = 0.4 + self.isDrilled = False + self.lineIndex = 0 + self.lines = None + self.maximumDistance = 0.06 + self.oldLocation = None + self.threadLayer = None + self.threadLayers = [] + + def addDrillHoles(self): + "Parse a gcode line." + self.isDrilled = True + if len( self.threadLayers ) < 1: + return + topThreadLayer = self.threadLayers[0] + drillPoints = topThreadLayer.points + for drillPoint in drillPoints: + zTop = topThreadLayer.z + self.halfLayerThickness + self.repository.drillingMarginOnTop.value + drillingCenterDepth = self.getDrillingCenterDepth( topThreadLayer.z, drillPoint ) + zBottom = drillingCenterDepth - self.halfLayerThickness - self.repository.drillingMarginOnBottom.value + self.addGcodeFromVerticalThread( drillPoint, zTop, zBottom ) + + def addGcodeFromVerticalThread( self, point, zBegin, zEnd ): + "Add a thread to the output." + self.distanceFeedRate.addGcodeMovementZ( point, zBegin ) + self.distanceFeedRate.addLine('M101') # Turn extruder on. + self.distanceFeedRate.addGcodeMovementZ( point, zEnd ) + self.distanceFeedRate.addLine('M103') # Turn extruder off. + + def addThreadLayerIfNone(self): + "Add a thread layer if it is none." + if self.threadLayer != None: + return + self.threadLayer = ThreadLayer( self.layerZ ) + self.threadLayers.append( self.threadLayer ) + + def getCraftedGcode(self, gcodeText, repository): + "Parse gcode text and store the drill gcode." + self.lines = archive.getTextLines(gcodeText) + self.repository = repository + self.parseInitialization() + for line in self.lines[self.lineIndex :]: + self.parseNestedRing(line) + for line in self.lines[self.lineIndex :]: + self.parseLine(line) + return self.distanceFeedRate.output.getvalue() + + def getDrillingCenterDepth( self, drillingCenterDepth, drillPoint ): + "Get the drilling center depth." + for threadLayer in self.threadLayers[1 :]: + if self.isPointClose( drillPoint, threadLayer.points ): + drillingCenterDepth = threadLayer.z + else: + return drillingCenterDepth + return drillingCenterDepth + + def isPointClose( self, drillPoint, points ): + "Determine if a point on the thread layer is close." + for point in points: + if abs( point - drillPoint ) < self.maximumDistance: + return True + return False + + def linearMove( self, splitLine ): + "Add a linear move to the loop." + self.addThreadLayerIfNone() + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + if self.extruderActive: + self.boundary = None + self.oldLocation = location + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('drill') + return + elif firstWord == '(': + self.halfLayerThickness = 0.5 * float(splitLine[1]) + elif firstWord == '(': + self.maximumDistance = 0.1 * float(splitLine[1]) + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + "Parse a gcode line." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + self.distanceFeedRate.addLine(line) + if firstWord == '(': + if not self.isDrilled: + self.addDrillHoles() + + def parseNestedRing(self, line): + "Parse a nested ring." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + self.linearMove(splitLine) + if firstWord == 'M101': + self.extruderActive = True + elif firstWord == 'M103': + self.extruderActive = False + elif firstWord == '(': + location = gcodec.getLocationFromSplitLine(None, splitLine) + if self.boundary == None: + self.boundary = [] + self.boundary.append(location.dropAxis()) + elif firstWord == '(': + self.layerZ = float(splitLine[1]) + self.threadLayer = None + elif firstWord == '()': + self.addThreadLayerIfNone() + elif firstWord == '()': + if self.boundary != None: + self.threadLayer.points.append( getPolygonCenter( self.boundary ) ) + self.boundary = None + + +def main(): + "Display the drill dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/export.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/export.py new file mode 100644 index 0000000..d601612 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/export.py @@ -0,0 +1,436 @@ +""" +This page is in the table of contents. +Export is a craft tool to pick an export plugin, add information to the file name, and delete comments. + +The export manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Export + +==Operation== +The default 'Activate Export' checkbox is on. When it is on, the functions described below will work, when it is off, the functions will not be called. + +==Settings== +===Add Descriptive Extension=== +Default is off. + +When selected, key profile values will be added as an extension to the gcode file. For example: +test.04hx06w_03fill_2cx2r_33EL.gcode + +would mean: + +* . (Carve section.) +* 04h = 'Layer Height (mm):' 0.4 +* x +* 06w = 0.6 width i.e. 0.4 times 'Edge Width over Height (ratio):' 1.5 +* _ (Fill section.) +* 03fill = 'Infill Solidity (ratio):' 0.3 +* _ (Multiply section; if there is one column and one row then this section is not shown.) +* 2c = 'Number of Columns (integer):' 2 +* x +* 2r = 'Number of Rows (integer):' 2. +* _ (Speed section.) +* 33EL = 'Feed Rate (mm/s):' 33.0 and 'Flow Rate Setting (float):' 33.0. If either value has a positive value after the decimal place then this is also shown, but if it is zero it is hidden. Also, if the values differ (which they shouldn't with 5D volumetrics) then each should be displayed separately. For example, 35.2E30L = 'Feed Rate (mm/s):' 35.2 and 'Flow Rate Setting (float):' 30.0. + +===Add Profile Extension=== +Default is off. + +When selected, the current profile will be added to the file extension. For example: +test.my_profile_name.gcode + +===Add Timestamp Extension=== +Default is off. + +When selected, the current date and time is added as an extension in format YYYYmmdd_HHMMSS (so it is sortable if one has many files). For example: +test.my_profile_name.20110613_220113.gcode + +===Also Send Output To=== +Default is empty. + +Defines the output name for sending to a file or pipe. A common choice is stdout to print the output in the shell screen. Another common choice is stderr. With the empty default, nothing will be done. If the value is anything else, the output will be written to that file name. + +===Analyze Gcode=== +Default is on. + +When selected, the penultimate gcode will be sent to the analyze plugins to be analyzed and viewed. + +===Comment Choice=== +Default is 'Delete All Comments'. + +====Do Not Delete Comments==== +When selected, export will not delete comments. Crafting comments slow down the processing in many firmware types, which leads to pauses and therefore a lower quality print. + +====Delete Crafting Comments==== +When selected, export will delete the time consuming crafting comments, but leave the initialization comments. Since the crafting comments are deleted, there are no pauses during extrusion. The remaining initialization comments provide some useful information for the analyze tools. + +====Delete All Comments==== +When selected, export will delete all comments. The comments are not necessary to run a fabricator. Some printers do not support comments at all so the safest way is choose this option. + +===Export Operations=== +Export presents the user with a choice of the export plugins in the export_plugins folder. The chosen plugin will then modify the gcode or translate it into another format. There is also the "Do Not Change Output" choice, which will not change the output. An export plugin is a script in the export_plugins folder which has the getOutput function, the globalIsReplaceable variable and if it's output is not replaceable, the writeOutput function. + +===File Extension=== +Default is gcode. + +Defines the file extension added to the name of the output file. The output file will be named as originalname_export.extension so if you are processing XYZ.stl the output will by default be XYZ_export.gcode + +===Name of Replace File=== +Default is replace.csv. + +When export is exporting the code, if there is a tab separated file with the name of the "Name of Replace File" setting, it will replace the string in the first column by its replacement in the second column. If there is nothing in the second column, the first column string will be deleted, if this leads to an empty line, the line will be deleted. If there are replacement columns after the second, they will be added as extra lines of text. There is an example file replace_example.csv to demonstrate the tab separated format, which can be edited in a text editor or a spreadsheet. + +Export looks for the alteration file in the alterations folder in the .skeinforge folder in the home directory. Export does not care if the text file names are capitalized, but some file systems do not handle file name cases properly, so to be on the safe side you should give them lower case names. If it doesn't find the file it then looks in the alterations folder in the skeinforge_plugins folder. + +===Save Penultimate Gcode=== +Default is off. + +When selected, export will save the gcode file with the suffix '_penultimate.gcode' just before it is exported. This is useful because the code after it is exported could be in a form which the viewers can not display well. + +==Examples== +The following examples export the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and export.py. + +> python export.py +This brings up the export dialog. + +> python export.py Screw Holder Bottom.stl +The export tool is parsing the file: +Screw Holder Bottom.stl +.. +The export tool has created the file: +.. Screw Holder Bottom_export.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_analyze +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import cStringIO +import os +import sys +import time + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__credits__ = 'Gary Hodgson ' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCraftedTextFromText(gcodeText, repository=None): + 'Export a gcode linear move text.' + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'export'): + return gcodeText + if repository == None: + repository = settings.getReadRepository(ExportRepository()) + if not repository.activateExport.value: + return gcodeText + return ExportSkein().getCraftedGcode(repository, gcodeText) + +def getDescriptionCarve(lines): + 'Get the description for carve.' + descriptionCarve = '' + layerThicknessString = getSettingString(lines, 'carve', 'Layer Height') + if layerThicknessString != None: + descriptionCarve += layerThicknessString.replace('.', '') + 'h' + edgeWidthString = getSettingString(lines, 'carve', 'Edge Width over Height') + if edgeWidthString != None: + descriptionCarve += 'x%sw' % str(float(edgeWidthString) * float(layerThicknessString)).replace('.', '') + return descriptionCarve + +def getDescriptionFill(lines): + 'Get the description for fill.' + activateFillString = getSettingString(lines, 'fill', 'Activate Fill') + if activateFillString == None or activateFillString == 'False': + return '' + infillSolidityString = getSettingString(lines, 'fill', 'Infill Solidity') + return '_' + infillSolidityString.replace('.', '') + 'fill' + +def getDescriptionMultiply(lines): + 'Get the description for multiply.' + activateMultiplyString = getSettingString(lines, 'multiply', 'Activate Multiply') + if activateMultiplyString == None or activateMultiplyString == 'False': + return '' + columnsString = getSettingString(lines, 'multiply', 'Number of Columns') + rowsString = getSettingString(lines, 'multiply', 'Number of Rows') + if columnsString == '1' and rowsString == '1': + return '' + return '_%scx%sr' % (columnsString, rowsString) + +def getDescriptionSpeed(lines): + 'Get the description for speed.' + activateSpeedString = getSettingString(lines, 'speed', 'Activate Speed') + if activateSpeedString == None or activateSpeedString == 'False': + return '' + feedRateString = getSettingString(lines, 'speed', 'Feed Rate') + flowRateString = getSettingString(lines, 'speed', 'Flow Rate') + if feedRateString == flowRateString: + return '_%sEL' % feedRateString.replace('.0', '') + return '_%sE%sL' % (feedRateString.replace('.0', ''), flowRateString.replace('.0', '')) + +def getDescriptiveExtension(gcodeText): + 'Get the descriptive extension.' + lines = archive.getTextLines(gcodeText) + return '.' + getDescriptionCarve(lines) + getDescriptionFill(lines) + getDescriptionMultiply(lines) + getDescriptionSpeed(lines) + +def getDistanceGcode(exportText): + 'Get gcode lines with distance variable added, this is for if ever there is distance code.' + lines = archive.getTextLines(exportText) + oldLocation = None + for line in lines: + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = None + if len(splitLine) > 0: + firstWord = splitLine[0] + if firstWord == 'G1': + location = gcodec.getLocationFromSplitLine(oldLocation, splitLine) + if oldLocation != None: + distance = location.distance(oldLocation) + oldLocation = location + return exportText + +def getFirstValue(gcodeText, word): + 'Get the value from the first line which starts with the given word.' + for line in archive.getTextLines(gcodeText): + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if gcodec.getFirstWord(splitLine) == word: + return splitLine[1] + return '' + +def getNewRepository(): + 'Get new repository.' + return ExportRepository() + +def getReplaceableExportGcode(nameOfReplaceFile, replaceableExportGcode): + 'Get text with strings replaced according to replace.csv file.' + replaceLines = settings.getAlterationLines(nameOfReplaceFile) + if len(replaceLines) < 1: + return replaceableExportGcode + for replaceLine in replaceLines: + splitLine = replaceLine.replace('\\n', '\t').split('\t') + if len(splitLine) > 0: + replaceableExportGcode = replaceableExportGcode.replace(splitLine[0], '\n'.join(splitLine[1 :])) + output = cStringIO.StringIO() + gcodec.addLinesToCString(output, archive.getTextLines(replaceableExportGcode)) + return output.getvalue() + +def getSelectedPluginModule( plugins ): + 'Get the selected plugin module.' + for plugin in plugins: + if plugin.value: + return archive.getModuleWithDirectoryPath( plugin.directoryPath, plugin.name ) + return None + +def getSettingString(lines, procedureName, settingNameStart): + 'Get the setting value from the lines, return None if there is no setting starting with that name.' + settingNameStart = settingNameStart.replace(' ', '_') + for line in lines: + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = None + if len(splitLine) > 0: + firstWord = splitLine[0] + if firstWord == '(': + if len(splitLine) > 4: + if splitLine[1] == procedureName and splitLine[2].startswith(settingNameStart): + return splitLine[3] + elif firstWord == '()': + return None + return None + +def sendOutputTo(outputTo, text): + 'Send output to a file or a standard output.' + if outputTo.endswith('stderr'): + sys.stderr.write(text) + sys.stderr.write('\n') + sys.stderr.flush() + return + if outputTo.endswith('stdout'): + sys.stdout.write(text) + sys.stdout.write('\n') + sys.stdout.flush() + return + archive.writeFileText(outputTo, text) + +def writeOutput(fileName, shouldAnalyze=True): + 'Export a gcode linear move file.' + if fileName == '': + return None + repository = ExportRepository() + settings.getReadRepository(repository) + startTime = time.time() + print('File ' + archive.getSummarizedFileName(fileName) + ' is being chain exported.') + fileNameSuffix = fileName[: fileName.rfind('.')] + if repository.addExportSuffix.value: + fileNameSuffix += '_export' + gcodeText = gcodec.getGcodeFileText(fileName, '') + procedures = skeinforge_craft.getProcedures('export', gcodeText) + gcodeText = skeinforge_craft.getChainTextFromProcedures(fileName, procedures[: -1], gcodeText) + if gcodeText == '': + return None + if repository.addProfileExtension.value: + fileNameSuffix += '.' + getFirstValue(gcodeText, '(') + if repository.addDescriptiveExtension.value: + fileNameSuffix += getDescriptiveExtension(gcodeText) + if repository.addTimestampExtension.value: + fileNameSuffix += '.' + getFirstValue(gcodeText, '(') + fileNameSuffix += '.' + repository.fileExtension.value + fileNamePenultimate = fileName[: fileName.rfind('.')] + '_penultimate.gcode' + filePenultimateWritten = False + if repository.savePenultimateGcode.value: + archive.writeFileText(fileNamePenultimate, gcodeText) + filePenultimateWritten = True + print('The penultimate file is saved as ' + archive.getSummarizedFileName(fileNamePenultimate)) + exportGcode = getCraftedTextFromText(gcodeText, repository) + window = None + if shouldAnalyze and repository.analyzeGcode.value: + window = skeinforge_analyze.writeOutput(fileName, fileNamePenultimate, fileNameSuffix, filePenultimateWritten, gcodeText) + replaceableExportGcode = None + selectedPluginModule = getSelectedPluginModule(repository.exportPlugins) + if selectedPluginModule == None: + replaceableExportGcode = exportGcode + else: + if selectedPluginModule.globalIsReplaceable: + replaceableExportGcode = selectedPluginModule.getOutput(exportGcode) + else: + selectedPluginModule.writeOutput(fileNameSuffix, exportGcode) + if replaceableExportGcode != None: + replaceableExportGcode = getReplaceableExportGcode(repository.nameOfReplaceFile.value, replaceableExportGcode) + archive.writeFileText( fileNameSuffix, replaceableExportGcode ) + print('The exported file is saved as ' + archive.getSummarizedFileName(fileNameSuffix)) + if repository.alsoSendOutputTo.value != '': + if replaceableExportGcode == None: + replaceableExportGcode = selectedPluginModule.getOutput(exportGcode) + sendOutputTo(repository.alsoSendOutputTo.value, replaceableExportGcode) + print('It took %s to export the file.' % euclidean.getDurationString(time.time() - startTime)) + return window + + +class ExportRepository: + 'A class to handle the export settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.export.html', self) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Export', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Export') + self.activateExport = settings.BooleanSetting().getFromValue('Activate Export', self, True) + self.addDescriptiveExtension = settings.BooleanSetting().getFromValue('Add Descriptive Extension', self, False) + self.addExportSuffix = settings.BooleanSetting().getFromValue('Add Export Suffix', self, True) + self.addProfileExtension = settings.BooleanSetting().getFromValue('Add Profile Extension', self, False) + self.addTimestampExtension = settings.BooleanSetting().getFromValue('Add Timestamp Extension', self, False) + self.alsoSendOutputTo = settings.StringSetting().getFromValue('Also Send Output To:', self, '') + self.analyzeGcode = settings.BooleanSetting().getFromValue('Analyze Gcode', self, True) + self.commentChoice = settings.MenuButtonDisplay().getFromName('Comment Choice:', self) + self.doNotDeleteComments = settings.MenuRadio().getFromMenuButtonDisplay(self.commentChoice, 'Do Not Delete Comments', self, False) + self.deleteCraftingComments = settings.MenuRadio().getFromMenuButtonDisplay(self.commentChoice, 'Delete Crafting Comments', self, False) + self.deleteAllComments = settings.MenuRadio().getFromMenuButtonDisplay(self.commentChoice, 'Delete All Comments', self, True) + exportPluginsFolderPath = archive.getAbsoluteFrozenFolderPath(archive.getCraftPluginsDirectoryPath('export.py'), 'export_plugins') + exportStaticDirectoryPath = os.path.join(exportPluginsFolderPath, 'static_plugins') + exportPluginFileNames = archive.getPluginFileNamesFromDirectoryPath(exportPluginsFolderPath) + exportStaticPluginFileNames = archive.getPluginFileNamesFromDirectoryPath(exportStaticDirectoryPath) + self.exportLabel = settings.LabelDisplay().getFromName('Export Operations: ', self) + self.exportPlugins = [] + exportLatentStringVar = settings.LatentStringVar() + self.doNotChangeOutput = settings.RadioCapitalized().getFromRadio(exportLatentStringVar, 'Do Not Change Output', self, False) + self.doNotChangeOutput.directoryPath = None + allExportPluginFileNames = exportPluginFileNames + exportStaticPluginFileNames + for exportPluginFileName in allExportPluginFileNames: + exportPlugin = None + default = False + if exportPluginFileName == "gcode_small": + default = True + if exportPluginFileName in exportPluginFileNames: + path = os.path.join(exportPluginsFolderPath, exportPluginFileName) + exportPlugin = settings.RadioCapitalizedButton().getFromPath(exportLatentStringVar, exportPluginFileName, path, self, default) + exportPlugin.directoryPath = exportPluginsFolderPath + else: + exportPlugin = settings.RadioCapitalized().getFromRadio(exportLatentStringVar, exportPluginFileName, self, default) + exportPlugin.directoryPath = exportStaticDirectoryPath + self.exportPlugins.append(exportPlugin) + self.fileExtension = settings.StringSetting().getFromValue('File Extension:', self, 'gcode') + self.nameOfReplaceFile = settings.StringSetting().getFromValue('Name of Replace File:', self, 'replace.csv') + self.savePenultimateGcode = settings.BooleanSetting().getFromValue('Save Penultimate Gcode', self, False) + self.executeTitle = 'Export' + + def execute(self): + 'Export button has been clicked.' + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class ExportSkein: + 'A class to export a skein of extrusions.' + def __init__(self): + self.crafting = False + self.decimalPlacesExported = 2 + self.output = cStringIO.StringIO() + + def addLine(self, line): + 'Add a line of text and a newline to the output.' + if line != '': + self.output.write(line + '\n') + + def getCraftedGcode( self, repository, gcodeText ): + 'Parse gcode text and store the export gcode.' + self.repository = repository + lines = archive.getTextLines(gcodeText) + for line in lines: + self.parseLine(line) + return self.output.getvalue() + + def getLineWithTruncatedNumber(self, character, line, splitLine): + 'Get a line with the number after the character truncated.' + numberString = gcodec.getStringFromCharacterSplitLine(character, splitLine) + if numberString == None: + return line + roundedNumberString = euclidean.getRoundedToPlacesString(self.decimalPlacesExported, float(numberString)) + return gcodec.getLineWithValueString(character, line, splitLine, roundedNumberString) + + def parseLine(self, line): + 'Parse a gcode line.' + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == '()': + self.crafting = False + elif firstWord == '(': + self.decimalPlacesExported = int(splitLine[1]) - 1 + if self.repository.deleteAllComments.value or (self.repository.deleteCraftingComments.value and self.crafting): + if firstWord[0] == '(': + return + else: + line = line.split(';')[0].split('(')[0].strip() + if firstWord == '()': + self.crafting = True + if firstWord == '()': + self.addLine(gcodec.getTagBracketedProcedure('export')) + if firstWord != 'G1' and firstWord != 'G2' and firstWord != 'G3' : + self.addLine(line) + return + line = self.getLineWithTruncatedNumber('X', line, splitLine) + line = self.getLineWithTruncatedNumber('Y', line, splitLine) + line = self.getLineWithTruncatedNumber('Z', line, splitLine) + line = self.getLineWithTruncatedNumber('I', line, splitLine) + line = self.getLineWithTruncatedNumber('J', line, splitLine) + line = self.getLineWithTruncatedNumber('R', line, splitLine) + self.addLine(line) + + +def main(): + 'Display the export dialog.' + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/__init__.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/__init__.py new file mode 100644 index 0000000..58ec332 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 4 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/binary_16_byte.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/binary_16_byte.py new file mode 100644 index 0000000..67604fe --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/binary_16_byte.py @@ -0,0 +1,231 @@ +""" +This page is in the table of contents. +Binary 16 byte is an export plugin to convert gcode into 16 byte binary segments. + +An export plugin is a script in the export_plugins folder which has the getOutput function, the globalIsReplaceable variable and if it's output is not replaceable, the writeOutput function. It is meant to be run from the export tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name. + +The getOutput function of this script takes a gcode text and returns that text converted into 16 byte segments. The writeOutput function of this script takes a gcode text and writes that in a binary format converted into 16 byte segments. + +This plugin is just a starter to make a real binary converter. + +==Settings== +===Feed Rate Step Length=== +Default is 0.1 millimeters/second. + +Defines the feed rate step length. + +===File Extension=== +Default is bin. + +Defines the file extension suffix. + +===Offset=== +====X Offset==== +Default is zero. + +Defines the X Offset. + +====Y Offset==== +Default is zero. + +Defines the Y Offset. + +====Z Offset==== +Default is zero. + +Defines the Z Offset. + +===Step Length=== +====X Step Length==== +Default is 0.1 millimeters. + +Defines the X axis step length. + +====Y Step Length==== +Default is 0.1 millimeters. + +Defines the Y axis step length. + +====Z Step Length==== +Default is 0.01 millimeters. + +Defines the Z axis step length. + +==Record structure== +BinArray(0) = AscW(Inst_Code_Letter) +BinArray(1) = cInst_Code + +X Data +sInt32_to_Hbytes(iXdim_1) +BinArray(2) = lsb 'short lsb +BinArray(3) = msb 'short msb + +Y Data +sInt32_to_Hbytes(iYdim_2) +BinArray(4) = lsb 'short lsb +BinArray(5) = msb 'short msb + +Z Data +sInt32_to_Hbytes(iZdim_3) +BinArray(6) = lsb 'short lsb +BinArray(7) = msb 'short msb + +I Data +sInt32_to_Hbytes(iIdim_4) +BinArray(8) = lsb 'short lsb +BinArray(9) = msb 'short msb + +J Data +sInt32_to_Hbytes(iJdim_5) +BinArray(10) = lsb 'short lsb +BinArray(11) = msb 'short msb + +BinArray(12) = FP_Char +sInt32_to_Hbytes(iFP_Num) +BinArray(13) = lsb 'short lsb + +BinArray(14) = bActiveFlags + +BinArray(15) = AscW("#")End of record filler + +Byte 14 is worth a few extra notes, this byte is used to define which of the axes are active, its used to get round the problem of say a line of code with no mention of z. This would be put into the file as z = 0 as the space for this data is reserved, if we did nothing, this would instruct the machine to go to z = 0. If we use the active flag to define the z axis as inactive the z = 0 is ignored and the value set to the last saved value of z, i.e it does not move. If the z data is actually set to z = 0 then the axis would be set to active and the move takes place. + +""" + +from __future__ import absolute_import +import __init__ +from fabmetheus_utilities import archive +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +from struct import Struct +import cStringIO +import os +import sys + + +__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' + + +# This is true if the output is text and false if it is binary." +globalIsReplaceable = False + + +def getIntegerFlagFromCharacterSplitLine(character, splitLine): + "Get the integer flag after the first occurence of the character in the split line." + lineFromCharacter = gcodec.getStringFromCharacterSplitLine(character, splitLine) + if lineFromCharacter == None: + return 0 + return 1 + +def getIntegerFromCharacterLengthLineOffset( character, offset, splitLine, stepLength ): + "Get the integer after the first occurence of the character in the split line." + lineFromCharacter = gcodec.getStringFromCharacterSplitLine(character, splitLine) + if lineFromCharacter == None: + return 0 + floatValue = ( float( lineFromCharacter ) + offset ) / stepLength + return int( round( floatValue ) ) + +def getNewRepository(): + 'Get new repository.' + return Binary16ByteRepository() + +def getOutput( gcodeText, binary16ByteRepository = None ): + 'Get the exported version of a gcode file.' + if gcodeText == '': + return '' + if binary16ByteRepository == None: + binary16ByteRepository = Binary16ByteRepository() + settings.getReadRepository( binary16ByteRepository ) + return Binary16ByteSkein().getCraftedGcode( gcodeText, binary16ByteRepository ) + +def writeOutput( fileName, gcodeText = ''): + "Write the exported version of a gcode file." + binary16ByteRepository = Binary16ByteRepository() + settings.getReadRepository( binary16ByteRepository ) + gcodeText = gcodec.getGcodeFileText(fileName, gcodeText) + skeinOutput = getOutput( gcodeText, binary16ByteRepository ) + suffixFileName = fileName[ : fileName.rfind('.') ] + '.' + binary16ByteRepository.fileExtension.value + archive.writeFileText( suffixFileName, skeinOutput ) + print('The converted file is saved as ' + archive.getSummarizedFileName(suffixFileName) ) + + +class Binary16ByteRepository: + "A class to handle the export settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + #Set the default settings. + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.binary_16_byte.html', self) + self.fileNameInput = settings.FileNameInput().getFromFileName( [ ('Gcode text files', '*.gcode') ], 'Open File to be Converted to Binary 16 Byte', self, '') + self.feedRateStepLength = settings.FloatSpin().getFromValue( 0.0, 'Feed Rate Step Length (millimeters/second)', self, 1.0, 0.1 ) + self.fileExtension = settings.StringSetting().getFromValue('File Extension:', self, 'bin') + settings.LabelDisplay().getFromName('Offset:', self ) + self.xOffset = settings.FloatSpin().getFromValue( - 100.0, 'X Offset (millimeters)', self, 100.0, 0.0 ) + self.yOffset = settings.FloatSpin().getFromValue( -100.0, 'Y Offset (millimeters)', self, 100.0, 0.0 ) + self.zOffset = settings.FloatSpin().getFromValue( - 10.0, 'Z Offset (millimeters)', self, 10.0, 0.0 ) + settings.LabelDisplay().getFromName('Step Length:', self ) + self.xStepLength = settings.FloatSpin().getFromValue( 0.0, 'X Step Length (millimeters)', self, 1.0, 0.1 ) + self.yStepLength = settings.FloatSpin().getFromValue( 0.0, 'Y Step Length (millimeters)', self, 1.0, 0.1 ) + self.zStepLength = settings.FloatSpin().getFromValue( 0.0, 'Z Step Length (millimeters)', self, 0.2, 0.01 ) + self.executeTitle = 'Convert to Binary 16 Byte' + + def execute(self): + "Convert to binary 16 byte button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode( self.fileNameInput.value, ['.gcode'], self.fileNameInput.wasCancelled ) + for fileName in fileNames: + writeOutput(fileName) + + +class Binary16ByteSkein: + "A class to convert gcode into 16 byte binary segments." + def __init__(self): + self.output = cStringIO.StringIO() + + def getCraftedGcode( self, gcodeText, binary16ByteRepository ): + "Parse gcode text and store the gcode." + self.binary16ByteRepository = binary16ByteRepository + lines = archive.getTextLines(gcodeText) + for line in lines: + self.parseLine(line) + return self.output.getvalue() + + def parseLine(self, line): + "Parse a gcode line." + binary16ByteRepository = self.binary16ByteRepository + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if len(firstWord) < 1: + return + firstLetter = firstWord[0] + if firstLetter == '(': + return + feedRateInteger = getIntegerFromCharacterLengthLineOffset('F', 0.0, splitLine, binary16ByteRepository.feedRateStepLength.value ) + iInteger = getIntegerFromCharacterLengthLineOffset('I', 0.0, splitLine, binary16ByteRepository.xStepLength.value ) + jInteger = getIntegerFromCharacterLengthLineOffset('J', 0.0, splitLine, binary16ByteRepository.yStepLength.value ) + xInteger = getIntegerFromCharacterLengthLineOffset('X', binary16ByteRepository.xOffset.value, splitLine, binary16ByteRepository.xStepLength.value ) + yInteger = getIntegerFromCharacterLengthLineOffset('Y', binary16ByteRepository.yOffset.value, splitLine, binary16ByteRepository.yStepLength.value ) + zInteger = getIntegerFromCharacterLengthLineOffset('Z', binary16ByteRepository.zOffset.value, splitLine, binary16ByteRepository.zStepLength.value ) + sixteenByteStruct = Struct('cBhhhhhhBc') + flagInteger = getIntegerFlagFromCharacterSplitLine('X', splitLine ) + flagInteger += 2 * getIntegerFlagFromCharacterSplitLine('Y', splitLine ) + flagInteger += 4 * getIntegerFlagFromCharacterSplitLine('Z', splitLine ) + flagInteger += 8 * getIntegerFlagFromCharacterSplitLine('I', splitLine ) + flagInteger += 16 * getIntegerFlagFromCharacterSplitLine('J', splitLine ) + flagInteger += 32 * getIntegerFlagFromCharacterSplitLine('F', splitLine ) + packedString = sixteenByteStruct.pack( firstLetter, int( firstWord[1 :] ), xInteger, yInteger, zInteger, iInteger, jInteger, feedRateInteger, flagInteger, '#') + self.output.write( packedString ) + + +def main(): + "Display the export dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/gcode_step.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/gcode_step.py new file mode 100644 index 0000000..f0abbdf --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/gcode_step.py @@ -0,0 +1,242 @@ +""" +This page is in the table of contents. +Gcode step is an export plugin to convert gcode from float position to number of steps. + +An export plugin is a script in the export_plugins folder which has the getOutput function, the globalIsReplaceable variable and if it's output is not replaceable, the writeOutput function. It is meant to be run from the export tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name. + +The getOutput function of this script takes a gcode text and returns it with the positions converted into number of steps. The writeOutput function of this script takes a gcode text and writes that with the positions converted into number of steps. + +==Settings== +===Add Feed Rate Even When Unchanging=== +Default is on. + +When selected, the feed rate will be added even when it did not change from the previous line. + +===Add Space Between Words=== +Default is on. + +When selected, a space will be added between each gcode word. + +===Add Z Even When Unchanging=== +Default is on. + +When selected, the z word will be added even when it did not change. + +===Feed Rate Step Length=== +Default is 0.1 millimeters/second. + +Defines the feed rate step length. + +===Offset=== +====X Offset==== +Default is zero. + +Defines the X Offset. + +====Y Offset==== +Default is zero. + +Defines the Y Offset. + +====Z Offset==== +Default is zero. + +Defines the Z Offset. + +===Step Length=== +====E Step Length==== +Default is 0.1 millimeters. + +Defines the E extrusion distance step length. + +===Radius Rate Step Length=== +Default is 0.1 millimeters/second. + +Defines the radius step length. + +====X Step Length==== +Default is 0.1 millimeters. + +Defines the X axis step length. + +====Y Step Length==== +Default is 0.1 millimeters. + +Defines the Y axis step length. + +====Z Step Length==== +Default is 0.01 millimeters. + +Defines the Z axis step length. + +""" + + +from __future__ import absolute_import +import __init__ +from fabmetheus_utilities import archive +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +from struct import Struct +import cStringIO +import os +import sys + + +__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' + + +# This is true if the output is text and false if it is binary. +globalIsReplaceable = True + + +def getCharacterIntegerString(character, offset, splitLine, stepLength): + 'Get a character and integer string.' + floatValue = getFloatFromCharacterSplitLine(character, splitLine) + if floatValue == None: + return '' + floatValue += offset + integerValue = int(round(float(floatValue / stepLength))) + return character + str(integerValue) + +def getFloatFromCharacterSplitLine(character, splitLine): + 'Get the float after the first occurence of the character in the split line.' + lineFromCharacter = gcodec.getStringFromCharacterSplitLine(character, splitLine) + if lineFromCharacter == None: + return None + return float(lineFromCharacter) + +def getNewRepository(): + 'Get new repository.' + return GcodeStepRepository() + +def getOutput(gcodeText, repository=None): + 'Get the exported version of a gcode file.' + if gcodeText == '': + return '' + if repository == None: + repository = GcodeStepRepository() + settings.getReadRepository(repository) + return GcodeStepSkein().getCraftedGcode(repository, gcodeText) + +def writeOutput( fileName, gcodeText = ''): + 'Write the exported version of a gcode file.' + gcodeText = gcodec.getGcodeFileText(fileName, gcodeText) + repository = GcodeStepRepository() + settings.getReadRepository(repository) + output = getOutput(gcodeText, repository) + suffixFileName = fileName[: fileName.rfind('.')] + '_gcode_step.gcode' + archive.writeFileText(suffixFileName, output) + print('The converted file is saved as ' + archive.getSummarizedFileName(suffixFileName)) + + +class GcodeStepRepository: + 'A class to handle the export settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.gcode_step.html', self) + self.addFeedRateEvenWhenUnchanging = settings.BooleanSetting().getFromValue('Add Feed Rate Even When Unchanging', self, True) + self.addSpaceBetweenWords = settings.BooleanSetting().getFromValue('Add Space Between Words', self, True) + self.addZEvenWhenUnchanging = settings.BooleanSetting().getFromValue('Add Z Even When Unchanging', self, True) + self.fileNameInput = settings.FileNameInput().getFromFileName([('Gcode text files', '*.gcode')], 'Open File to be Converted to Gcode Step', self, '') + self.feedRateStepLength = settings.FloatSpin().getFromValue(0.0, 'Feed Rate Step Length (millimeters/second)', self, 1.0, 0.1) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Offset -', self ) + self.xOffset = settings.FloatSpin().getFromValue(-100.0, 'X Offset (millimeters)', self, 100.0, 0.0) + self.yOffset = settings.FloatSpin().getFromValue(-100.0, 'Y Offset (millimeters)', self, 100.0, 0.0) + self.zOffset = settings.FloatSpin().getFromValue(-10.0, 'Z Offset (millimeters)', self, 10.0, 0.0) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Step Length -', self ) + self.eStepLength = settings.FloatSpin().getFromValue(0.0, 'E Step Length (float)', self, 1.0, 0.1) + self.radiusStepLength = settings.FloatSpin().getFromValue(0.0, 'Radius Step Length (millimeters)', self, 1.0, 0.1) + self.xStepLength = settings.FloatSpin().getFromValue(0.0, 'X Step Length (millimeters)', self, 1.0, 0.1) + self.yStepLength = settings.FloatSpin().getFromValue(0.0, 'Y Step Length (millimeters)', self, 1.0, 0.1) + self.zStepLength = settings.FloatSpin().getFromValue(0.0, 'Z Step Length (millimeters)', self, 0.2, 0.01) + self.executeTitle = 'Convert to Gcode Step' + + def execute(self): + 'Convert to gcode step button has been clicked.' + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, ['.gcode'], self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class GcodeStepSkein: + 'A class to convert gcode into 16 byte binary segments.' + def __init__(self): + self.oldFeedRateString = None + self.oldZString = None + self.output = cStringIO.StringIO() + + def addCharacterInteger(self, character, lineStringIO, offset, splitLine, stepLength): + 'Add a character and integer to line string.' + characterIntegerString = getCharacterIntegerString(character, offset, splitLine, stepLength) + self.addStringToLine(lineStringIO, characterIntegerString) + + def addLine(self, line): + 'Add a line of text and a newline to the output.' + self.output.write(line + '\n') + + def addStringToLine(self, lineStringIO, wordString): + 'Add a character and integer to line string.' + if wordString == '': + return + if self.repository.addSpaceBetweenWords.value: + lineStringIO.write(' ') + lineStringIO.write(wordString) + + def getCraftedGcode(self, repository, gcodeText): + 'Parse gcode text and store the gcode.' + self.repository = repository + lines = archive.getTextLines(gcodeText) + for line in lines: + self.parseLine(line) + return self.output.getvalue() + + def parseLine(self, line): + 'Parse a gcode line.' + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if len(firstWord) < 1: + return + firstLetter = firstWord[0] + if firstLetter == '(': + return + if firstWord != 'G1' and firstWord != 'G2' and firstWord != 'G3': + self.addLine(line) + return + lineStringIO = cStringIO.StringIO() + lineStringIO.write(firstWord) + self.addCharacterInteger('I', lineStringIO, 0.0, splitLine, self.repository.xStepLength.value) + self.addCharacterInteger('J', lineStringIO, 0.0, splitLine, self.repository.yStepLength.value) + self.addCharacterInteger('R', lineStringIO, 0.0, splitLine, self.repository.radiusStepLength.value) + self.addCharacterInteger('X', lineStringIO, self.repository.xOffset.value, splitLine, self.repository.xStepLength.value) + self.addCharacterInteger('Y', lineStringIO, self.repository.yOffset.value, splitLine, self.repository.yStepLength.value) + zString = getCharacterIntegerString('Z', self.repository.zOffset.value, splitLine, self.repository.zStepLength.value) + feedRateString = getCharacterIntegerString('F', 0.0, splitLine, self.repository.feedRateStepLength.value) + if zString != '': + if zString != self.oldZString or self.repository.addZEvenWhenUnchanging.value: + self.addStringToLine(lineStringIO, zString) + if feedRateString != '': + if feedRateString != self.oldFeedRateString or self.repository.addFeedRateEvenWhenUnchanging.value: + self.addStringToLine(lineStringIO, feedRateString) + self.addCharacterInteger('E', lineStringIO, 0.0, splitLine, self.repository.eStepLength.value) + self.addLine(lineStringIO.getvalue()) + self.oldFeedRateString = feedRateString + self.oldZString = zString + + +def main(): + 'Display the export dialog.' + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/gcode_time_segment.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/gcode_time_segment.py new file mode 100644 index 0000000..4a7e756 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/gcode_time_segment.py @@ -0,0 +1,245 @@ +""" +This page is in the table of contents. +Gcode time segment is an export plugin to convert gcode from float position to number of steps. + +An export plugin is a script in the export_plugins folder which has the getOutput function, the globalIsReplaceable variable and if it's output is not replaceable, the writeOutput function. It is meant to be run from the export tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name. + +The getOutput function of this script takes a gcode text and returns it with the positions converted into number of steps and time. The writeOutput function of this script takes a gcode text and writes that with the positions converted into number of steps and time. + +==Settings== +===Add Space Between Words=== +Default is on. + +When selected, a space will be added between each gcode word. + +===Offset=== +====X Offset==== +Default is zero. + +Defines the X Offset. + +====Y Offset==== +Default is zero. + +Defines the Y Offset. + +====Z Offset==== +Default is zero. + +Defines the Z Offset. + +===Step=== +===Extrusion Step=== +Default is 0.01 mm. + +Defines the radius step length. + +===Time Step=== +Default is 1 microsecond(mcs). + +Defines the time step duration. + +====X Step==== +Default is 0.1 mm. + +Defines the X axis step length. + +====Y Step==== +Default is 0.1 mm. + +Defines the Y axis step length. + +====Z Step==== +Default is 0.01 mm. + +Defines the Z axis step length. + +""" + + +from __future__ import absolute_import +import __init__ +from fabmetheus_utilities import archive +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +from struct import Struct +import cStringIO +import os +import sys + + +__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' + + +# This is true if the output is text and false if it is binary." +globalIsReplaceable = True + + +def getCharacterIntegerString( character, offset, splitLine, step ): + "Get a character and integer string." + floatValue = getFloatFromCharacterSplitLine(character, splitLine) + if floatValue == None: + return None + floatValue += offset + integerValue = int(round(float(floatValue / step))) + return character + str( integerValue ) + +def getFloatFromCharacterSplitLine(character, splitLine): + "Get the float after the first occurence of the character in the split line." + lineFromCharacter = gcodec.getStringFromCharacterSplitLine(character, splitLine) + if lineFromCharacter == None: + return None + return float(lineFromCharacter) + +def getNewRepository(): + 'Get new repository.' + return GcodeTimeSegmentRepository() + +def getOutput(gcodeText, repository=None): + 'Get the exported version of a gcode file.' + if gcodeText == '': + return '' + if repository == None: + repository = GcodeTimeSegmentRepository() + settings.getReadRepository(repository) + return GcodeTimeSegmentSkein().getCraftedGcode(gcodeText, repository) + +def writeOutput( fileName, gcodeText = ''): + "Write the exported version of a gcode file." + gcodeText = gcodec.getGcodeFileText(fileName, gcodeText) + repository = GcodeTimeSegmentRepository() + settings.getReadRepository(repository) + output = getOutput(gcodeText, repository) + suffixFileName = fileName[ : fileName.rfind('.') ] + '_gcode_time_segment.gcode' + archive.writeFileText( suffixFileName, output ) + print('The converted file is saved as ' + archive.getSummarizedFileName(suffixFileName) ) + + +class GcodeTimeSegmentRepository: + "A class to handle the export settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.export_plugins.gcode_time.html', self) + self.addSpaceBetweenWords = settings.BooleanSetting().getFromValue('Add Space Between Words', self, True ) + self.fileNameInput = settings.FileNameInput().getFromFileName( [ ('Gcode text files', '*.gcode') ], 'Open File to be Converted to Gcode Time', self, '') + self.initialTime = settings.FloatSpin().getFromValue(0.0, 'Initial Time (s)', self, 20.0, 10.0) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Offset -', self ) + self.xOffset = settings.FloatSpin().getFromValue( - 100.0, 'X Offset (mm)', self, 100.0, 0.0 ) + self.yOffset = settings.FloatSpin().getFromValue( -100.0, 'Y Offset (mm)', self, 100.0, 0.0 ) + self.zOffset = settings.FloatSpin().getFromValue( - 10.0, 'Z Offset (mm)', self, 10.0, 0.0 ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Step -', self ) + self.extrusionStep = settings.FloatSpin().getFromValue(0.0, 'Extrusion Step (mm)', self, 0.2, 0.01) + self.timeStep = settings.FloatSpin().getFromValue(0.0, 'Time Step (mcs)', self, 2000.0, 1000.0) + self.xStep = settings.FloatSpin().getFromValue(0.0, 'X Step (mm)', self, 1.0, 0.1) + self.yStep = settings.FloatSpin().getFromValue(0.0, 'Y Step (mm)', self, 1.0, 0.1) + self.zStep = settings.FloatSpin().getFromValue(0.0, 'Z Step (mm)', self, 0.2, 0.01) + settings.LabelSeparator().getFromRepository(self) + self.executeTitle = 'Convert to Gcode Time' + + def execute(self): + "Convert to gcode step button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode( self.fileNameInput.value, ['.gcode'], self.fileNameInput.wasCancelled ) + for fileName in fileNames: + writeOutput(fileName) + + +class GcodeTimeSegmentSkein: + "A class to convert gcode into time segments." + def __init__(self): + 'Initialize.' + self.feedRateMinute = None + self.isExtruderActive = False + self.oldFeedRateString = None + self.oldLocation = None + self.oldZString = None + self.operatingFlowRate = None + self.output = cStringIO.StringIO() + + def addCharacterInteger(self, character, lineStringIO, offset, splitLine, step): + "Add a character and integer to line string." + characterIntegerString = getCharacterIntegerString(character, offset, splitLine, step) + self.addStringToLine(lineStringIO, characterIntegerString) + + def addLine(self, line): + "Add a line of text and a newline to the output." + self.output.write(line + '\n') + + def addStringToLine( self, lineStringIO, wordString ): + "Add a character and integer to line string." + if wordString == None: + return + if self.repository.addSpaceBetweenWords.value: + lineStringIO.write(' ') + lineStringIO.write( wordString ) + + def getCraftedGcode(self, gcodeText, repository): + "Parse gcode text and store the gcode." + self.repository = repository + lines = archive.getTextLines(gcodeText) + for line in lines: + self.parseLine(line) + return self.output.getvalue() + + def parseLine(self, line): + "Parse a gcode line." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if len(firstWord) < 1: + return + firstLetter = firstWord[0] + if firstWord == '(': + self.feedRateMinute = 60.0 * float(splitLine[1]) + elif firstWord == '(': + self.operatingFlowRate = float(splitLine[1]) + self.flowRate = self.operatingFlowRate + if firstLetter == '(': + return + if firstWord == 'M101': + self.isExtruderActive = True + elif firstWord == 'M103': + self.isExtruderActive = False + elif firstWord == 'M108': + self.flowRate = float(splitLine[1][1 :]) + if firstWord != 'G1' and firstWord != 'G2' and firstWord != 'G3': + self.addLine(line) + return + self.feedRateMinute = gcodec.getFeedRateMinute(self.feedRateMinute, splitLine) + lineStringIO = cStringIO.StringIO() + lineStringIO.write(firstWord) + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + self.addCharacterInteger('X', lineStringIO, self.repository.xOffset.value, splitLine, self.repository.xStep.value ) + self.addCharacterInteger('Y', lineStringIO, self.repository.yOffset.value, splitLine, self.repository.yStep.value ) + zString = getCharacterIntegerString('Z', self.repository.zOffset.value, splitLine, self.repository.zStep.value ) + if zString == None: + zString = self.oldZString + self.addStringToLine(lineStringIO, zString) + duration = self.repository.initialTime.value + if self.oldLocation != None: + distance = abs(location - self.oldLocation) + duration = 60.0 / self.feedRateMinute * distance + extrusionDistance = 0.0 + if self.isExtruderActive: + extrusionDistance = self.flowRate * duration + self.addStringToLine(lineStringIO, 'E%s' % int(round(extrusionDistance / self.repository.extrusionStep.value))) + self.addStringToLine(lineStringIO, 'D%s' % int(round(duration * 1000000.0 / self.repository.timeStep.value))) + self.addLine(lineStringIO.getvalue()) + self.oldLocation = location + self.oldZString = zString + + +def main(): + "Display the export dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/static_plugins/__init__.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/static_plugins/__init__.py new file mode 100644 index 0000000..b83e941 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/static_plugins/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 5 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/static_plugins/gcode_small.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/static_plugins/gcode_small.py new file mode 100644 index 0000000..3443777 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/static_plugins/gcode_small.py @@ -0,0 +1,110 @@ +""" +This page is in the table of contents. +Gcode_small is an export plugin to remove the comments and the redundant z and feed rate parameters from a gcode file. + +An export plugin is a script in the export_plugins folder which has the getOutput function, the globalIsReplaceable variable and if it's output is not replaceable, the writeOutput function. It is meant to be run from the export tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name. + +The getOutput function of this script takes a gcode text and returns that text without comments and redundant z and feed rate parameters. The writeOutput function of this script takes a gcode text and writes that text without comments and redundant z and feed rate parameters to a file. + +Many of the functions in this script are copied from gcodec in skeinforge_utilities. They are copied rather than imported so developers making new plugins do not have to learn about gcodec, the code here is all they need to learn. + +""" + +from __future__ import absolute_import +import cStringIO +import os + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +# This is true if the output is text and false if it is binary." +globalIsReplaceable = True + + +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 getOutput(gcodeText): + 'Get the exported version of a gcode file.' + return GcodeSmallSkein().getCraftedGcode(gcodeText) + +def getSplitLineBeforeBracketSemicolon(line): + "Get the split line before a bracket or semicolon." + bracketSemicolonIndex = min( line.find(';'), line.find('(') ) + if bracketSemicolonIndex < 0: + return line.split() + return line[ : bracketSemicolonIndex ].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 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 getTextLines(text): + "Get the all the lines of text of a text." + return text.replace('\r', '\n').split('\n') + + +class GcodeSmallSkein: + "A class to remove redundant z and feed rate parameters from a skein of extrusions." + def __init__(self): + self.lastFeedRateString = None + self.lastZString = None + self.output = cStringIO.StringIO() + + def getCraftedGcode( self, gcodeText ): + "Parse gcode text and store the gcode." + lines = getTextLines(gcodeText) + for line in lines: + self.parseLine(line) + return self.output.getvalue() + + def parseLine(self, line): + "Parse a gcode line." + splitLine = getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if len(firstWord) < 1: + return + if firstWord[0] == '(': + return + if firstWord != 'G1': + self.output.write(line + '\n') + return + eString = getStringFromCharacterSplitLine('E', splitLine ) + xString = getStringFromCharacterSplitLine('X', splitLine ) + yString = getStringFromCharacterSplitLine('Y', splitLine ) + zString = getStringFromCharacterSplitLine('Z', splitLine ) + feedRateString = getStringFromCharacterSplitLine('F', splitLine ) + self.output.write('G1') + if xString != None: + self.output.write(' X' + xString ) + if yString != None: + self.output.write(' Y' + yString ) + if zString != None and zString != self.lastZString: + self.output.write(' Z' + zString ) + if feedRateString != None and feedRateString != self.lastFeedRateString: + self.output.write(' F' + feedRateString ) + if eString != None: + self.output.write(' E' + eString ) + self.lastFeedRateString = feedRateString + self.lastZString = zString + self.output.write('\n') diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/feed.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/feed.py new file mode 100644 index 0000000..042da50 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/feed.py @@ -0,0 +1,183 @@ +""" +This page is in the table of contents. +The feed script sets the maximum feed rate, operating feed rate & travel feed rate. + +==Operation== +The default 'Activate Feed' checkbox is on. When it is on, the functions described below will work, when it is off, the functions will not be called. + +==Settings== +===Feed Rate=== +Default is 16 millimeters/second. + +Defines the feed rate for the shape. + +===Maximum Z Drill Feed Rate=== +Default is 0.1 millimeters/second. + +If your firmware limits the z feed rate, you do not need to set this setting. + +Defines the maximum feed that the tool head will move in the z direction while the tool is on. + +===Maximum Z Feed Rate=== +Default is one millimeter per second. + +Defines the maximum speed that the tool head will move in the z direction. + +===Travel Feed Rate=== +Default is 16 millimeters/second. + +Defines the feed rate when the cutter is off. The travel feed rate could be set as high as the cutter can be moved, it does not have to be limited by the maximum cutter rate. + +==Examples== +The following examples feed the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and feed.py. + +> python feed.py +This brings up the feed dialog. + +> python feed.py Screw Holder Bottom.stl +The feed tool is parsing the file: +Screw Holder Bottom.stl +.. +The feed tool has created the file: +.. Screw Holder Bottom_feed.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import sys + + +__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 getCraftedText(fileName, gcodeText='', repository=None): + "Feed the file or text." + return getCraftedTextFromText( archive.getTextIfEmpty( fileName, gcodeText ), repository ) + +def getCraftedTextFromText(gcodeText, repository=None): + "Feed a gcode linear move text." + if gcodec.isProcedureDoneOrFileIsEmpty(gcodeText, 'feed'): + return gcodeText + if repository == None: + repository = settings.getReadRepository(FeedRepository()) + if not repository.activateFeed.value: + return gcodeText + return FeedSkein().getCraftedGcode(gcodeText, repository) + +def getNewRepository(): + 'Get new repository.' + return FeedRepository() + +def writeOutput(fileName, shouldAnalyze=True): + "Feed a gcode linear move file." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'feed', shouldAnalyze) + + +class FeedRepository: + "A class to handle the feed settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.feed.html', self) + self.fileNameInput = settings.FileNameInput().getFromFileName(fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Feed', self, '') + self.activateFeed = settings.BooleanSetting().getFromValue('Activate Feed', self, True) + self.feedRatePerSecond = settings.FloatSpin().getFromValue(2.0, 'Feed Rate (mm/s):', self, 50.0, 16.0) + self.maximumZDrillFeedRatePerSecond = settings.FloatSpin().getFromValue(0.02, 'Maximum Z Drill Feed Rate (mm/s):', self, 0.5, 0.1) + self.maximumZFeedRatePerSecond = settings.FloatSpin().getFromValue(0.5, 'Maximum Z Feed Rate (mm/s):', self, 10.0, 1.0) + self.travelFeedRatePerSecond = settings.FloatSpin().getFromValue(2.0, 'Travel Feed Rate (mm/s):', self, 50.0, 16.0) + self.executeTitle = 'Feed' + + def execute(self): + "Feed button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class FeedSkein: + "A class to feed a skein of cuttings." + def __init__(self): + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.feedRatePerSecond = 16.0 + self.isExtruderActive = False + self.lineIndex = 0 + self.lines = None + self.oldFlowrateString = None + self.oldLocation = None + + def getCraftedGcode(self, gcodeText, repository): + "Parse gcode text and store the feed gcode." + self.repository = repository + self.feedRatePerSecond = repository.feedRatePerSecond.value + self.travelFeedRateMinute = 60.0 * self.repository.travelFeedRatePerSecond.value + self.lines = archive.getTextLines(gcodeText) + self.parseInitialization() + for line in self.lines[self.lineIndex :]: + self.parseLine(line) + return self.distanceFeedRate.output.getvalue() + + def getFeededLine(self, line, splitLine): + "Get gcode line with feed rate." + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + self.oldLocation = location + feedRateMinute = 60.0 * self.feedRatePerSecond + if not self.isExtruderActive: + feedRateMinute = self.travelFeedRateMinute + return self.distanceFeedRate.getLineWithFeedRate(feedRateMinute, line, splitLine) + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('feed') + return + elif firstWord == '(': + self.absoluteEdgeWidth = abs(float(splitLine[1])) + self.distanceFeedRate.addTagBracketedLine('maximumZDrillFeedRatePerSecond', self.repository.maximumZDrillFeedRatePerSecond.value) + self.distanceFeedRate.addTagBracketedLine('maximumZFeedRatePerSecond', self.repository.maximumZFeedRatePerSecond.value ) + self.distanceFeedRate.addTagBracketedLine('operatingFeedRatePerSecond', self.feedRatePerSecond) + self.distanceFeedRate.addTagBracketedLine('travelFeedRatePerSecond', self.repository.travelFeedRatePerSecond.value) + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + "Parse a gcode line and add it to the feed skein." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + line = self.getFeededLine(line, splitLine) + elif firstWord == 'M101': + self.isExtruderActive = True + elif firstWord == 'M103': + self.isExtruderActive = False + self.distanceFeedRate.addLine(line) + + +def main(): + 'Display the feed dialog.' + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/fill.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/fill.py new file mode 100644 index 0000000..75ea0f2 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/fill.py @@ -0,0 +1,1371 @@ +#! /usr/bin/env python +""" +This page is in the table of contents. +Fill is a script to fill the edges of a gcode file. + +The fill manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Fill + +Allan Ecker aka The Masked Retriever has written the "Skeinforge Quicktip: Fill" at: +http://blog.thingiverse.com/2009/07/21/mysteries-of-skeinforge-fill/ + +==Operation== +The default 'Activate Fill' checkbox is off. When it is on, the functions described below will work, when it is off, the functions will not be called. + +==Settings== +===Diaphragm=== +The diaphragm is a solid group of layers, at regular intervals. It can be used with a sparse infill to give the object watertight, horizontal compartments and/or a higher shear strength. + +====Diaphragm Period==== +Default is one hundred. + +Defines the number of layers between diaphrams. + +====Diaphragm Thickness==== +Default is zero, because the diaphragm feature is rarely used. + +Defines the number of layers the diaphram is composed of. + +===Extra Shells=== +The shells interior edge loops. Adding extra shells makes the object stronger & heavier. + +====Extra Shells on Alternating Solid Layers==== +Default is two. + +Defines the number of extra shells, on the alternating solid layers. + +====Extra Shells on Base==== +Default is one. + +Defines the number of extra shells on the bottom, base layer and every even solid layer after that. Setting this to a different value than the "Extra Shells on Alternating Solid Layers" means the infill pattern will alternate, creating a strong interleaved bond even if the edge loop shrinks. + +====Extra Shells on Sparse Layer==== +Default is one. + +Defines the number of extra shells on the sparse layers. The solid layers are those at the top & bottom, and wherever the object has a plateau or overhang, the sparse layers are the layers in between. + +===Grid=== +====Grid Circle Separation over Perimeter Width==== +Default is 0.2. + +Defines the ratio of the amount the grid circle is inset over the edge width, the default is zero. With a value of zero the circles will touch, with a value of one two threads could be fitted between the circles. + +====Grid Extra Overlap==== +Default is 0.1. + +Defines the amount of extra overlap added when extruding the grid to compensate for the fact that when the first thread going through a grid point is extruded, since there is nothing there yet for it to connect to it will shrink extra. + +====Grid Junction Separation over Octogon Radius At End==== +Default is zero. + +Defines the ratio of the amount the grid square is increased in each direction over the extrusion width at the end. With a value of one or so the grid pattern will have large squares to go with the octogons. + +====Grid Junction Separation over Octogon Radius At Middle==== +Default is zero. + +Defines the increase at the middle. If this value is different than the value at the end, the grid would have an accordion pattern, which would give it a higher shear strength. + +====Grid Junction Separation Band Height==== +Default is ten. + +Defines the height of the bands of the accordion pattern. + +===Infill=== +====Infill Pattern==== +Default is 'Line', since it is quicker to generate and does not add extra movements for the extruder. The grid pattern has extra diagonal lines, so when choosing a grid option, set the infill solidity to 0.2 or less so that there is not too much plastic and the grid generation time, which increases with the third power of solidity, will be reasonable. + +=====Grid Circular===== +When selected, the infill will be a grid of separated circles. Because the circles are separated, the pattern is weak, it only provides support for the top layer threads and some strength in the z direction. The flip side is that this infill does not warp the object, the object will get warped only by the walls. + +Because this pattern turns the extruder on and off often, it is best to use a stepper motor extruder. + +=====Grid Hexagonal===== +When selected, the infill will be a hexagonal grid. Because the grid is made with threads rather than with molding or milling, only a partial hexagon is possible, so the rectangular grid pattern is stronger. + +=====Grid Rectangular===== +When selected, the infill will be a funky octogon square honeycomb like pattern which gives the object extra strength. + +=====Line===== +When selected, the infill will be made up of lines. + +====Infill Begin Rotation==== +Default is forty five degrees, giving a diagonal infill. + +Defines the amount the infill direction of the base and every second layer thereafter is rotated. + +====Infill Odd Layer Extra Rotation==== +Default is ninety degrees, making the odd layer infill perpendicular to the base layer. + +Defines the extra amount the infill direction of the odd layers is rotated compared to the base layer. + +====Infill Begin Rotation Repeat==== +Default is one, giving alternating cross hatching. + +Defines the number of layers that the infill begin rotation will repeat. With a value higher than one, the infill will go in one direction more often, giving the object more strength in one direction and less in the other, this is useful for beams and cantilevers. + +====Infill Perimeter Overlap==== +Default is 0.15. + +Defines the amount the infill overlaps the edge over the average of the edge and infill width. The higher the value the more the infill will overlap the edge, and the thicker join between the infill and the edge. If the value is too high, the join will be so thick that the nozzle will run plow through the join below making a mess, also when it is above 0.45 fill may not be able to create infill correctly. If you want to stretch the infill a lot, set 'Path Stretch over Perimeter Width' in stretch to a high value. + +====Infill Solidity==== +Default is 0.2. + +Defines the solidity of the infill, this is the most important setting in fill. A value of one means the infill lines will be right beside each other, resulting in a solid, strong, heavy shape which takes a long time to extrude. A low value means the infill will be sparse, the interior will be mosty empty space, the object will be weak, light and quick to build. + +====Infill Width over Thickness==== +Default is 1.5. + +Defines the ratio of the infill width over the layer height. The higher the value the wider apart the infill will be and therefore the sparser the infill will be. + +===Solid Surface Thickness=== +Default is three. + +Defines the number of solid layers that are at the bottom, top, plateaus and overhang. With a value of zero, the entire object will be composed of a sparse infill, and water could flow right through it. With a value of one, water will leak slowly through the surface and with a value of three, the object could be watertight. The higher the solid surface thickness, the stronger and heavier the object will be. + +===Start From Choice=== +Default is 'Lower Left'. + +Defines where each layer starts from. + +====Lower Left==== +When selected the layer will start from the lower left corner. This is to extrude in round robin fashion so that the first extrusion will be deposited on the coolest part of the last layer. The reason for this is described at: +http://hydraraptor.blogspot.com/2010/12/round-robin.html + +====Nearest==== +When selected the layer will start from the closest point to the end of the last layer. This leads to less stringing, but the first extrusion will be deposited on the hottest part of the last layer which leads to melting problems. So this option is deprecated, eventually this option will be removed and the layers will always start from the lower left. + +===Surrounding Angle=== +Default: 60 degrees + +Defines the angle that the surrounding layers around the infill are expanded. + +To decide whether or not the infill should be sparse or solid, fill looks at the 'Solid Surface Thickness' surrounding layers above and below the infill. If any of the expanded layers above or below the infill do not cover the infill, then the infill will be solid in that region. The layers are expanded by the height difference times the tangent of the surrounding angle, which is from the vertical. For example, if the model is a wedge with a wall angle less than the surrounding angle, the interior layers (those which are not on the bottom or top) will be sparse. If the wall angle is greater than the surrounding angle, the interior layers will be solid. + +The time required to examine the surrounding layers increases with the surrounding angle, so the surrounding angle is limited to eighty degrees, regardless of the input value. + +If you have an organic shape with gently sloping surfaces; if the surrounding angle is set too high, then too many layers will be sparse. If the surrounding angle is too low, then too many layers will be solid and the extruder may end up plowing through previous layers: +http://hydraraptor.blogspot.com/2008/08/bearing-fruit.html + +===Thread Sequence Choice=== +The 'Thread Sequence Choice' is the sequence in which the threads will be extruded on the second and higher layers. There are three kinds of thread, the edge threads on the outside of the object, the loop threads aka inner shell threads, and the interior infill threads. The first layer thread sequence is 'Perimeter > Loops > Infill'. + +The default choice is 'Perimeter > Loops > Infill', which the default stretch parameters are based on. If you change from the default sequence choice setting of edge, then loops, then infill, the optimal stretch thread parameters would also be different. In general, if the infill is extruded first, the infill would have to be stretched more so that even after the filament shrinkage, it would still be long enough to connect to the loop or edge. The six sequence combinations follow below. + +====Infill > Loops > Perimeter==== +====Infill > Perimeter > Loops==== +====Loops > Infill > Perimeter==== +====Loops > Perimeter > Infill==== +====Perimeter > Infill > Loops==== +====Perimeter > Loops > Infill==== + +==Examples== +The following examples fill the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and fill.py. + +> python fill.py +This brings up the fill dialog. + +> python fill.py Screw Holder Bottom.stl +The fill tool is parsing the file: +Screw Holder Bottom.stl +.. +The fill tool has created the file: +.. Screw Holder Bottom_fill.gcode + +""" + +from __future__ import absolute_import +try: + import psyco + psyco.full() +except: + pass +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import sys + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/28/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + + +def addAroundGridPoint( arounds, gridPoint, gridPointInsetX, gridPointInsetY, gridPoints, gridSearchRadius, isBothOrNone, isDoubleJunction, isJunctionWide, paths, pixelTable, width ): + 'Add the path around the grid point.' + closestPathIndex = None + aroundIntersectionPaths = [] + for aroundIndex in xrange( len(arounds) ): + loop = arounds[ aroundIndex ] + for pointIndex in xrange(len(loop)): + pointFirst = loop[pointIndex] + pointSecond = loop[(pointIndex + 1) % len(loop)] + yIntersection = euclidean.getYIntersectionIfExists( pointFirst, pointSecond, gridPoint.real ) + addYIntersectionPathToList( aroundIndex, pointIndex, gridPoint.imag, yIntersection, aroundIntersectionPaths ) + if len( aroundIntersectionPaths ) < 2: + print('Warning, aroundIntersectionPaths is less than 2 in fill.') + print(aroundIntersectionPaths) + print(gridPoint) + return + yCloseToCenterArounds = getClosestOppositeIntersectionPaths(aroundIntersectionPaths) + if len(yCloseToCenterArounds) < 2: + return + segmentFirstY = min( yCloseToCenterArounds[0].y, yCloseToCenterArounds[1].y ) + segmentSecondY = max( yCloseToCenterArounds[0].y, yCloseToCenterArounds[1].y ) + yIntersectionPaths = [] + gridPixel = euclidean.getStepKeyFromPoint( gridPoint / width ) + segmentFirstPixel = euclidean.getStepKeyFromPoint( complex( gridPoint.real, segmentFirstY ) / width ) + segmentSecondPixel = euclidean.getStepKeyFromPoint( complex( gridPoint.real, segmentSecondY ) / width ) + pathIndexTable = {} + addPathIndexFirstSegment( gridPixel, pathIndexTable, pixelTable, segmentFirstPixel ) + addPathIndexSecondSegment( gridPixel, pathIndexTable, pixelTable, segmentSecondPixel ) + for pathIndex in pathIndexTable.keys(): + path = paths[ pathIndex ] + for pointIndex in xrange( len(path) - 1 ): + pointFirst = path[pointIndex] + pointSecond = path[pointIndex + 1] + yIntersection = getYIntersectionInsideYSegment( segmentFirstY, segmentSecondY, pointFirst, pointSecond, gridPoint.real ) + addYIntersectionPathToList( pathIndex, pointIndex, gridPoint.imag, yIntersection, yIntersectionPaths ) + if len( yIntersectionPaths ) < 1: + return + yCloseToCenterPaths = [] + if isDoubleJunction: + yCloseToCenterPaths = getClosestOppositeIntersectionPaths( yIntersectionPaths ) + else: + yIntersectionPaths.sort( compareDistanceFromCenter ) + yCloseToCenterPaths = [ yIntersectionPaths[0] ] + for yCloseToCenterPath in yCloseToCenterPaths: + setIsOutside( yCloseToCenterPath, aroundIntersectionPaths ) + if len( yCloseToCenterPaths ) < 2: + yCloseToCenterPaths[0].gridPoint = gridPoint + insertGridPointPair( gridPoint, gridPointInsetX, gridPoints, isJunctionWide, paths, pixelTable, yCloseToCenterPaths[0], width ) + return + plusMinusSign = getPlusMinusSign( yCloseToCenterPaths[1].y - yCloseToCenterPaths[0].y ) + yCloseToCenterPaths[0].gridPoint = complex( gridPoint.real, gridPoint.imag - plusMinusSign * gridPointInsetY ) + yCloseToCenterPaths[1].gridPoint = complex( gridPoint.real, gridPoint.imag + plusMinusSign * gridPointInsetY ) + yCloseToCenterPaths.sort( comparePointIndexDescending ) + insertGridPointPairs( gridPoint, gridPointInsetX, gridPoints, yCloseToCenterPaths[0], yCloseToCenterPaths[1], isBothOrNone, isJunctionWide, paths, pixelTable, width ) + +def addInfillBoundary(infillBoundary, nestedRings): + 'Add infill boundary to the nested ring that contains it.' + infillPoint = infillBoundary[0] + for nestedRing in nestedRings: + if euclidean.isPointInsideLoop(nestedRing.boundary, infillPoint): + nestedRing.infillBoundaries.append(infillBoundary) + return + +def addLoop(infillWidth, infillPaths, loop, rotationPlaneAngle): + 'Add simplified path to fill.' + simplifiedLoop = euclidean.getSimplifiedLoop(loop, infillWidth) + if len(simplifiedLoop) < 2: + return + simplifiedLoop.append(simplifiedLoop[0]) + planeRotated = euclidean.getRotatedComplexes(rotationPlaneAngle, simplifiedLoop) + infillPaths.append(planeRotated) + +def addPath(infillWidth, infillPaths, path, rotationPlaneAngle): + 'Add simplified path to fill.' + simplifiedPath = euclidean.getSimplifiedPath(path, infillWidth) + if len(simplifiedPath) < 2: + return + planeRotated = euclidean.getRotatedComplexes(rotationPlaneAngle, simplifiedPath) + infillPaths.append(planeRotated) + +def addPathIndexFirstSegment( gridPixel, pathIndexTable, pixelTable, segmentFirstPixel ): + 'Add the path index of the closest segment found toward the second segment.' + for yStep in xrange( gridPixel[1], segmentFirstPixel[1] - 1, - 1 ): + if getKeyIsInPixelTableAddValue( ( gridPixel[0], yStep ), pathIndexTable, pixelTable ): + return + +def addPathIndexSecondSegment( gridPixel, pathIndexTable, pixelTable, segmentSecondPixel ): + 'Add the path index of the closest segment found toward the second segment.' + for yStep in xrange( gridPixel[1], segmentSecondPixel[1] + 1 ): + if getKeyIsInPixelTableAddValue( ( gridPixel[0], yStep ), pathIndexTable, pixelTable ): + return + +def addPointOnPath( path, pathIndex, pixelTable, point, pointIndex, width ): + 'Add a point to a path and the pixel table.' + pointIndexMinusOne = pointIndex - 1 + if pointIndex < len(path) and pointIndexMinusOne >= 0: + segmentTable = {} + begin = path[ pointIndexMinusOne ] + end = path[pointIndex] + euclidean.addValueSegmentToPixelTable( begin, end, segmentTable, pathIndex, width ) + euclidean.removePixelTableFromPixelTable( segmentTable, pixelTable ) + if pointIndexMinusOne >= 0: + begin = path[ pointIndexMinusOne ] + euclidean.addValueSegmentToPixelTable( begin, point, pixelTable, pathIndex, width ) + if pointIndex < len(path): + end = path[pointIndex] + euclidean.addValueSegmentToPixelTable( point, end, pixelTable, pathIndex, width ) + path.insert( pointIndex, point ) + +def addPointOnPathIfFree( path, pathIndex, pixelTable, point, pointIndex, width ): + 'Add the closest point to a path, if the point added to a path is free.' + if isAddedPointOnPathFree( path, pixelTable, point, pointIndex, width ): + addPointOnPath( path, pathIndex, pixelTable, point, pointIndex, width ) + +def addSparseEndpoints(doubleInfillWidth, endpoints, horizontalSegmentsDictionary, horizontalSegmentsDictionaryKey, infillSolidity, removedEndpoints, solidSurfaceThickness, surroundingXIntersections): + 'Add sparse endpoints.' + segments = horizontalSegmentsDictionary[horizontalSegmentsDictionaryKey] + for segment in segments: + addSparseEndpointsFromSegment(doubleInfillWidth, endpoints, horizontalSegmentsDictionary, horizontalSegmentsDictionaryKey, infillSolidity, removedEndpoints, segment, solidSurfaceThickness, surroundingXIntersections) + +def addSparseEndpointsFromSegment(doubleInfillWidth, endpoints, horizontalSegmentsDictionary, horizontalSegmentsDictionaryKey, infillSolidity, removedEndpoints, segment, solidSurfaceThickness, surroundingXIntersections): + 'Add sparse endpoints from a segment.' + if infillSolidity > 0.0: + if int(round(round(float(horizontalSegmentsDictionaryKey) * infillSolidity) / infillSolidity)) == horizontalSegmentsDictionaryKey: + endpoints += segment + return + if abs(segment[0].point - segment[1].point) < doubleInfillWidth: + endpoints += segment + return + if not isSegmentAround(horizontalSegmentsDictionary, horizontalSegmentsDictionaryKey - 1, segment): + endpoints += segment + return + if not isSegmentAround(horizontalSegmentsDictionary, horizontalSegmentsDictionaryKey + 1, segment): + endpoints += segment + return + if solidSurfaceThickness == 0: + removedEndpoints += segment + return + if isSegmentCompletelyInAnIntersection(segment, surroundingXIntersections): + removedEndpoints += segment + return + endpoints += segment + +def addYIntersectionPathToList( pathIndex, pointIndex, y, yIntersection, yIntersectionPaths ): + 'Add the y intersection path to the y intersection paths.' + if yIntersection == None: + return + yIntersectionPath = YIntersectionPath( pathIndex, pointIndex, yIntersection ) + yIntersectionPath.yMinusCenter = yIntersection - y + yIntersectionPaths.append( yIntersectionPath ) + +def compareDistanceFromCenter(self, other): + 'Get comparison in order to sort y intersections in ascending order of distance from the center.' + distanceFromCenter = abs( self.yMinusCenter ) + distanceFromCenterOther = abs( other.yMinusCenter ) + if distanceFromCenter > distanceFromCenterOther: + return 1 + if distanceFromCenter < distanceFromCenterOther: + return - 1 + return 0 + +def comparePointIndexDescending(self, other): + 'Get comparison in order to sort y intersections in descending order of point index.' + if self.pointIndex > other.pointIndex: + return - 1 + if self.pointIndex < other.pointIndex: + return 1 + return 0 + +def createExtraFillLoops(nestedRing, radius, radiusAround, shouldExtraLoopsBeAdded): + 'Create extra fill loops.' + for innerNestedRing in nestedRing.innerNestedRings: + createFillForSurroundings(innerNestedRing.innerNestedRings, radius, radiusAround, shouldExtraLoopsBeAdded) + allFillLoops = intercircle.getInsetSeparateLoopsFromAroundLoops(nestedRing.getLoopsToBeFilled(), radius, max(1.4 * radius, radiusAround)) + if len(allFillLoops) < 1: + return + if shouldExtraLoopsBeAdded: + nestedRing.extraLoops += allFillLoops + nestedRing.penultimateFillLoops = nestedRing.lastFillLoops + nestedRing.lastFillLoops = allFillLoops + +def createFillForSurroundings(nestedRings, radius, radiusAround, shouldExtraLoopsBeAdded): + 'Create extra fill loops for nested rings.' + for nestedRing in nestedRings: + createExtraFillLoops(nestedRing, radius, radiusAround, shouldExtraLoopsBeAdded) + +def getAdditionalLength( path, point, pointIndex ): + 'Get the additional length added by inserting a point into a path.' + if pointIndex == 0: + return abs( point - path[0] ) + if pointIndex == len(path): + return abs( point - path[-1] ) + return abs( point - path[pointIndex - 1] ) + abs( point - path[pointIndex] ) - abs( path[pointIndex] - path[pointIndex - 1] ) + +def getClosestOppositeIntersectionPaths( yIntersectionPaths ): + 'Get the close to center paths, starting with the first and an additional opposite if it exists.' + yIntersectionPaths.sort( compareDistanceFromCenter ) + beforeFirst = yIntersectionPaths[0].yMinusCenter < 0.0 + yCloseToCenterPaths = [ yIntersectionPaths[0] ] + for yIntersectionPath in yIntersectionPaths[1 :]: + beforeSecond = yIntersectionPath.yMinusCenter < 0.0 + if beforeFirst != beforeSecond: + yCloseToCenterPaths.append( yIntersectionPath ) + return yCloseToCenterPaths + return yCloseToCenterPaths + +def getCraftedText( fileName, gcodeText = '', repository=None): + 'Fill the inset file or gcode text.' + return getCraftedTextFromText( archive.getTextIfEmpty(fileName, gcodeText), repository ) + +def getCraftedTextFromText(gcodeText, repository=None): + 'Fill the inset gcode text.' + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'fill'): + return gcodeText + if repository == None: + repository = settings.getReadRepository( FillRepository() ) + if not repository.activateFill.value: + return gcodeText + return FillSkein().getCraftedGcode( repository, gcodeText ) + +def getKeyIsInPixelTableAddValue( key, pathIndexTable, pixelTable ): + 'Determine if the key is in the pixel table, and if it is and if the value is not None add it to the path index table.' + if key in pixelTable: + value = pixelTable[key] + if value != None: + pathIndexTable[value] = None + return True + return False + +def getLowerLeftCorner(nestedRings): + 'Get the lower left corner from the nestedRings.' + lowerLeftCorner = Vector3() + lowestRealPlusImaginary = 987654321.0 + for nestedRing in nestedRings: + for point in nestedRing.boundary: + realPlusImaginary = point.real + point.imag + if realPlusImaginary < lowestRealPlusImaginary: + lowestRealPlusImaginary = realPlusImaginary + lowerLeftCorner.setToXYZ(point.real, point.imag, nestedRing.z) + return lowerLeftCorner + +def getNewRepository(): + 'Get new repository.' + return FillRepository() + +def getNonIntersectingGridPointLine( gridPointInsetX, isJunctionWide, paths, pixelTable, yIntersectionPath, width ): + 'Get the points around the grid point that is junction wide that do not intersect.' + pointIndexPlusOne = yIntersectionPath.getPointIndexPlusOne() + path = yIntersectionPath.getPath(paths) + begin = path[ yIntersectionPath.pointIndex ] + end = path[ pointIndexPlusOne ] + plusMinusSign = getPlusMinusSign( end.real - begin.real ) + if isJunctionWide: + gridPointXFirst = complex( yIntersectionPath.gridPoint.real - plusMinusSign * gridPointInsetX, yIntersectionPath.gridPoint.imag ) + gridPointXSecond = complex( yIntersectionPath.gridPoint.real + plusMinusSign * gridPointInsetX, yIntersectionPath.gridPoint.imag ) + if isAddedPointOnPathFree( path, pixelTable, gridPointXSecond, pointIndexPlusOne, width ): + if isAddedPointOnPathFree( path, pixelTable, gridPointXFirst, pointIndexPlusOne, width ): + return [ gridPointXSecond, gridPointXFirst ] + if isAddedPointOnPathFree( path, pixelTable, yIntersectionPath.gridPoint, pointIndexPlusOne, width ): + return [ gridPointXSecond, yIntersectionPath.gridPoint ] + return [ gridPointXSecond ] + if isAddedPointOnPathFree( path, pixelTable, yIntersectionPath.gridPoint, pointIndexPlusOne, width ): + return [ yIntersectionPath.gridPoint ] + return [] + +def getPlusMinusSign(number): + 'Get one if the number is zero or positive else negative one.' + if number >= 0.0: + return 1.0 + return - 1.0 + +def getWithLeastLength( path, point ): + 'Insert a point into a path, at the index at which the path would be shortest.' + if len(path) < 1: + return 0 + shortestPointIndex = None + shortestAdditionalLength = 999999999987654321.0 + for pointIndex in xrange( len(path) + 1 ): + additionalLength = getAdditionalLength( path, point, pointIndex ) + if additionalLength < shortestAdditionalLength: + shortestAdditionalLength = additionalLength + shortestPointIndex = pointIndex + return shortestPointIndex + +def getYIntersectionInsideYSegment( segmentFirstY, segmentSecondY, beginComplex, endComplex, x ): + 'Get the y intersection inside the y segment if it does, else none.' + yIntersection = euclidean.getYIntersectionIfExists( beginComplex, endComplex, x ) + if yIntersection == None: + return None + if yIntersection < min( segmentFirstY, segmentSecondY ): + return None + if yIntersection <= max( segmentFirstY, segmentSecondY ): + return yIntersection + return None + +def insertGridPointPair( gridPoint, gridPointInsetX, gridPoints, isJunctionWide, paths, pixelTable, yIntersectionPath, width ): + 'Insert a pair of points around the grid point is is junction wide, otherwise inset one point.' + linePath = getNonIntersectingGridPointLine( gridPointInsetX, isJunctionWide, paths, pixelTable, yIntersectionPath, width ) + insertGridPointPairWithLinePath( gridPoint, gridPointInsetX, gridPoints, isJunctionWide, linePath, paths, pixelTable, yIntersectionPath, width ) + +def insertGridPointPairs( gridPoint, gridPointInsetX, gridPoints, intersectionPathFirst, intersectionPathSecond, isBothOrNone, isJunctionWide, paths, pixelTable, width ): + 'Insert a pair of points around a pair of grid points.' + gridPointLineFirst = getNonIntersectingGridPointLine( gridPointInsetX, isJunctionWide, paths, pixelTable, intersectionPathFirst, width ) + if len( gridPointLineFirst ) < 1: + if isBothOrNone: + return + intersectionPathSecond.gridPoint = gridPoint + insertGridPointPair( gridPoint, gridPointInsetX, gridPoints, isJunctionWide, paths, pixelTable, intersectionPathSecond, width ) + return + gridPointLineSecond = getNonIntersectingGridPointLine( gridPointInsetX, isJunctionWide, paths, pixelTable, intersectionPathSecond, width ) + if len( gridPointLineSecond ) > 0: + insertGridPointPairWithLinePath( gridPoint, gridPointInsetX, gridPoints, isJunctionWide, gridPointLineFirst, paths, pixelTable, intersectionPathFirst, width ) + insertGridPointPairWithLinePath( gridPoint, gridPointInsetX, gridPoints, isJunctionWide, gridPointLineSecond, paths, pixelTable, intersectionPathSecond, width ) + return + if isBothOrNone: + return + originalGridPointFirst = intersectionPathFirst.gridPoint + intersectionPathFirst.gridPoint = gridPoint + gridPointLineFirstCenter = getNonIntersectingGridPointLine( gridPointInsetX, isJunctionWide, paths, pixelTable, intersectionPathFirst, width ) + if len( gridPointLineFirstCenter ) > 0: + insertGridPointPairWithLinePath( gridPoint, gridPointInsetX, gridPoints, isJunctionWide, gridPointLineFirstCenter, paths, pixelTable, intersectionPathFirst, width ) + return + intersectionPathFirst.gridPoint = originalGridPointFirst + insertGridPointPairWithLinePath( gridPoint, gridPointInsetX, gridPoints, isJunctionWide, gridPointLineFirst, paths, pixelTable, intersectionPathFirst, width ) + +def insertGridPointPairWithLinePath( gridPoint, gridPointInsetX, gridPoints, isJunctionWide, linePath, paths, pixelTable, yIntersectionPath, width ): + 'Insert a pair of points around the grid point is is junction wide, otherwise inset one point.' + if len( linePath ) < 1: + return + if gridPoint in gridPoints: + gridPoints.remove( gridPoint ) + intersectionBeginPoint = None + moreThanInset = 2.1 * gridPointInsetX + path = yIntersectionPath.getPath(paths) + begin = path[ yIntersectionPath.pointIndex ] + end = path[ yIntersectionPath.getPointIndexPlusOne() ] + if yIntersectionPath.isOutside: + distanceX = end.real - begin.real + if abs( distanceX ) > 2.1 * moreThanInset: + intersectionBeginXDistance = yIntersectionPath.gridPoint.real - begin.real + endIntersectionXDistance = end.real - yIntersectionPath.gridPoint.real + intersectionPoint = begin * endIntersectionXDistance / distanceX + end * intersectionBeginXDistance / distanceX + distanceYAbsoluteInset = max( abs( yIntersectionPath.gridPoint.imag - intersectionPoint.imag ), moreThanInset ) + intersectionEndSegment = end - intersectionPoint + intersectionEndSegmentLength = abs( intersectionEndSegment ) + if intersectionEndSegmentLength > 1.1 * distanceYAbsoluteInset: + intersectionEndPoint = intersectionPoint + intersectionEndSegment * distanceYAbsoluteInset / intersectionEndSegmentLength + path.insert( yIntersectionPath.getPointIndexPlusOne(), intersectionEndPoint ) + intersectionBeginSegment = begin - intersectionPoint + intersectionBeginSegmentLength = abs( intersectionBeginSegment ) + if intersectionBeginSegmentLength > 1.1 * distanceYAbsoluteInset: + intersectionBeginPoint = intersectionPoint + intersectionBeginSegment * distanceYAbsoluteInset / intersectionBeginSegmentLength + for point in linePath: + addPointOnPath( path, yIntersectionPath.pathIndex, pixelTable, point, yIntersectionPath.getPointIndexPlusOne(), width ) + if intersectionBeginPoint != None: + addPointOnPath( path, yIntersectionPath.pathIndex, pixelTable, intersectionBeginPoint, yIntersectionPath.getPointIndexPlusOne(), width ) + +def isAddedPointOnPathFree( path, pixelTable, point, pointIndex, width ): + 'Determine if the point added to a path is intersecting the pixel table or the path.' + if pointIndex > 0 and pointIndex < len(path): + if isSharpCorner( ( path[pointIndex - 1] ), point, ( path[pointIndex] ) ): + return False + pointIndexMinusOne = pointIndex - 1 + if pointIndexMinusOne >= 0: + maskTable = {} + begin = path[ pointIndexMinusOne ] + if pointIndex < len(path): + end = path[pointIndex] + euclidean.addValueSegmentToPixelTable( begin, end, maskTable, None, width ) + segmentTable = {} + euclidean.addSegmentToPixelTable( point, begin, segmentTable, 0.0, 2.0, width ) + if euclidean.isPixelTableIntersecting( pixelTable, segmentTable, maskTable ): + return False + if isAddedPointOnPathIntersectingPath( begin, path, point, pointIndexMinusOne ): + return False + if pointIndex < len(path): + maskTable = {} + begin = path[pointIndex] + if pointIndexMinusOne >= 0: + end = path[ pointIndexMinusOne ] + euclidean.addValueSegmentToPixelTable( begin, end, maskTable, None, width ) + segmentTable = {} + euclidean.addSegmentToPixelTable( point, begin, segmentTable, 0.0, 2.0, width ) + if euclidean.isPixelTableIntersecting( pixelTable, segmentTable, maskTable ): + return False + if isAddedPointOnPathIntersectingPath( begin, path, point, pointIndex ): + return False + return True + +def isAddedPointOnPathIntersectingPath( begin, path, point, pointIndex ): + 'Determine if the point added to a path is intersecting the path by checking line intersection.' + segment = point - begin + segmentLength = abs(segment) + if segmentLength <= 0.0: + return False + normalizedSegment = segment / segmentLength + segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag) + pointRotated = segmentYMirror * point + beginRotated = segmentYMirror * begin + if euclidean.isXSegmentIntersectingPath( path[ max( 0, pointIndex - 20 ) : pointIndex ], pointRotated.real, beginRotated.real, segmentYMirror, pointRotated.imag ): + return True + return euclidean.isXSegmentIntersectingPath( path[ pointIndex + 1 : pointIndex + 21 ], pointRotated.real, beginRotated.real, segmentYMirror, pointRotated.imag ) + +def isIntersectingLoopsPaths( loops, paths, pointBegin, pointEnd ): + 'Determine if the segment between the first and second point is intersecting the loop list.' + normalizedSegment = pointEnd.dropAxis() - pointBegin.dropAxis() + normalizedSegmentLength = abs( normalizedSegment ) + if normalizedSegmentLength == 0.0: + return False + normalizedSegment /= normalizedSegmentLength + segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag) + pointBeginRotated = euclidean.getRoundZAxisByPlaneAngle( segmentYMirror, pointBegin ) + pointEndRotated = euclidean.getRoundZAxisByPlaneAngle( segmentYMirror, pointEnd ) + if euclidean.isLoopListIntersectingInsideXSegment( loops, pointBeginRotated.real, pointEndRotated.real, segmentYMirror, pointBeginRotated.imag ): + return True + return euclidean.isXSegmentIntersectingPaths( paths, pointBeginRotated.real, pointEndRotated.real, segmentYMirror, pointBeginRotated.imag ) + +def isPointAddedAroundClosest(layerInfillWidth, paths, pixelTable, removedEndpointPoint, width): + 'Add the closest removed endpoint to the path, with minimal twisting.' + closestDistanceSquared = 999999999987654321.0 + closestPathIndex = None + for pathIndex in xrange(len(paths)): + path = paths[ pathIndex ] + for pointIndex in xrange(len(path)): + point = path[pointIndex] + distanceSquared = abs(point - removedEndpointPoint) + if distanceSquared < closestDistanceSquared: + closestDistanceSquared = distanceSquared + closestPathIndex = pathIndex + if closestPathIndex == None: + return + if closestDistanceSquared < 0.8 * layerInfillWidth * layerInfillWidth: + return + closestPath = paths[closestPathIndex] + closestPointIndex = getWithLeastLength(closestPath, removedEndpointPoint) + if isAddedPointOnPathFree(closestPath, pixelTable, removedEndpointPoint, closestPointIndex, width): + addPointOnPath(closestPath, closestPathIndex, pixelTable, removedEndpointPoint, closestPointIndex, width) + return True + return isSidePointAdded(pixelTable, closestPath, closestPathIndex, closestPointIndex, layerInfillWidth, removedEndpointPoint, width) + +def isSegmentAround(aroundSegmentsDictionary, aroundSegmentsDictionaryKey, segment): + 'Determine if there is another segment around.' + if aroundSegmentsDictionaryKey not in aroundSegmentsDictionary: + return False + for aroundSegment in aroundSegmentsDictionary[aroundSegmentsDictionaryKey]: + endpoint = aroundSegment[0] + if isSegmentInX(segment, endpoint.point.real, endpoint.otherEndpoint.point.real): + return True + return False + +def isSegmentCompletelyInAnIntersection( segment, xIntersections ): + 'Add sparse endpoints from a segment.' + for xIntersectionIndex in xrange( 0, len( xIntersections ), 2 ): + surroundingXFirst = xIntersections[ xIntersectionIndex ] + surroundingXSecond = xIntersections[ xIntersectionIndex + 1 ] + if euclidean.isSegmentCompletelyInX( segment, surroundingXFirst, surroundingXSecond ): + return True + return False + +def isSegmentInX( segment, xFirst, xSecond ): + 'Determine if the segment overlaps within x.' + segmentFirstX = segment[0].point.real + segmentSecondX = segment[1].point.real + if min( segmentFirstX, segmentSecondX ) > max( xFirst, xSecond ): + return False + return max( segmentFirstX, segmentSecondX ) > min( xFirst, xSecond ) + +def isSharpCorner( beginComplex, centerComplex, endComplex ): + 'Determine if the three complex points form a sharp corner.' + centerBeginComplex = beginComplex - centerComplex + centerEndComplex = endComplex - centerComplex + centerBeginLength = abs( centerBeginComplex ) + centerEndLength = abs( centerEndComplex ) + if centerBeginLength <= 0.0 or centerEndLength <= 0.0: + return False + centerBeginComplex /= centerBeginLength + centerEndComplex /= centerEndLength + return euclidean.getDotProduct( centerBeginComplex, centerEndComplex ) > 0.9 + +def isSidePointAdded( pixelTable, closestPath, closestPathIndex, closestPointIndex, layerInfillWidth, removedEndpointPoint, width ): + 'Add side point along with the closest removed endpoint to the path, with minimal twisting.' + if closestPointIndex <= 0 or closestPointIndex >= len( closestPath ): + return False + pointBegin = closestPath[ closestPointIndex - 1 ] + pointEnd = closestPath[ closestPointIndex ] + removedEndpointPoint = removedEndpointPoint + closest = pointBegin + farthest = pointEnd + removedMinusClosest = removedEndpointPoint - pointBegin + removedMinusClosestLength = abs( removedMinusClosest ) + if removedMinusClosestLength <= 0.0: + return False + removedMinusOther = removedEndpointPoint - pointEnd + removedMinusOtherLength = abs( removedMinusOther ) + if removedMinusOtherLength <= 0.0: + return False + insertPointAfter = None + insertPointBefore = None + if removedMinusOtherLength < removedMinusClosestLength: + closest = pointEnd + farthest = pointBegin + removedMinusClosest = removedMinusOther + removedMinusClosestLength = removedMinusOtherLength + insertPointBefore = removedEndpointPoint + else: + insertPointAfter = removedEndpointPoint + removedMinusClosestNormalized = removedMinusClosest / removedMinusClosestLength + perpendicular = removedMinusClosestNormalized * complex( 0.0, layerInfillWidth ) + sidePoint = removedEndpointPoint + perpendicular + #extra check in case the line to the side point somehow slips by the line to the perpendicular + sidePointOther = removedEndpointPoint - perpendicular + if abs( sidePoint - farthest ) > abs( sidePointOther - farthest ): + perpendicular = - perpendicular + sidePoint = sidePointOther + maskTable = {} + closestSegmentTable = {} + toPerpendicularTable = {} + euclidean.addValueSegmentToPixelTable( pointBegin, pointEnd, maskTable, None, width ) + euclidean.addValueSegmentToPixelTable( closest, removedEndpointPoint, closestSegmentTable, None, width ) + euclidean.addValueSegmentToPixelTable( sidePoint, farthest, toPerpendicularTable, None, width ) + if euclidean.isPixelTableIntersecting( pixelTable, toPerpendicularTable, maskTable ) or euclidean.isPixelTableIntersecting( closestSegmentTable, toPerpendicularTable, maskTable ): + sidePoint = removedEndpointPoint - perpendicular + toPerpendicularTable = {} + euclidean.addValueSegmentToPixelTable( sidePoint, farthest, toPerpendicularTable, None, width ) + if euclidean.isPixelTableIntersecting( pixelTable, toPerpendicularTable, maskTable ) or euclidean.isPixelTableIntersecting( closestSegmentTable, toPerpendicularTable, maskTable ): + return False + if insertPointBefore != None: + addPointOnPathIfFree( closestPath, closestPathIndex, pixelTable, insertPointBefore, closestPointIndex, width ) + addPointOnPathIfFree( closestPath, closestPathIndex, pixelTable, sidePoint, closestPointIndex, width ) + if insertPointAfter != None: + addPointOnPathIfFree( closestPath, closestPathIndex, pixelTable, insertPointAfter, closestPointIndex, width ) + return True + +def removeEndpoints(layerInfillWidth, paths, pixelTable, removedEndpoints, aroundWidth): + 'Remove endpoints which are added to the path.' + for removedEndpointIndex in xrange(len(removedEndpoints) -1, -1, -1): + removedEndpoint = removedEndpoints[removedEndpointIndex] + removedEndpointPoint = removedEndpoint.point + if isPointAddedAroundClosest(layerInfillWidth, paths, pixelTable, removedEndpointPoint, aroundWidth): + removedEndpoints.remove(removedEndpoint ) + +def setIsOutside( yCloseToCenterPath, yIntersectionPaths ): + 'Determine if the yCloseToCenterPath is outside.' + beforeClose = yCloseToCenterPath.yMinusCenter < 0.0 + for yIntersectionPath in yIntersectionPaths: + if yIntersectionPath != yCloseToCenterPath: + beforePath = yIntersectionPath.yMinusCenter < 0.0 + if beforeClose == beforePath: + yCloseToCenterPath.isOutside = False + return + yCloseToCenterPath.isOutside = True + +def writeOutput(fileName, shouldAnalyze=True): + 'Fill an inset gcode file.' + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'fill', shouldAnalyze) + + +class FillRepository: + 'A class to handle the fill settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.fill.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Fill', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Fill') + self.activateFill = settings.BooleanSetting().getFromValue('Activate Fill', self, True) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Diaphragm -', self ) + self.diaphragmPeriod = settings.IntSpin().getFromValue( 20, 'Diaphragm Period (layers):', self, 200, 100 ) + self.diaphragmThickness = settings.IntSpin().getFromValue( 0, 'Diaphragm Thickness (layers):', self, 5, 0 ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Extra Shells -', self ) + self.extraShellsAlternatingSolidLayer = settings.IntSpin().getFromValue( 0, 'Extra Shells on Alternating Solid Layer (layers):', self, 3, 2 ) + self.extraShellsBase = settings.IntSpin().getFromValue( 0, 'Extra Shells on Base (layers):', self, 3, 1 ) + self.extraShellsSparseLayer = settings.IntSpin().getFromValue( 0, 'Extra Shells on Sparse Layer (layers):', self, 3, 1 ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Grid -', self ) + self.gridCircleSeparationOverEdgeWidth = settings.FloatSpin().getFromValue(0.0, 'Grid Circle Separation over Perimeter Width (ratio):', self, 1.0, 0.2) + self.gridExtraOverlap = settings.FloatSpin().getFromValue( 0.0, 'Grid Extra Overlap (ratio):', self, 0.5, 0.1 ) + self.gridJunctionSeparationBandHeight = settings.IntSpin().getFromValue( 0, 'Grid Junction Separation Band Height (layers):', self, 20, 10 ) + self.gridJunctionSeparationOverOctogonRadiusAtEnd = settings.FloatSpin().getFromValue( 0.0, 'Grid Junction Separation over Octogon Radius At End (ratio):', self, 0.8, 0.0 ) + self.gridJunctionSeparationOverOctogonRadiusAtMiddle = settings.FloatSpin().getFromValue( 0.0, 'Grid Junction Separation over Octogon Radius At Middle (ratio):', self, 0.8, 0.0 ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Infill -', self ) + self.infillBeginRotation = settings.FloatSpin().getFromValue( 0.0, 'Infill Begin Rotation (degrees):', self, 90.0, 45.0 ) + self.infillBeginRotationRepeat = settings.IntSpin().getFromValue( 0, 'Infill Begin Rotation Repeat (layers):', self, 3, 1 ) + self.infillOddLayerExtraRotation = settings.FloatSpin().getFromValue( 30.0, 'Infill Odd Layer Extra Rotation (degrees):', self, 90.0, 90.0 ) + self.infillPatternLabel = settings.LabelDisplay().getFromName('Infill Pattern:', self ) + infillLatentStringVar = settings.LatentStringVar() + self.infillPatternGridCircular = settings.Radio().getFromRadio( infillLatentStringVar, 'Grid Circular', self, False ) + self.infillPatternGridHexagonal = settings.Radio().getFromRadio( infillLatentStringVar, 'Grid Hexagonal', self, False ) + self.infillPatternGridRectangular = settings.Radio().getFromRadio( infillLatentStringVar, 'Grid Rectangular', self, False ) + self.infillPatternLine = settings.Radio().getFromRadio( infillLatentStringVar, 'Line', self, True ) + self.infillPerimeterOverlap = settings.FloatSpin().getFromValue( 0.0, 'Infill Perimeter Overlap (ratio):', self, 0.4, 0.15 ) + self.infillSolidity = settings.FloatSpin().getFromValue( 0.04, 'Infill Solidity (ratio):', self, 0.3, 0.2 ) + self.infillWidth = settings.FloatSpin().getFromValue( 0.1, 'Infill Width:', self, 1.7, 0.4 ) + settings.LabelSeparator().getFromRepository(self) + self.solidSurfaceThickness = settings.IntSpin().getFromValue(0, 'Solid Surface Thickness (layers):', self, 5, 3) + self.startFromChoice = settings.MenuButtonDisplay().getFromName('Start From Choice:', self) + self.startFromLowerLeft = settings.MenuRadio().getFromMenuButtonDisplay(self.startFromChoice, 'Lower Left', self, True) + self.startFromNearest = settings.MenuRadio().getFromMenuButtonDisplay(self.startFromChoice, 'Nearest', self, False) + self.surroundingAngle = settings.FloatSpin().getFromValue(30.0, 'Surrounding Angle (degrees):', self, 80.0, 60.0) + self.threadSequenceChoice = settings.MenuButtonDisplay().getFromName('Thread Sequence Choice:', self) + self.threadSequenceInfillLoops = settings.MenuRadio().getFromMenuButtonDisplay(self.threadSequenceChoice, 'Infill > Loops > Perimeter', self, False) + self.threadSequenceInfillPerimeter = settings.MenuRadio().getFromMenuButtonDisplay(self.threadSequenceChoice, 'Infill > Perimeter > Loops', self, False) + self.threadSequenceLoopsInfill = settings.MenuRadio().getFromMenuButtonDisplay(self.threadSequenceChoice, 'Loops > Infill > Perimeter', self, False) + self.threadSequenceLoopsPerimeter = settings.MenuRadio().getFromMenuButtonDisplay(self.threadSequenceChoice, 'Loops > Perimeter > Infill', self, True) + self.threadSequencePerimeterInfill = settings.MenuRadio().getFromMenuButtonDisplay(self.threadSequenceChoice, 'Perimeter > Infill > Loops', self, False) + self.threadSequencePerimeterLoops = settings.MenuRadio().getFromMenuButtonDisplay(self.threadSequenceChoice, 'Perimeter > Loops > Infill', self, False) + self.executeTitle = 'Fill' + + def execute(self): + 'Fill button has been clicked.' + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class FillSkein: + 'A class to fill a skein of extrusions.' + def __init__(self): + 'Initialize.' + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.edgeWidth = None + self.extruderActive = False + self.fillInset = 0.18 + self.isEdge = False + self.lastExtraShells = - 1 + self.lineIndex = 0 + self.oldLocation = None + self.oldOrderedLocation = None + self.rotatedLayer = None + self.rotatedLayers = [] + self.shutdownLineIndex = sys.maxint + self.nestedRing = None + self.thread = None + + def addFill(self, layerIndex): + 'Add fill to the carve layer.' +# if layerIndex > 2: +# return + settings.printProgressByNumber(layerIndex, len(self.rotatedLayers), 'fill') + arounds = [] + endpoints = [] + extraShells = self.repository.extraShellsSparseLayer.value + infillPaths = [] + layerFillInset = self.fillInset + layerInfillSolidity = self.infillSolidity + layerRemainder = layerIndex % int(round(self.repository.diaphragmPeriod.value)) + layerRotation = self.getLayerRotation(layerIndex) + pixelTable = {} + reverseRotation = complex(layerRotation.real, - layerRotation.imag) + rotatedLayer = self.rotatedLayers[layerIndex] + self.isDoubleJunction = True + self.isJunctionWide = True + surroundingCarves = [] + self.distanceFeedRate.addLine('( %s )' % rotatedLayer.z) + if layerRemainder >= int(round(self.repository.diaphragmThickness.value)): + for surroundingIndex in xrange(1, self.solidSurfaceThickness + 1): + self.addRotatedCarve(layerIndex, -surroundingIndex, reverseRotation, surroundingCarves) + self.addRotatedCarve(layerIndex, surroundingIndex, reverseRotation, surroundingCarves) + if len(surroundingCarves) < self.doubleSolidSurfaceThickness: + extraShells = self.repository.extraShellsAlternatingSolidLayer.value + if self.lastExtraShells != self.repository.extraShellsBase.value: + extraShells = self.repository.extraShellsBase.value + if rotatedLayer.rotation != None: + extraShells = 0 + self.distanceFeedRate.addLine('( %s )' % layerRotation) + self.distanceFeedRate.addLine('( %s )' % layerRotation) + aroundWidth = 0.34321 * self.infillWidth + doubleInfillWidth = 2.0 * self.infillWidth + gridPointInsetX = 0.5 * self.fillInset + self.lastExtraShells = extraShells + if self.repository.infillPatternGridHexagonal.value: + infillBeginRotationPolar = euclidean.getWiddershinsUnitPolar(self.infillBeginRotation) + if abs(euclidean.getDotProduct(layerRotation, infillBeginRotationPolar)) < math.sqrt( 0.5): + layerInfillSolidity *= 0.5 + self.isDoubleJunction = False + else: + self.isJunctionWide = False + nestedRings = euclidean.getOrderedNestedRings(rotatedLayer.nestedRings) + radiusAround = 0.5 * min(self.infillWidth, self.edgeWidth) + createFillForSurroundings(nestedRings, self.edgeMinusHalfInfillWidth, radiusAround, False) + for extraShellIndex in xrange(extraShells): + createFillForSurroundings(nestedRings, self.infillWidth, radiusAround, True) + fillLoops = euclidean.getFillOfSurroundings(nestedRings, None) + rotatedLoops = euclidean.getRotatedComplexLists(reverseRotation, fillLoops) + infillDictionary = triangle_mesh.getInfillDictionary(arounds, aroundWidth, self.fillInset, self.infillWidth, pixelTable, rotatedLoops) + if len(arounds) < 1: + self.addThreadsBridgeLayer(layerIndex, nestedRings, rotatedLayer) + return + self.horizontalSegmentsDictionary = {} + for infillDictionaryKey in infillDictionary.keys(): + xIntersections = infillDictionary[infillDictionaryKey] + xIntersections.sort() + y = infillDictionaryKey * self.infillWidth + self.horizontalSegmentsDictionary[infillDictionaryKey] = euclidean.getSegmentsFromXIntersections(xIntersections, y) + self.surroundingXIntersectionsDictionary = {} + gridCircular = False + removedEndpoints = [] + if len(surroundingCarves) >= self.doubleSolidSurfaceThickness: + if self.repository.infillPatternGridCircular.value and self.repository.infillSolidity.value > 0.0: + gridCircular = True + layerInfillSolidity = 0.0 + xSurroundingIntersectionsDictionaries = [infillDictionary] + for surroundingCarve in surroundingCarves: + xSurroundingIntersectionsDictionary = {} + euclidean.addXIntersectionsFromLoopsForTable(surroundingCarve, xSurroundingIntersectionsDictionary, self.infillWidth) + xSurroundingIntersectionsDictionaries.append(xSurroundingIntersectionsDictionary) + self.surroundingXIntersectionsDictionary = euclidean.getIntersectionOfXIntersectionsTables(xSurroundingIntersectionsDictionaries) + for horizontalSegmentsDictionaryKey in self.horizontalSegmentsDictionary.keys(): + if horizontalSegmentsDictionaryKey in self.surroundingXIntersectionsDictionary: + surroundingXIntersections = self.surroundingXIntersectionsDictionary[horizontalSegmentsDictionaryKey] + else: + surroundingXIntersections = [] + addSparseEndpoints(doubleInfillWidth, endpoints, self.horizontalSegmentsDictionary, horizontalSegmentsDictionaryKey, layerInfillSolidity, removedEndpoints, self.solidSurfaceThickness, surroundingXIntersections) + else: + for segments in self.horizontalSegmentsDictionary.values(): + for segment in segments: + endpoints += segment + paths = euclidean.getPathsFromEndpoints(endpoints, 5.0 * self.infillWidth, pixelTable, aroundWidth) + if gridCircular: + startAngle = euclidean.globalGoldenAngle * float(layerIndex) + for gridPoint in self.getGridPoints(fillLoops, reverseRotation): + self.addGridCircle(gridPoint, infillPaths, layerRotation, pixelTable, rotatedLoops, layerRotation, aroundWidth) + else: + if self.isGridToBeExtruded(): + self.addGrid( + arounds, fillLoops, gridPointInsetX, layerIndex, paths, pixelTable, reverseRotation, surroundingCarves, aroundWidth) + oldRemovedEndpointLength = len(removedEndpoints) + 1 + while oldRemovedEndpointLength - len(removedEndpoints) > 0: + oldRemovedEndpointLength = len(removedEndpoints) + removeEndpoints(self.infillWidth, paths, pixelTable, removedEndpoints, aroundWidth) + paths = euclidean.getConnectedPaths(paths, pixelTable, aroundWidth) + for path in paths: + addPath(self.infillWidth, infillPaths, path, layerRotation) + euclidean.transferPathsToNestedRings(nestedRings, infillPaths) + for fillLoop in fillLoops: + addInfillBoundary(fillLoop, nestedRings) + self.addThreadsBridgeLayer(layerIndex, nestedRings, rotatedLayer) + + def addGcodeFromThreadZ( self, thread, z ): + 'Add a gcode thread to the output.' + self.distanceFeedRate.addGcodeFromThreadZ( thread, z ) + + def addGrid(self, arounds, fillLoops, gridPointInsetX, layerIndex, paths, pixelTable, reverseRotation, surroundingCarves, width): + 'Add the grid to the infill layer.' + if len(surroundingCarves) < self.doubleSolidSurfaceThickness: + return + explodedPaths = [] + pathGroups = [] + for path in paths: + pathIndexBegin = len( explodedPaths ) + for pointIndex in xrange( len(path) - 1 ): + pathSegment = [ path[pointIndex], path[pointIndex + 1] ] + explodedPaths.append( pathSegment ) + pathGroups.append( ( pathIndexBegin, len( explodedPaths ) ) ) + for pathIndex in xrange( len( explodedPaths ) ): + explodedPath = explodedPaths[ pathIndex ] + euclidean.addPathToPixelTable( explodedPath, pixelTable, pathIndex, width ) + gridPoints = self.getGridPoints(fillLoops, reverseRotation) + gridPointInsetY = gridPointInsetX * ( 1.0 - self.repository.gridExtraOverlap.value ) + if self.repository.infillPatternGridRectangular.value: + gridBandHeight = self.repository.gridJunctionSeparationBandHeight.value + gridLayerRemainder = ( layerIndex - self.solidSurfaceThickness ) % gridBandHeight + halfBandHeight = 0.5 * float( gridBandHeight ) + halfBandHeightFloor = math.floor( halfBandHeight ) + fromMiddle = math.floor( abs( gridLayerRemainder - halfBandHeight ) ) + fromEnd = halfBandHeightFloor - fromMiddle + gridJunctionSeparation = self.gridJunctionEnd * fromMiddle + self.gridJunctionMiddle * fromEnd + gridJunctionSeparation /= halfBandHeightFloor + gridPointInsetX += gridJunctionSeparation + gridPointInsetY += gridJunctionSeparation + oldGridPointLength = len( gridPoints ) + 1 + while oldGridPointLength - len( gridPoints ) > 0: + oldGridPointLength = len( gridPoints ) + self.addRemainingGridPoints( arounds, gridPointInsetX, gridPointInsetY, gridPoints, True, explodedPaths, pixelTable, width ) + oldGridPointLength = len( gridPoints ) + 1 + while oldGridPointLength - len( gridPoints ) > 0: + oldGridPointLength = len( gridPoints ) + self.addRemainingGridPoints( arounds, gridPointInsetX, gridPointInsetY, gridPoints, False, explodedPaths, pixelTable, width ) + for pathGroupIndex in xrange( len( pathGroups ) ): + pathGroup = pathGroups[ pathGroupIndex ] + paths[ pathGroupIndex ] = [] + for explodedPathIndex in xrange( pathGroup[0], pathGroup[1] ): + explodedPath = explodedPaths[ explodedPathIndex ] + if len( paths[ pathGroupIndex ] ) == 0: + paths[ pathGroupIndex ] = explodedPath + else: + paths[ pathGroupIndex ] += explodedPath[1 :] + + def addGridCircle(self, center, infillPaths, layerRotation, pixelTable, rotatedLoops, startRotation, width): + 'Add circle to the grid.' + startAngle = -math.atan2(startRotation.imag, startRotation.real) + loop = euclidean.getComplexPolygon(center, self.gridCircleRadius, 17, startAngle) + loopPixelDictionary = {} + euclidean.addLoopToPixelTable(loop, loopPixelDictionary, width) + if not euclidean.isPixelTableIntersecting(pixelTable, loopPixelDictionary): + if euclidean.getIsInFilledRegion(rotatedLoops, euclidean.getLeftPoint(loop)): + addLoop(self.infillWidth, infillPaths, loop, layerRotation) + return + insideIndexPaths = [] + insideIndexPath = None + for pointIndex, point in enumerate(loop): + nextPoint = loop[(pointIndex + 1) % len(loop)] + segmentDictionary = {} + euclidean.addValueSegmentToPixelTable(point, nextPoint, segmentDictionary, None, width) + euclidean.addSquareTwoToPixelDictionary(segmentDictionary, point, None, width) + euclidean.addSquareTwoToPixelDictionary(segmentDictionary, nextPoint, None, width) + shouldAddLoop = not euclidean.isPixelTableIntersecting(pixelTable, segmentDictionary) + if shouldAddLoop: + shouldAddLoop = euclidean.getIsInFilledRegion(rotatedLoops, point) + if shouldAddLoop: + if insideIndexPath == None: + insideIndexPath = [pointIndex] + insideIndexPaths.append(insideIndexPath) + else: + insideIndexPath.append(pointIndex) + else: + insideIndexPath = None + if len(insideIndexPaths) > 1: + insideIndexPathFirst = insideIndexPaths[0] + insideIndexPathLast = insideIndexPaths[-1] + if insideIndexPathFirst[0] == 0 and insideIndexPathLast[-1] == len(loop) - 1: + insideIndexPaths[0] = insideIndexPathLast + insideIndexPathFirst + del insideIndexPaths[-1] + for insideIndexPath in insideIndexPaths: + path = [] + for insideIndex in insideIndexPath: + if len(path) == 0: + path.append(loop[insideIndex]) + path.append(loop[(insideIndex + 1) % len(loop)]) + addPath(self.infillWidth, infillPaths, path, layerRotation) + + def addGridLinePoints( self, begin, end, gridPoints, gridRotationAngle, offset, y ): + 'Add the segments of one line of a grid to the infill.' + if self.gridRadius == 0.0: + return + gridXStep = int(math.floor((begin) / self.gridXStepSize)) - 3 + gridXOffset = offset + self.gridXStepSize * float(gridXStep) + while gridXOffset < end: + if gridXOffset >= begin: + gridPointComplex = complex(gridXOffset, y) * gridRotationAngle + if self.repository.infillPatternGridCircular.value or self.isPointInsideLineSegments(gridPointComplex): + gridPoints.append(gridPointComplex) + gridXStep = self.getNextGripXStep(gridXStep) + gridXOffset = offset + self.gridXStepSize * float(gridXStep) + + def addRemainingGridPoints( + self, arounds, gridPointInsetX, gridPointInsetY, gridPoints, isBothOrNone, paths, pixelTable, width): + 'Add the remaining grid points to the grid point list.' + for gridPointIndex in xrange( len( gridPoints ) - 1, - 1, - 1 ): + gridPoint = gridPoints[ gridPointIndex ] + addAroundGridPoint( arounds, gridPoint, gridPointInsetX, gridPointInsetY, gridPoints, self.gridRadius, isBothOrNone, self.isDoubleJunction, self.isJunctionWide, paths, pixelTable, width ) + + def addRotatedCarve(self, currentLayer, layerDelta, reverseRotation, surroundingCarves): + 'Add a rotated carve to the surrounding carves.rotatedCarveDictionary' + layerIndex = currentLayer + layerDelta + if layerIndex < 0 or layerIndex >= len(self.rotatedLayers): + return + layerDifference = abs(layerDelta) + rotatedLayer = self.rotatedLayers[layerIndex] + if layerDifference in rotatedLayer.rotatedCarveDictionary: + surroundingCarves.append(rotatedLayer.rotatedCarveDictionary[layerDifference]) + return + nestedRings = rotatedLayer.nestedRings + rotatedCarve = [] + for nestedRing in nestedRings: + planeRotatedLoop = euclidean.getRotatedComplexes(reverseRotation, nestedRing.boundary) + rotatedCarve.append(planeRotatedLoop) + outsetRadius = float(layerDifference) * self.layerHeight * self.surroundingSlope - self.edgeWidth + if outsetRadius > 0.0: + rotatedCarve = intercircle.getInsetSeparateLoopsFromAroundLoops(rotatedCarve, -outsetRadius, self.layerHeight) + surroundingCarves.append(rotatedCarve) + rotatedLayer.rotatedCarveDictionary[layerDifference] = rotatedCarve + + def addThreadsBridgeLayer(self, layerIndex, nestedRings, rotatedLayer, testLoops=None): + 'Add the threads, add the bridge end & the layer end tag.' + if self.oldOrderedLocation == None or self.repository.startFromLowerLeft.value: + self.oldOrderedLocation = getLowerLeftCorner(nestedRings) + extrusionHalfWidth = 0.5 * self.infillWidth + threadSequence = self.threadSequence + if layerIndex < 1: + threadSequence = ['edge', 'loops', 'infill'] + euclidean.addToThreadsRemove(extrusionHalfWidth, nestedRings, self.oldOrderedLocation, self, threadSequence) + if testLoops != None: + for testLoop in testLoops: + self.addGcodeFromThreadZ(testLoop, self.oldOrderedLocation.z) + self.distanceFeedRate.addLine('()') + if rotatedLayer.rotation != None: + self.distanceFeedRate.addLine('()') + self.distanceFeedRate.addLine('()') + + def addToThread(self, location): + 'Add a location to thread.' + if self.oldLocation == None: + return + if self.isEdge: + self.nestedRing.addToLoop( location ) + return + if self.thread == None: + self.thread = [ self.oldLocation.dropAxis() ] + self.nestedRing.edgePaths.append(self.thread) + self.thread.append(location.dropAxis()) + + def getCraftedGcode( self, repository, gcodeText ): + 'Parse gcode text and store the bevel gcode.' + self.repository = repository + self.lines = archive.getTextLines(gcodeText) + self.threadSequence = None + if repository.threadSequenceInfillLoops.value: + self.threadSequence = ['infill', 'loops', 'edge'] + if repository.threadSequenceInfillPerimeter.value: + self.threadSequence = ['infill', 'edge', 'loops'] + if repository.threadSequenceLoopsInfill.value: + self.threadSequence = ['loops', 'infill', 'edge'] + if repository.threadSequenceLoopsPerimeter.value: + self.threadSequence = ['loops', 'edge', 'infill'] + if repository.threadSequencePerimeterInfill.value: + self.threadSequence = ['edge', 'infill', 'loops'] + if repository.threadSequencePerimeterLoops.value: + self.threadSequence = ['edge', 'loops', 'infill'] + if self.repository.infillPerimeterOverlap.value > 0.45: + print('') + print('!!! WARNING !!!') + print('"Infill Perimeter Overlap" is greater than 0.45, which may create problems with the infill, like threads going through empty space and/or the extruder switching on and off a lot.') + print('If you want to stretch the infill a lot, set "Path Stretch over Perimeter Width" in stretch to a high value instead of setting "Infill Perimeter Overlap" to a high value.') + print('') + self.parseInitialization() + if self.edgeWidth == None: + print('Warning, nothing will be done because self.edgeWidth in getCraftedGcode in FillSkein was None.') + return '' + self.fillInset = self.infillWidth - self.infillWidth * self.repository.infillPerimeterOverlap.value + self.infillSolidity = repository.infillSolidity.value + self.edgeMinusHalfInfillWidth = self.edgeWidth - 0.5 * self.infillWidth + if self.isGridToBeExtruded(): + self.setGridVariables(repository) + self.infillBeginRotation = math.radians( repository.infillBeginRotation.value ) + self.infillOddLayerExtraRotation = math.radians( repository.infillOddLayerExtraRotation.value ) + self.solidSurfaceThickness = int( round( self.repository.solidSurfaceThickness.value ) ) + self.doubleSolidSurfaceThickness = self.solidSurfaceThickness + self.solidSurfaceThickness + for lineIndex in xrange(self.lineIndex, len(self.lines)): + self.parseLine( lineIndex ) + for layerIndex in xrange(len(self.rotatedLayers)): + self.addFill(layerIndex) + self.distanceFeedRate.addLines( self.lines[ self.shutdownLineIndex : ] ) + return self.distanceFeedRate.output.getvalue() + + def getGridPoints(self, fillLoops, reverseRotation): + 'Get the grid points.' + if self.infillSolidity > 0.8: + return [] + rotationBaseAngle = euclidean.getWiddershinsUnitPolar(self.infillBeginRotation) + reverseRotationBaseAngle = complex(rotationBaseAngle.real, - rotationBaseAngle.imag) + gridRotationAngle = reverseRotation * rotationBaseAngle + slightlyGreaterThanFillInset = intercircle.globalIntercircleMultiplier * self.gridInset + triangle_mesh.sortLoopsInOrderOfArea(True, fillLoops) + rotatedLoops = euclidean.getRotatedComplexLists(reverseRotationBaseAngle, fillLoops) + if self.repository.infillPatternGridCircular.value: + return self.getGridPointsByLoops( + gridRotationAngle, intercircle.getInsetSeparateLoopsFromLoops(rotatedLoops, -self.gridCircleRadius)) + return self.getGridPointsByLoops(gridRotationAngle, intercircle.getInsetSeparateLoopsFromLoops(rotatedLoops, self.gridInset)) + + def getGridPointsByLoops(self, gridRotationAngle, loops): + 'Get the grid points by loops.' + gridIntersectionsDictionary = {} + gridPoints = [] + euclidean.addXIntersectionsFromLoopsForTable(loops, gridIntersectionsDictionary, self.gridRadius) + for gridIntersectionsKey in gridIntersectionsDictionary: + y = gridIntersectionsKey * self.gridRadius + self.gridRadius * 0.5 + gridIntersections = gridIntersectionsDictionary[gridIntersectionsKey] + gridIntersections.sort() + gridIntersectionsLength = len(gridIntersections) + if gridIntersectionsLength % 2 == 1: + gridIntersectionsLength -= 1 + for gridIntersectionIndex in xrange(0, gridIntersectionsLength, 2): + begin = gridIntersections[gridIntersectionIndex] + end = gridIntersections[gridIntersectionIndex + 1] + offset = self.offsetMultiplier * (gridIntersectionsKey % 2) + self.offsetBaseX + self.addGridLinePoints(begin, end, gridPoints, gridRotationAngle, offset, y) + return gridPoints + + def getLayerRotation(self, layerIndex): + 'Get the layer rotation.' + rotation = self.rotatedLayers[layerIndex].rotation + if rotation != None: + return rotation + infillBeginRotationRepeat = self.repository.infillBeginRotationRepeat.value + infillOddLayerRotationMultiplier = float( layerIndex % ( infillBeginRotationRepeat + 1 ) == infillBeginRotationRepeat ) + layerAngle = self.infillBeginRotation + infillOddLayerRotationMultiplier * self.infillOddLayerExtraRotation + return euclidean.getWiddershinsUnitPolar(layerAngle) + + def getNextGripXStep( self, gridXStep ): + 'Get the next grid x step, increment by an extra one every three if hexagonal grid is chosen.' + gridXStep += 1 + if self.repository.infillPatternGridHexagonal.value: + if gridXStep % 3 == 0: + gridXStep += 1 + return gridXStep + + def isGridToBeExtruded(self): + 'Determine if the grid is to be extruded.' + if self.repository.infillPatternLine.value: + return False + return self.repository.infillSolidity.value > 0.0 + + def isPointInsideLineSegments( self, gridPoint ): + 'Is the point inside the line segments of the loops.' + if self.solidSurfaceThickness <= 0: + return True + fillLine = int(round(gridPoint.imag / self.infillWidth)) + if fillLine not in self.horizontalSegmentsDictionary: + return False + if fillLine not in self.surroundingXIntersectionsDictionary: + return False + lineSegments = self.horizontalSegmentsDictionary[fillLine] + surroundingXIntersections = self.surroundingXIntersectionsDictionary[fillLine] + for lineSegment in lineSegments: + if isSegmentCompletelyInAnIntersection(lineSegment, surroundingXIntersections ): + xFirst = lineSegment[0].point.real + xSecond = lineSegment[1].point.real + if gridPoint.real > min(xFirst, xSecond) and gridPoint.real < max(xFirst, xSecond): + return True + return False + + def linearMove( self, splitLine ): + 'Add a linear move to the thread.' + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + if self.extruderActive: + self.addToThread( location ) + self.oldLocation = location + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addLine(line) + return + elif firstWord == '(': + self.layerHeight = float(splitLine[1]) + self.infillWidth = self.repository.infillWidth.value + self.surroundingSlope = math.tan(math.radians(min(self.repository.surroundingAngle.value, 80.0))) + self.distanceFeedRate.addTagRoundedLine('infillPerimeterOverlap', self.repository.infillPerimeterOverlap.value) + self.distanceFeedRate.addTagRoundedLine('infillWidth', self.infillWidth) + elif firstWord == '(': + self.edgeWidth = float(splitLine[1]) + threadSequenceString = ' '.join( self.threadSequence ) + self.distanceFeedRate.addTagBracketedLine('threadSequenceString', threadSequenceString ) + elif firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('fill') + self.distanceFeedRate.addLine(line) + + def parseLine( self, lineIndex ): + 'Parse a gcode line and add it to the fill skein.' + line = self.lines[lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + self.linearMove(splitLine) + elif firstWord == 'M101': + self.extruderActive = True + elif firstWord == 'M103': + self.extruderActive = False + self.isEdge = False + self.thread = None + elif firstWord == '()': + self.nestedRing = euclidean.NestedBand() + self.rotatedLayer.nestedRings.append( self.nestedRing ) + elif firstWord == '()': + self.nestedRing = None + elif firstWord == '(': + location = gcodec.getLocationFromSplitLine(None, splitLine) + self.nestedRing.addToBoundary( location ) + elif firstWord == '(': + self.rotatedLayer.rotation = gcodec.getRotationBySplitLine(splitLine) + elif firstWord == '()': + self.shutdownLineIndex = lineIndex + elif firstWord == '(': + self.rotatedLayer = RotatedLayer(float(splitLine[1])) + self.rotatedLayers.append( self.rotatedLayer ) + self.thread = None + elif firstWord == '(': + self.isEdge = True + + def setGridVariables( self, repository ): + 'Set the grid variables.' + self.gridInset = 1.2 * self.infillWidth + self.gridRadius = self.infillWidth / self.infillSolidity + self.gridXStepSize = 2.0 * self.gridRadius + self.offsetMultiplier = self.gridRadius + if self.repository.infillPatternGridHexagonal.value: + self.gridXStepSize = 4.0 / 3.0 * self.gridRadius + self.offsetMultiplier = 1.5 * self.gridXStepSize + if self.repository.infillPatternGridCircular.value: + self.gridRadius += self.gridRadius + self.gridXStepSize = self.gridRadius / math.sqrt(.75) + self.offsetMultiplier = 0.5 * self.gridXStepSize + circleInsetOverEdgeWidth = repository.gridCircleSeparationOverEdgeWidth.value + 0.5 + self.gridMinimumCircleRadius = self.edgeWidth + self.gridInset = self.gridMinimumCircleRadius + self.gridCircleRadius = self.offsetMultiplier - circleInsetOverEdgeWidth * self.edgeWidth + if self.gridCircleRadius < self.gridMinimumCircleRadius: + print('') + print('!!! WARNING !!!') + print('Grid Circle Separation over Edge Width is too high, which makes the grid circles too small.') + print('You should reduce Grid Circle Separation over Edge Width to a reasonable value, like the default of 0.5.') + print('The grid circle radius will be set to the minimum grid circle radius.') + print('') + self.gridCircleRadius = self.gridMinimumCircleRadius + self.offsetBaseX = 0.25 * self.gridXStepSize + if self.repository.infillPatternGridRectangular.value: + halfGridMinusWidth = 0.5 * ( self.gridRadius - self.infillWidth ) + self.gridJunctionEnd = halfGridMinusWidth * repository.gridJunctionSeparationOverOctogonRadiusAtEnd.value + self.gridJunctionMiddle = halfGridMinusWidth * repository.gridJunctionSeparationOverOctogonRadiusAtMiddle.value + + +class RotatedLayer: + 'A rotated layer.' + def __init__( self, z ): + 'Initialize.' + self.rotatedCarveDictionary = {} + self.rotation = None + self.nestedRings = [] + self.z = z + + def __repr__(self): + 'Get the string representation of this RotatedLayer.' + return '%s, %s, %s' % ( self.z, self.rotation, self.nestedRings ) + + +class YIntersectionPath: + 'A class to hold the y intersection position, the loop which it intersected and the point index of the loop which it intersected.' + def __init__( self, pathIndex, pointIndex, y ): + 'Initialize from the path, point index, and y.' + self.pathIndex = pathIndex + self.pointIndex = pointIndex + self.y = y + + def __repr__(self): + 'Get the string representation of this y intersection.' + return '%s, %s, %s' % ( self.pathIndex, self.pointIndex, self.y ) + + def getPath( self, paths ): + 'Get the path from the paths and path index.' + return paths[ self.pathIndex ] + + def getPointIndexPlusOne(self): + 'Get the point index plus one.' + return self.pointIndex + 1 + + +def main(): + 'Display the fill dialog.' + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/fillet.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/fillet.py new file mode 100644 index 0000000..d5af827 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/fillet.py @@ -0,0 +1,393 @@ +""" +This page is in the table of contents. +Fillet rounds the corners slightly in a variety of ways. This is to reduce corner blobbing and sudden extruder acceleration. + +The fillet manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Fillet + +==Operation== +The default 'Activate Fillet' checkbox is off. When it is on, the functions described below will work, when it is off, nothing will be done. + +==Settings== +===Fillet Procedure Choice=== +Default is 'Bevel''. + +====Arc Point==== +When selected, the corners will be filleted with an arc using the gcode point form. + +====Arc Radius==== +When selected, the corners will be filleted with an arc using the gcode radius form. + +====Arc Segment==== +When selected, the corners will be filleted with an arc composed of several segments. + +====Bevel==== +When selected, the corners will be beveled. + +===Corner Feed Rate Multiplier=== +Default: 1.0 + +Defines the ratio of the feed rate in corners over the original feed rate. With a high value the extruder will move quickly in corners, accelerating quickly and leaving a thin extrusion. With a low value, the extruder will move slowly in corners, accelerating gently and leaving a thick extrusion. + +===Fillet Radius over Perimeter Width=== +Default is 0.35. + +Defines the width of the fillet. + +===Reversal Slowdown over Perimeter Width=== +Default is 0.5. + +Defines how far before a path reversal the extruder will slow down. Some tools, like nozzle wipe, double back the path of the extruder and this option will add a slowdown point in that path so there won't be a sudden jerk at the end of the path. If the value is less than 0.1 a slowdown will not be added. + +===Use Intermediate Feed Rate in Corners=== +Default is on. + +When selected, the feed rate entering the corner will be the average of the old feed rate and the new feed rate. + +==Examples== +The following examples fillet the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and fillet.py. + +> python fillet.py +This brings up the fillet dialog. + +> python fillet.py Screw Holder Bottom.stl +The fillet tool is parsing the file: +Screw Holder Bottom.stl +.. +The fillet tool has created the file: +.. Screw Holder Bottom_fillet.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import sys + + +__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 getCraftedText( fileName, gcodeText, repository = None ): + "Fillet a gcode linear move file or text." + return getCraftedTextFromText( archive.getTextIfEmpty( fileName, gcodeText ), repository ) + +def getCraftedTextFromText( gcodeText, repository = None ): + "Fillet a gcode linear move text." + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'fillet'): + return gcodeText + if repository == None: + repository = settings.getReadRepository( FilletRepository() ) + if not repository.activateFillet.value: + return gcodeText + if repository.arcPoint.value: + return ArcPointSkein().getCraftedGcode( repository, gcodeText ) + elif repository.arcRadius.value: + return ArcRadiusSkein().getCraftedGcode( repository, gcodeText ) + elif repository.arcSegment.value: + return ArcSegmentSkein().getCraftedGcode( repository, gcodeText ) + elif repository.bevel.value: + return BevelSkein().getCraftedGcode( repository, gcodeText ) + return gcodeText + +def getNewRepository(): + 'Get new repository.' + return FilletRepository() + +def writeOutput(fileName, shouldAnalyze=True): + "Fillet a gcode linear move file. Depending on the settings, either arcPoint, arcRadius, arcSegment, bevel or do nothing." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'fillet', shouldAnalyze) + + +class BevelSkein: + "A class to bevel a skein of extrusions." + def __init__(self): + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.extruderActive = False + self.feedRateMinute = 960.0 + self.filletRadius = 0.2 + self.lineIndex = 0 + self.lines = None + self.oldFeedRateMinute = None + self.oldLocation = None + self.shouldAddLine = True + + def addLinearMovePoint( self, feedRateMinute, point ): + "Add a gcode linear move, feedRate and newline to the output." + self.distanceFeedRate.addLine( self.distanceFeedRate.getLinearGcodeMovementWithFeedRate( feedRateMinute, point.dropAxis(), point.z ) ) + + def getCornerFeedRate(self): + "Get the corner feed rate, which may be based on the intermediate feed rate." + feedRateMinute = self.feedRateMinute + if self.repository.useIntermediateFeedRateInCorners.value: + if self.oldFeedRateMinute != None: + feedRateMinute = 0.5 * ( self.oldFeedRateMinute + self.feedRateMinute ) + return feedRateMinute * self.cornerFeedRateMultiplier + + def getCraftedGcode( self, repository, gcodeText ): + "Parse gcode text and store the bevel gcode." + self.cornerFeedRateMultiplier = repository.cornerFeedRateMultiplier.value + self.lines = archive.getTextLines(gcodeText) + self.repository = repository + self.parseInitialization( repository ) + for self.lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[self.lineIndex] + self.parseLine(line) + return self.distanceFeedRate.output.getvalue() + + def getExtruderOffReversalPoint( self, afterSegment, afterSegmentComplex, beforeSegment, beforeSegmentComplex, location ): + "If the extruder is off and the path is reversing, add intermediate slow points." + if self.repository.reversalSlowdownDistanceOverEdgeWidth.value < 0.1: + return None + if self.extruderActive: + return None + reversalBufferSlowdownDistance = self.reversalSlowdownDistance * 2.0 + afterSegmentComplexLength = abs( afterSegmentComplex ) + if afterSegmentComplexLength < reversalBufferSlowdownDistance: + return None + beforeSegmentComplexLength = abs( beforeSegmentComplex ) + if beforeSegmentComplexLength < reversalBufferSlowdownDistance: + return None + afterSegmentComplexNormalized = afterSegmentComplex / afterSegmentComplexLength + beforeSegmentComplexNormalized = beforeSegmentComplex / beforeSegmentComplexLength + if euclidean.getDotProduct( afterSegmentComplexNormalized, beforeSegmentComplexNormalized ) < 0.95: + return None + slowdownFeedRate = self.feedRateMinute * 0.5 + self.shouldAddLine = False + beforePoint = euclidean.getPointPlusSegmentWithLength( self.reversalSlowdownDistance * abs( beforeSegment ) / beforeSegmentComplexLength, location, beforeSegment ) + self.addLinearMovePoint( self.feedRateMinute, beforePoint ) + self.addLinearMovePoint( slowdownFeedRate, location ) + afterPoint = euclidean.getPointPlusSegmentWithLength( self.reversalSlowdownDistance * abs( afterSegment ) / afterSegmentComplexLength, location, afterSegment ) + self.addLinearMovePoint( slowdownFeedRate, afterPoint ) + return afterPoint + + def getNextLocation(self): + "Get the next linear move. Return none is none is found." + for afterIndex in xrange( self.lineIndex + 1, len(self.lines) ): + line = self.lines[ afterIndex ] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if gcodec.getFirstWord(splitLine) == 'G1': + nextLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + return nextLocation + return None + + def linearMove( self, splitLine ): + "Bevel a linear move." + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + self.feedRateMinute = gcodec.getFeedRateMinute( self.feedRateMinute, splitLine ) + if self.oldLocation != None: + nextLocation = self.getNextLocation() + if nextLocation != None: + location = self.splitPointGetAfter( location, nextLocation ) + self.oldLocation = location + self.oldFeedRateMinute = self.feedRateMinute + + def parseInitialization( self, repository ): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('fillet') + return + elif firstWord == '(': + edgeWidth = abs(float(splitLine[1])) + self.curveSection = 0.7 * edgeWidth + self.filletRadius = edgeWidth * repository.filletRadiusOverEdgeWidth.value + self.minimumRadius = 0.1 * edgeWidth + self.reversalSlowdownDistance = edgeWidth * repository.reversalSlowdownDistanceOverEdgeWidth.value + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + "Parse a gcode line and add it to the bevel gcode." + self.shouldAddLine = True + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + self.linearMove(splitLine) + elif firstWord == 'M101': + self.extruderActive = True + elif firstWord == 'M103': + self.extruderActive = False + if self.shouldAddLine: + self.distanceFeedRate.addLine(line) + + def splitPointGetAfter( self, location, nextLocation ): + "Bevel a point and return the end of the bevel. should get complex for radius" + if self.filletRadius < 2.0 * self.minimumRadius: + return location + afterSegment = nextLocation - location + afterSegmentComplex = afterSegment.dropAxis() + afterSegmentComplexLength = abs( afterSegmentComplex ) + thirdAfterSegmentLength = 0.333 * afterSegmentComplexLength + if thirdAfterSegmentLength < self.minimumRadius: + return location + beforeSegment = self.oldLocation - location + beforeSegmentComplex = beforeSegment.dropAxis() + beforeSegmentComplexLength = abs( beforeSegmentComplex ) + thirdBeforeSegmentLength = 0.333 * beforeSegmentComplexLength + if thirdBeforeSegmentLength < self.minimumRadius: + return location + extruderOffReversalPoint = self.getExtruderOffReversalPoint( afterSegment, afterSegmentComplex, beforeSegment, beforeSegmentComplex, location ) + if extruderOffReversalPoint != None: + return extruderOffReversalPoint + bevelRadius = min( thirdAfterSegmentLength, self.filletRadius ) + bevelRadius = min( thirdBeforeSegmentLength, bevelRadius ) + self.shouldAddLine = False + beforePoint = euclidean.getPointPlusSegmentWithLength( bevelRadius * abs( beforeSegment ) / beforeSegmentComplexLength, location, beforeSegment ) + self.addLinearMovePoint( self.feedRateMinute, beforePoint ) + afterPoint = euclidean.getPointPlusSegmentWithLength( bevelRadius * abs( afterSegment ) / afterSegmentComplexLength, location, afterSegment ) + self.addLinearMovePoint( self.getCornerFeedRate(), afterPoint ) + return afterPoint + + +class ArcSegmentSkein( BevelSkein ): + "A class to arc segment a skein of extrusions." + def addArc( self, afterCenterDifferenceAngle, afterPoint, beforeCenterSegment, beforePoint, center ): + "Add arc segments to the filleted skein." + absoluteDifferenceAngle = abs( afterCenterDifferenceAngle ) +# steps = int( math.ceil( absoluteDifferenceAngle * 1.5 ) ) + steps = int( math.ceil( min( absoluteDifferenceAngle * 1.5, absoluteDifferenceAngle * abs( beforeCenterSegment ) / self.curveSection ) ) ) + stepPlaneAngle = euclidean.getWiddershinsUnitPolar( afterCenterDifferenceAngle / steps ) + for step in xrange( 1, steps ): + beforeCenterSegment = euclidean.getRoundZAxisByPlaneAngle( stepPlaneAngle, beforeCenterSegment ) + arcPoint = center + beforeCenterSegment + self.addLinearMovePoint( self.getCornerFeedRate(), arcPoint ) + self.addLinearMovePoint( self.getCornerFeedRate(), afterPoint ) + + def splitPointGetAfter( self, location, nextLocation ): + "Fillet a point into arc segments and return the end of the last segment." + if self.filletRadius < 2.0 * self.minimumRadius: + return location + afterSegment = nextLocation - location + afterSegmentComplex = afterSegment.dropAxis() + thirdAfterSegmentLength = 0.333 * abs( afterSegmentComplex ) + if thirdAfterSegmentLength < self.minimumRadius: + return location + beforeSegment = self.oldLocation - location + beforeSegmentComplex = beforeSegment.dropAxis() + thirdBeforeSegmentLength = 0.333 * abs( beforeSegmentComplex ) + if thirdBeforeSegmentLength < self.minimumRadius: + return location + extruderOffReversalPoint = self.getExtruderOffReversalPoint( afterSegment, afterSegmentComplex, beforeSegment, beforeSegmentComplex, location ) + if extruderOffReversalPoint != None: + return extruderOffReversalPoint + bevelRadius = min( thirdAfterSegmentLength, self.filletRadius ) + bevelRadius = min( thirdBeforeSegmentLength, bevelRadius ) + self.shouldAddLine = False + beforePoint = euclidean.getPointPlusSegmentWithLength( bevelRadius * abs( beforeSegment ) / abs( beforeSegmentComplex ), location, beforeSegment ) + self.addLinearMovePoint( self.feedRateMinute, beforePoint ) + afterPoint = euclidean.getPointPlusSegmentWithLength( bevelRadius * abs( afterSegment ) / abs( afterSegmentComplex ), location, afterSegment ) + afterPointComplex = afterPoint.dropAxis() + beforePointComplex = beforePoint.dropAxis() + locationComplex = location.dropAxis() + midpoint = 0.5 * ( afterPoint + beforePoint ) + midpointComplex = midpoint.dropAxis() + midpointMinusLocationComplex = midpointComplex - locationComplex + midpointLocationLength = abs( midpointMinusLocationComplex ) + if midpointLocationLength < 0.01 * self.filletRadius: + self.addLinearMovePoint( self.getCornerFeedRate(), afterPoint ) + return afterPoint + midpointAfterPointLength = abs( midpointComplex - afterPointComplex ) + midpointCenterLength = midpointAfterPointLength * midpointAfterPointLength / midpointLocationLength + radius = math.sqrt( midpointCenterLength * midpointCenterLength + midpointAfterPointLength * midpointAfterPointLength ) + centerComplex = midpointComplex + midpointMinusLocationComplex * midpointCenterLength / midpointLocationLength + center = Vector3( centerComplex.real, centerComplex.imag, midpoint.z ) + afterCenterComplex = afterPointComplex - centerComplex + beforeCenter = beforePoint - center + angleDifference = euclidean.getAngleDifferenceByComplex( afterCenterComplex, beforeCenter.dropAxis() ) + self.addArc( angleDifference, afterPoint, beforeCenter, beforePoint, center ) + return afterPoint + + +class ArcPointSkein( ArcSegmentSkein ): + "A class to arc point a skein of extrusions." + def addArc( self, afterCenterDifferenceAngle, afterPoint, beforeCenterSegment, beforePoint, center ): + "Add an arc point to the filleted skein." + if afterCenterDifferenceAngle == 0.0: + return + afterPointMinusBefore = afterPoint - beforePoint + centerMinusBefore = center - beforePoint + firstWord = 'G3' + if afterCenterDifferenceAngle < 0.0: + firstWord = 'G2' + centerMinusBeforeComplex = centerMinusBefore.dropAxis() + if abs( centerMinusBeforeComplex ) <= 0.0: + return + radius = abs( centerMinusBefore ) + arcDistanceZ = complex( abs( afterCenterDifferenceAngle ) * radius, afterPointMinusBefore.z ) + distance = abs( arcDistanceZ ) + if distance <= 0.0: + return + line = self.distanceFeedRate.getFirstWordMovement( firstWord, afterPointMinusBefore ) + self.getRelativeCenter( centerMinusBeforeComplex ) + cornerFeedRate = self.getCornerFeedRate() + if cornerFeedRate != None: + line += ' F' + self.distanceFeedRate.getRounded(cornerFeedRate) + self.distanceFeedRate.addLine(line) + + def getRelativeCenter( self, centerMinusBeforeComplex ): + "Get the relative center." + return ' I%s J%s' % ( self.distanceFeedRate.getRounded( centerMinusBeforeComplex.real ), self.distanceFeedRate.getRounded( centerMinusBeforeComplex.imag ) ) + + +class ArcRadiusSkein( ArcPointSkein ): + "A class to arc radius a skein of extrusions." + def getRelativeCenter( self, centerMinusBeforeComplex ): + "Get the relative center." + radius = abs( centerMinusBeforeComplex ) + return ' R' + ( self.distanceFeedRate.getRounded(radius) ) + + +class FilletRepository: + "A class to handle the fillet settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.fillet.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File to be Filleted', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Fillet') + self.activateFillet = settings.BooleanSetting().getFromValue('Activate Fillet', self, False ) + self.filletProcedureChoiceLabel = settings.LabelDisplay().getFromName('Fillet Procedure Choice: ', self ) + filletLatentStringVar = settings.LatentStringVar() + self.arcPoint = settings.Radio().getFromRadio( filletLatentStringVar, 'Arc Point', self, False ) + self.arcRadius = settings.Radio().getFromRadio( filletLatentStringVar, 'Arc Radius', self, False ) + self.arcSegment = settings.Radio().getFromRadio( filletLatentStringVar, 'Arc Segment', self, False ) + self.bevel = settings.Radio().getFromRadio( filletLatentStringVar, 'Bevel', self, True ) + self.cornerFeedRateMultiplier = settings.FloatSpin().getFromValue(0.8, 'Corner Feed Rate Multiplier (ratio):', self, 1.2, 1.0) + self.filletRadiusOverEdgeWidth = settings.FloatSpin().getFromValue( 0.25, 'Fillet Radius over Perimeter Width (ratio):', self, 0.65, 0.35 ) + self.reversalSlowdownDistanceOverEdgeWidth = settings.FloatSpin().getFromValue( 0.3, 'Reversal Slowdown Distance over Perimeter Width (ratio):', self, 0.7, 0.5 ) + self.useIntermediateFeedRateInCorners = settings.BooleanSetting().getFromValue('Use Intermediate Feed Rate in Corners', self, True ) + self.executeTitle = 'Fillet' + + def execute(self): + "Fillet button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +def main(): + "Display the fillet dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/flow.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/flow.py new file mode 100644 index 0000000..c462de7 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/flow.py @@ -0,0 +1,145 @@ +""" +This page is in the table of contents. +The flow script sets the flow rate by writing the M108 gcode. + +==Operation== +The default 'Activate Flow' checkbox is on. When it is on, the functions described below will work, when it is off, the functions will not be called. + +==Settings== +===Flow Rate=== +Default is 210. + +Defines the flow rate which will be written following the M108 command. The flow rate is usually a PWM setting, but could be anything, like the rpm of the tool or the duty cycle of the tool. + +==Examples== +The following examples flow the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and flow.py. + +> python flow.py +This brings up the flow dialog. + +> python flow.py Screw Holder Bottom.stl +The flow tool is parsing the file: +Screw Holder Bottom.stl +.. +The flow tool has created the file: +.. Screw Holder Bottom_flow.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import sys + + +__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 getCraftedText( fileName, text='', flowRepository = None ): + "Flow the file or text." + return getCraftedTextFromText( archive.getTextIfEmpty(fileName, text), flowRepository ) + +def getCraftedTextFromText( gcodeText, flowRepository = None ): + "Flow a gcode linear move text." + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'flow'): + return gcodeText + if flowRepository == None: + flowRepository = settings.getReadRepository( FlowRepository() ) + if not flowRepository.activateFlow.value: + return gcodeText + return FlowSkein().getCraftedGcode( gcodeText, flowRepository ) + +def getNewRepository(): + 'Get new repository.' + return FlowRepository() + +def writeOutput(fileName, shouldAnalyze=True): + "Flow a gcode linear move file." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'flow', shouldAnalyze) + + +class FlowRepository: + "A class to handle the flow settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.flow.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Flow', self, '') + self.activateFlow = settings.BooleanSetting().getFromValue('Activate Flow', self, True ) + self.flowRate = settings.FloatSpin().getFromValue( 50.0, 'Flow Rate (arbitrary units):', self, 250.0, 210.0 ) + self.executeTitle = 'Flow' + + def execute(self): + "Flow button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class FlowSkein: + "A class to flow a skein of extrusions." + def __init__(self): + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.lineIndex = 0 + self.lines = None + self.oldFlowRate = None + self.oldLocation = None + + def addFlowRateLine(self): + "Add flow rate line." + flowRate = self.flowRepository.flowRate.value + if flowRate != self.oldFlowRate: + self.distanceFeedRate.addLine('M108 S' + euclidean.getFourSignificantFigures(flowRate)) + self.oldFlowRate = flowRate + + def getCraftedGcode( self, gcodeText, flowRepository ): + "Parse gcode text and store the flow gcode." + self.flowRepository = flowRepository + self.lines = archive.getTextLines(gcodeText) + self.parseInitialization() + for line in self.lines[self.lineIndex :]: + self.parseLine(line) + return self.distanceFeedRate.output.getvalue() + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('flow') + return + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + "Parse a gcode line and add it to the flow skein." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1' or firstWord == '(': + self.addFlowRateLine() + self.distanceFeedRate.addLine(line) + + +def main(): + "Display the flow dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/home.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/home.py new file mode 100644 index 0000000..56016be --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/home.py @@ -0,0 +1,198 @@ +""" +This page is in the table of contents. +Plugin to home the tool at beginning of each layer. + +The home manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Home + +==Operation== +The default 'Activate Home' checkbox is on. When it is on, the functions described below will work, when it is off, nothing will be done. + +==Settings== +===Name of Home File=== +Default: home.gcode + +At the beginning of a each layer, home will add the commands of a gcode script with the name of the "Name of Home File" setting, if one exists. Home does not care if the text file names are capitalized, but some file systems do not handle file name cases properly, so to be on the safe side you should give them lower case names. Home looks for those files in the alterations folder in the .skeinforge folder in the home directory. If it doesn't find the file it then looks in the alterations folder in the skeinforge_plugins folder. + +==Examples== +The following examples home the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and home.py. + +> python home.py +This brings up the home dialog. + +> python home.py Screw Holder Bottom.stl +The home tool is parsing the file: +Screw Holder Bottom.stl +.. +The home tool has created the file: +.. Screw Holder Bottom_home.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import os +import sys + + +__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 getCraftedText( fileName, text, repository = None ): + "Home a gcode linear move file or text." + return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository) + +def getCraftedTextFromText( gcodeText, repository = None ): + "Home a gcode linear move text." + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'home'): + return gcodeText + if repository == None: + repository = settings.getReadRepository( HomeRepository() ) + if not repository.activateHome.value: + return gcodeText + return HomeSkein().getCraftedGcode(gcodeText, repository) + +def getNewRepository(): + 'Get new repository.' + return HomeRepository() + +def writeOutput(fileName, shouldAnalyze=True): + "Home a gcode linear move file. Chain home the gcode if it is not already homed." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'home', shouldAnalyze) + + +class HomeRepository: + "A class to handle the home settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.home.html', self) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Home', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Home') + self.activateHome = settings.BooleanSetting().getFromValue('Activate Home', self, False ) + self.nameOfHomeFile = settings.StringSetting().getFromValue('Name of Home File:', self, 'home.gcode') + self.executeTitle = 'Home' + + def execute(self): + "Home button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class HomeSkein: + "A class to home a skein of extrusions." + def __init__(self): + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.extruderActive = False + self.highestZ = None + self.homeLines = [] + self.layerCount = settings.LayerCount() + self.lineIndex = 0 + self.lines = None + self.oldLocation = None + self.shouldHome = False + self.travelFeedRateMinute = 957.0 + + def addFloat( self, begin, end ): + "Add dive to the original height." + beginEndDistance = begin.distance(end) + alongWay = self.absoluteEdgeWidth / beginEndDistance + closeToEnd = euclidean.getIntermediateLocation( alongWay, end, begin ) + closeToEnd.z = self.highestZ + self.distanceFeedRate.addLine( self.distanceFeedRate.getLinearGcodeMovementWithFeedRate( self.travelFeedRateMinute, closeToEnd.dropAxis(), closeToEnd.z ) ) + + def addHomeTravel( self, splitLine ): + "Add the home travel gcode." + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + self.highestZ = max( self.highestZ, location.z ) + if not self.shouldHome: + return + self.shouldHome = False + if self.oldLocation == None: + return + if self.extruderActive: + self.distanceFeedRate.addLine('M103') + self.addHopUp( self.oldLocation ) + self.distanceFeedRate.addLinesSetAbsoluteDistanceMode(self.homeLines) + self.addHopUp( self.oldLocation ) + self.addFloat( self.oldLocation, location ) + if self.extruderActive: + self.distanceFeedRate.addLine('M101') + + def addHopUp(self, location): + "Add hop to highest point." + locationUp = Vector3( location.x, location.y, self.highestZ ) + self.distanceFeedRate.addLine( self.distanceFeedRate.getLinearGcodeMovementWithFeedRate( self.travelFeedRateMinute, locationUp.dropAxis(), locationUp.z ) ) + + def getCraftedGcode( self, gcodeText, repository ): + "Parse gcode text and store the home gcode." + self.repository = repository + self.homeLines = settings.getAlterationFileLines(repository.nameOfHomeFile.value) + if len(self.homeLines) < 1: + return gcodeText + self.lines = archive.getTextLines(gcodeText) + self.parseInitialization( repository ) + for self.lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[self.lineIndex] + self.parseLine(line) + return self.distanceFeedRate.output.getvalue() + + def parseInitialization( self, repository ): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('home') + return + elif firstWord == '(': + self.absoluteEdgeWidth = abs(float(splitLine[1])) + elif firstWord == '(': + self.travelFeedRateMinute = 60.0 * float(splitLine[1]) + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + "Parse a gcode line and add it to the bevel gcode." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + self.addHomeTravel(splitLine) + self.oldLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + elif firstWord == '(': + self.layerCount.printProgressIncrement('home') + if len(self.homeLines) > 0: + self.shouldHome = True + elif firstWord == 'M101': + self.extruderActive = True + elif firstWord == 'M103': + self.extruderActive = False + self.distanceFeedRate.addLine(line) + + +def main(): + "Display the home dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/hop.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/hop.py new file mode 100644 index 0000000..9b4ac36 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/hop.py @@ -0,0 +1,223 @@ +""" +This page is in the table of contents. +Hop is a script to raise the extruder when it is not extruding. + +Note: + +Note: In some cases where you have thin overhang this plugin can help solve the problem object being knocked off by the head + +The hop manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Hop + +==Operation== +The default 'Activate Hop' checkbox is off. + +It is off because Vik and Nophead found better results without hopping. Numerous users reported better output without this plugin hence it is off by default. + +When activated the extruder will hop when traveling. When it is off, nothing will be done. + +==Settings== +===Hop Over Layer Thickness=== +Default is one. + +Defines the ratio of the hop height over the layer height, this is the most important hop setting. + +===Minimum Hop Angle=== +Default is 20 degrees. + +Defines the minimum angle that the path of the extruder will be raised. An angle of ninety means that the extruder will go straight up as soon as it is not extruding and a low angle means the extruder path will gradually rise to the hop height. + +==Examples== +The following examples hop the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and hop.py. + +> python hop.py +This brings up the hop dialog. + +> python hop.py Screw Holder Bottom.stl +The hop tool is parsing the file: +Screw Holder Bottom.stl +.. +The hop tool has created the file: +.. Screw Holder Bottom_hop.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import sys + + +__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 getCraftedText( fileName, text, hopRepository = None ): + "Hop a gcode linear move text." + return getCraftedTextFromText( archive.getTextIfEmpty(fileName, text), hopRepository ) + +def getCraftedTextFromText( gcodeText, hopRepository = None ): + "Hop a gcode linear move text." + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'hop'): + return gcodeText + if hopRepository == None: + hopRepository = settings.getReadRepository( HopRepository() ) + if not hopRepository.activateHop.value: + return gcodeText + return HopSkein().getCraftedGcode( gcodeText, hopRepository ) + +def getNewRepository(): + 'Get new repository.' + return HopRepository() + +def writeOutput(fileName, shouldAnalyze=True): + "Hop a gcode linear move file. Chain hop the gcode if it is not already hopped." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'hop', shouldAnalyze) + + +class HopRepository: + "A class to handle the hop settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.hop.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Hop', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Hop') + self.activateHop = settings.BooleanSetting().getFromValue('Activate Hop', self, False ) + self.hopOverLayerThickness = settings.FloatSpin().getFromValue( 0.5, 'Hop Over Layer Thickness (ratio):', self, 1.5, 1.0 ) + self.minimumHopAngle = settings.FloatSpin().getFromValue( 20.0, 'Minimum Hop Angle (degrees):', self, 60.0, 30.0 ) + self.executeTitle = 'Hop' + + def execute(self): + "Hop button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class HopSkein: + "A class to hop a skein of extrusions." + def __init__(self): + 'Initialize' + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.extruderActive = False + self.feedRateMinute = 961.0 + self.hopHeight = 0.4 + self.hopDistance = self.hopHeight + self.justDeactivated = False + self.layerCount = settings.LayerCount() + self.lineIndex = 0 + self.lines = None + self.oldLocation = None + + def getCraftedGcode( self, gcodeText, hopRepository ): + "Parse gcode text and store the hop gcode." + self.lines = archive.getTextLines(gcodeText) + self.minimumSlope = math.tan( math.radians( hopRepository.minimumHopAngle.value ) ) + self.parseInitialization( hopRepository ) + for self.lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[self.lineIndex] + self.parseLine(line) + return self.distanceFeedRate.output.getvalue() + + def getHopLine(self, line): + "Get hopped gcode line." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + self.feedRateMinute = gcodec.getFeedRateMinute( self.feedRateMinute, splitLine ) + if self.extruderActive: + return line + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + highestZ = location.z + if self.oldLocation != None: + highestZ = max( highestZ, self.oldLocation.z ) + highestZHop = highestZ + self.hopHeight + locationComplex = location.dropAxis() + if self.justDeactivated: + oldLocationComplex = self.oldLocation.dropAxis() + distance = abs( locationComplex - oldLocationComplex ) + if distance < self.minimumDistance: + if self.isNextTravel() or distance == 0.0: + return self.distanceFeedRate.getLineWithZ( line, splitLine, highestZHop ) + alongRatio = min( 0.41666666, self.hopDistance / distance ) + oneMinusAlong = 1.0 - alongRatio + closeLocation = oldLocationComplex * oneMinusAlong + locationComplex * alongRatio + self.distanceFeedRate.addLine( self.distanceFeedRate.getLineWithZ( line, splitLine, highestZHop ) ) + if self.isNextTravel(): + return self.distanceFeedRate.getLineWithZ( line, splitLine, highestZHop ) + farLocation = oldLocationComplex * alongRatio + locationComplex * oneMinusAlong + self.distanceFeedRate.addGcodeMovementZWithFeedRate( self.feedRateMinute, farLocation, highestZHop ) + return line + if self.isNextTravel(): + return self.distanceFeedRate.getLineWithZ( line, splitLine, highestZHop ) + return line + + def isNextTravel(self): + "Determine if there is another linear travel before the thread ends." + for afterIndex in xrange( self.lineIndex + 1, len(self.lines) ): + line = self.lines[ afterIndex ] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'G1': + return True + if firstWord == 'M101': + return False + return False + + def parseInitialization( self, hopRepository ): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '(': + layerHeight = float(splitLine[1]) + self.hopHeight = hopRepository.hopOverLayerThickness.value * layerHeight + self.hopDistance = self.hopHeight / self.minimumSlope + self.minimumDistance = 0.5 * layerHeight + elif firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('hop') + return + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + "Parse a gcode line and add it to the bevel gcode." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if self.distanceFeedRate.getIsAlteration(line): + return + if firstWord == 'G1': + line = self.getHopLine(line) + self.oldLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + self.justDeactivated = False + elif firstWord == '(': + self.layerCount.printProgressIncrement('hop') + elif firstWord == 'M101': + self.extruderActive = True + elif firstWord == 'M103': + self.extruderActive = False + self.justDeactivated = True + self.distanceFeedRate.addLineCheckAlteration(line) + + +def main(): + "Display the hop dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/inset.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/inset.py new file mode 100644 index 0000000..8f1ec92 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/inset.py @@ -0,0 +1,471 @@ +#! /usr/bin/env python +""" +This page is in the table of contents. +Inset will inset the outside outlines by half the edge width, and outset the inside outlines by the same amount. + +The inset manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Inset + +==Settings== +===Add Custom Code for Temperature Reading=== +Default is on. + +When selected, the M105 custom code for temperature reading will be added at the beginning of the file. + +===Infill in Direction of Bridge=== +Default is on. + +When selected, the infill will be in the direction of any bridge across a gap, so that the fill will be able to span a bridge easier. + +===Loop Order Choice=== +Default loop order choice is 'Ascending Area'. + +When overlap is to be removed, for each loop, the overlap is checked against the list of loops already extruded. If the latest loop overlaps an already extruded loop, the overlap is removed from the latest loop. The loops are ordered according to their areas. + +====Ascending Area==== +When selected, the loops will be ordered in ascending area. With thin walled parts, if overlap is being removed the outside of the container will not be extruded. Holes will be the correct size. + +====Descending Area==== +When selected, the loops will be ordered in descending area. With thin walled parts, if overlap is being removed the inside of the container will not be extruded. Holes will be missing the interior wall so they will be slightly wider than model size. + +===Overlap Removal Width over Perimeter Width=== +Default is 0.6. + +Defines the ratio of the overlap removal width over the edge width. Any part of the extrusion that comes within the overlap removal width of another is removed. This is to prevent the extruder from depositing two extrusions right beside each other. If the 'Overlap Removal Width over Perimeter Width' is less than 0.2, the overlap will not be removed. + +===Turn Extruder Heater Off at Shut Down=== +Default is on. + +When selected, the M104 S0 gcode line will be added to the end of the file to turn the extruder heater off by setting the extruder heater temperature to 0. + +==Examples== +The following examples inset the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and inset.py. + +> python inset.py +This brings up the inset dialog. + +> python inset.py Screw Holder Bottom.stl +The inset tool is parsing the file: +Screw Holder Bottom.stl +.. +The inset tool has created the file: +.. Screw Holder Bottom_inset.gcode + +""" + +from __future__ import absolute_import +try: + import psyco + psyco.full() +except: + pass +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import cmath +import math +import os +import sys + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def addAlreadyFilledArounds( alreadyFilledArounds, loop, radius ): + "Add already filled loops around loop to alreadyFilledArounds." + radius = abs(radius) + alreadyFilledLoop = [] + slightlyGreaterThanRadius = intercircle.globalIntercircleMultiplier * radius + muchGreaterThanRadius = 2.5 * radius + centers = intercircle.getCentersFromLoop( loop, slightlyGreaterThanRadius ) + for center in centers: + alreadyFilledInset = intercircle.getSimplifiedInsetFromClockwiseLoop( center, radius ) + if intercircle.isLargeSameDirection( alreadyFilledInset, center, radius ): + alreadyFilledLoop.append( alreadyFilledInset ) + if len( alreadyFilledLoop ) > 0: + alreadyFilledArounds.append( alreadyFilledLoop ) + +def addSegmentOutline( isThick, outlines, pointBegin, pointEnd, width ): + "Add a diamond or hexagonal outline for a line segment." + width = abs( width ) + exclusionWidth = 0.6 * width + slope = 0.2 + if isThick: + slope = 3.0 + exclusionWidth = 0.8 * width + segment = pointEnd - pointBegin + segmentLength = abs(segment) + if segmentLength == 0.0: + return + normalizedSegment = segment / segmentLength + outline = [] + segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag) + pointBeginRotated = segmentYMirror * pointBegin + pointEndRotated = segmentYMirror * pointEnd + along = 0.05 + alongLength = along * segmentLength + if alongLength > 0.1 * exclusionWidth: + along *= 0.1 * exclusionWidth / alongLength + alongEnd = 1.0 - along + remainingToHalf = 0.5 - along + alongToWidth = exclusionWidth / slope / segmentLength + pointBeginIntermediate = euclidean.getIntermediateLocation( along, pointBeginRotated, pointEndRotated ) + pointEndIntermediate = euclidean.getIntermediateLocation( alongEnd, pointBeginRotated, pointEndRotated ) + outline.append( pointBeginIntermediate ) + verticalWidth = complex( 0.0, exclusionWidth ) + if alongToWidth > 0.9 * remainingToHalf: + verticalWidth = complex( 0.0, slope * remainingToHalf * segmentLength ) + middle = ( pointBeginIntermediate + pointEndIntermediate ) * 0.5 + middleDown = middle - verticalWidth + middleUp = middle + verticalWidth + outline.append( middleUp ) + outline.append( pointEndIntermediate ) + outline.append( middleDown ) + else: + alongOutsideBegin = along + alongToWidth + alongOutsideEnd = alongEnd - alongToWidth + outsideBeginCenter = euclidean.getIntermediateLocation( alongOutsideBegin, pointBeginRotated, pointEndRotated ) + outsideBeginCenterDown = outsideBeginCenter - verticalWidth + outsideBeginCenterUp = outsideBeginCenter + verticalWidth + outsideEndCenter = euclidean.getIntermediateLocation( alongOutsideEnd, pointBeginRotated, pointEndRotated ) + outsideEndCenterDown = outsideEndCenter - verticalWidth + outsideEndCenterUp = outsideEndCenter + verticalWidth + outline.append( outsideBeginCenterUp ) + outline.append( outsideEndCenterUp ) + outline.append( pointEndIntermediate ) + outline.append( outsideEndCenterDown ) + outline.append( outsideBeginCenterDown ) + outlines.append( euclidean.getRotatedComplexes( normalizedSegment, outline ) ) + +def getBridgeDirection(belowLoops, layerLoops, radius): + 'Get span direction for the majority of the overhanging extrusion edge, if any.' + if len(belowLoops) < 1: + return None + belowOutsetLoops = intercircle.getInsetLoopsFromLoops(belowLoops, -radius) + bridgeRotation = complex() + for loop in layerLoops: + for pointIndex, point in enumerate(loop): + previousIndex = (pointIndex + len(loop) - 1) % len(loop) + bridgeRotation += getOverhangDirection(belowOutsetLoops, loop[previousIndex], point) + if abs(bridgeRotation) < 0.75 * radius: + return None + else: + return cmath.sqrt(bridgeRotation / abs(bridgeRotation)) + +def getCraftedText( fileName, text='', repository=None): + "Inset the preface file or text." + return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository) + +def getCraftedTextFromText(gcodeText, repository=None): + "Inset the preface gcode text." + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'inset'): + return gcodeText + if repository == None: + repository = settings.getReadRepository( InsetRepository() ) + return InsetSkein().getCraftedGcode(gcodeText, repository) + +def getDoubledRoundZ( overhangingSegment, segmentRoundZ ): + 'Get doubled plane angle around z of the overhanging segment.' + endpoint = overhangingSegment[0] + roundZ = endpoint.point - endpoint.otherEndpoint.point + roundZ *= segmentRoundZ + if abs( roundZ ) == 0.0: + return complex() + if roundZ.real < 0.0: + roundZ *= - 1.0 + roundZLength = abs( roundZ ) + return roundZ * roundZ / roundZLength + +def getInteriorSegments(loops, segments): + 'Get segments inside the loops.' + interiorSegments = [] + for segment in segments: + center = 0.5 * (segment[0].point + segment[1].point) + if euclidean.getIsInFilledRegion(loops, center): + interiorSegments.append(segment) + return interiorSegments + +def getIsIntersectingWithinList(loop, loopList): + "Determine if the loop is intersecting or is within the loop list." + leftPoint = euclidean.getLeftPoint(loop) + for otherLoop in loopList: + if euclidean.getNumberOfIntersectionsToLeft(otherLoop, leftPoint) % 2 == 1: + return True + return euclidean.isLoopIntersectingLoops(loop, loopList) + +def getNewRepository(): + 'Get new repository.' + return InsetRepository() + +def getOverhangDirection( belowOutsetLoops, segmentBegin, segmentEnd ): + 'Add to span direction from the endpoint segments which overhang the layer below.' + segment = segmentEnd - segmentBegin + normalizedSegment = euclidean.getNormalized( complex( segment.real, segment.imag ) ) + segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag) + segmentBegin = segmentYMirror * segmentBegin + segmentEnd = segmentYMirror * segmentEnd + solidXIntersectionList = [] + y = segmentBegin.imag + solidXIntersectionList.append( euclidean.XIntersectionIndex( - 1.0, segmentBegin.real ) ) + solidXIntersectionList.append( euclidean.XIntersectionIndex( - 1.0, segmentEnd.real ) ) + for belowLoopIndex in xrange( len( belowOutsetLoops ) ): + belowLoop = belowOutsetLoops[ belowLoopIndex ] + rotatedOutset = euclidean.getRotatedComplexes( segmentYMirror, belowLoop ) + euclidean.addXIntersectionIndexesFromLoopY( rotatedOutset, belowLoopIndex, solidXIntersectionList, y ) + overhangingSegments = euclidean.getSegmentsFromXIntersectionIndexes( solidXIntersectionList, y ) + overhangDirection = complex() + for overhangingSegment in overhangingSegments: + overhangDirection += getDoubledRoundZ( overhangingSegment, normalizedSegment ) + return overhangDirection + +def getSegmentsFromLoopListsPoints( loopLists, pointBegin, pointEnd ): + "Get endpoint segments from the beginning and end of a line segment." + normalizedSegment = pointEnd - pointBegin + normalizedSegmentLength = abs( normalizedSegment ) + if normalizedSegmentLength == 0.0: + return [] + normalizedSegment /= normalizedSegmentLength + segmentYMirror = complex(normalizedSegment.real, -normalizedSegment.imag) + pointBeginRotated = segmentYMirror * pointBegin + pointEndRotated = segmentYMirror * pointEnd + rotatedLoopLists = [] + for loopList in loopLists: + rotatedLoopLists.append(euclidean.getRotatedComplexLists(segmentYMirror, loopList)) + xIntersectionIndexList = [] + xIntersectionIndexList.append( euclidean.XIntersectionIndex( - 1, pointBeginRotated.real ) ) + xIntersectionIndexList.append( euclidean.XIntersectionIndex( - 1, pointEndRotated.real ) ) + euclidean.addXIntersectionIndexesFromLoopListsY( rotatedLoopLists, xIntersectionIndexList, pointBeginRotated.imag ) + segments = euclidean.getSegmentsFromXIntersectionIndexes( xIntersectionIndexList, pointBeginRotated.imag ) + for segment in segments: + for endpoint in segment: + endpoint.point *= normalizedSegment + return segments + +def isCloseToLast( paths, point, radius ): + "Determine if the point is close to the last point of the last path." + if len(paths) < 1: + return False + lastPath = paths[-1] + return abs( lastPath[-1] - point ) < radius + +def isIntersectingItself( loop, width ): + "Determine if the loop is intersecting itself." + outlines = [] + for pointIndex in xrange(len(loop)): + pointBegin = loop[pointIndex] + pointEnd = loop[(pointIndex + 1) % len(loop)] + if euclidean.isLineIntersectingLoops( outlines, pointBegin, pointEnd ): + return True + addSegmentOutline( False, outlines, pointBegin, pointEnd, width ) + return False + +def isIntersectingWithinLists( loop, loopLists ): + "Determine if the loop is intersecting or is within the loop lists." + for loopList in loopLists: + if getIsIntersectingWithinList( loop, loopList ): + return True + return False + +def writeOutput(fileName, shouldAnalyze=True): + "Inset the carving of a gcode file." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'inset', shouldAnalyze) + + +class InsetRepository: + "A class to handle the inset settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.inset.html', self) + self.baseNameSynonymDictionary = { + 'Infill in Direction of Bridge' : 'carve.csv', + 'Infill Width over Thickness (ratio):' : 'fill.csv'} + self.fileNameInput = settings.FileNameInput().getFromFileName(fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Inset', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Inset') + self.addCustomCodeForTemperatureReading = settings.BooleanSetting().getFromValue('Add Custom Code for Temperature Reading', self, True) + self.infillInDirectionOfBridge = settings.BooleanSetting().getFromValue('Infill in Direction of Bridge', self, True) + self.infillWidthOverThickness = settings.FloatSpin().getFromValue(1.3, 'Infill Width over Thickness (ratio):', self, 1.7, 1.5) + self.loopOrderChoice = settings.MenuButtonDisplay().getFromName('Loop Order Choice:', self ) + self.loopOrderAscendingArea = settings.MenuRadio().getFromMenuButtonDisplay(self.loopOrderChoice, 'Ascending Area', self, True) + self.loopOrderDescendingArea = settings.MenuRadio().getFromMenuButtonDisplay(self.loopOrderChoice, 'Descending Area', self, False) + self.overlapRemovalWidthOverEdgeWidth = settings.FloatSpin().getFromValue(0.3, 'Overlap Removal Width over Perimeter Width (ratio):', self, 0.9, 0.6) + self.turnExtruderHeaterOffAtShutDown = settings.BooleanSetting().getFromValue('Turn Extruder Heater Off at Shut Down', self, True) + self.executeTitle = 'Inset' + + def execute(self): + "Inset button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class InsetSkein: + "A class to inset a skein of extrusions." + def __init__(self): + 'Initialize.' + self.belowLoops = [] + self.boundary = None + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.layerCount = settings.LayerCount() + self.lineIndex = 0 + self.loopLayer = None + + def addGcodeFromPerimeterPaths(self, isIntersectingSelf, loop, loopLayer, loopLists, radius): + "Add the edge paths to the output." + segments = [] + outlines = [] + thickOutlines = [] + allLoopLists = loopLists[:] + [thickOutlines] + aroundLists = loopLists + for pointIndex in xrange(len(loop)): + pointBegin = loop[pointIndex] + pointEnd = loop[(pointIndex + 1) % len(loop)] + if isIntersectingSelf: + if euclidean.isLineIntersectingLoops(outlines, pointBegin, pointEnd): + segments += getSegmentsFromLoopListsPoints(allLoopLists, pointBegin, pointEnd) + else: + segments += getSegmentsFromLoopListsPoints(loopLists, pointBegin, pointEnd) + addSegmentOutline(False, outlines, pointBegin, pointEnd, self.overlapRemovalWidth) + addSegmentOutline(True, thickOutlines, pointBegin, pointEnd, self.overlapRemovalWidth) + else: + segments += getSegmentsFromLoopListsPoints(loopLists, pointBegin, pointEnd) + edgePaths = [] + path = [] + muchSmallerThanRadius = 0.1 * radius + segments = getInteriorSegments(loopLayer.loops, segments) + for segment in segments: + pointBegin = segment[0].point + if not isCloseToLast(edgePaths, pointBegin, muchSmallerThanRadius): + path = [pointBegin] + edgePaths.append(path) + path.append(segment[1].point) + if len(edgePaths) > 1: + firstPath = edgePaths[0] + lastPath = edgePaths[-1] + if abs(lastPath[-1] - firstPath[0]) < 0.1 * muchSmallerThanRadius: + connectedBeginning = lastPath[: -1] + firstPath + edgePaths[0] = connectedBeginning + edgePaths.remove(lastPath) + muchGreaterThanRadius = 6.0 * radius + for edgePath in edgePaths: + if euclidean.getPathLength(edgePath) > muchGreaterThanRadius: + self.distanceFeedRate.addGcodeFromThreadZ(edgePath, loopLayer.z) + + def addGcodeFromRemainingLoop(self, loop, loopLayer, loopLists, radius): + "Add the remainder of the loop which does not overlap the alreadyFilledArounds loops." + centerOutset = intercircle.getLargestCenterOutsetLoopFromLoopRegardless(loop, radius) + euclidean.addNestedRingBeginning(self.distanceFeedRate, centerOutset.outset, loopLayer.z) + self.addGcodePerimeterBlockFromRemainingLoop(centerOutset.center, loopLayer, loopLists, radius) + self.distanceFeedRate.addLine('()') + self.distanceFeedRate.addLine('()') + + def addGcodePerimeterBlockFromRemainingLoop(self, loop, loopLayer, loopLists, radius): + "Add the perimter block remainder of the loop which does not overlap the alreadyFilledArounds loops." + if self.repository.overlapRemovalWidthOverEdgeWidth.value < 0.2: + self.distanceFeedRate.addPerimeterBlock(loop, loopLayer.z) + return + isIntersectingSelf = isIntersectingItself(loop, self.overlapRemovalWidth) + if isIntersectingWithinLists(loop, loopLists) or isIntersectingSelf: + self.addGcodeFromPerimeterPaths(isIntersectingSelf, loop, loopLayer, loopLists, radius) + else: + self.distanceFeedRate.addPerimeterBlock(loop, loopLayer.z) + addAlreadyFilledArounds(loopLists, loop, self.overlapRemovalWidth) + + def addInitializationToOutput(self): + "Add initialization gcode to the output." + if self.repository.addCustomCodeForTemperatureReading.value: + self.distanceFeedRate.addLine('M105') # Custom code for temperature reading. + + def addInset(self, loopLayer): + "Add inset to the layer." + alreadyFilledArounds = [] + extrudateLoops = intercircle.getInsetLoopsFromLoops(loopLayer.loops, self.halfEdgeWidth) + if self.repository.infillInDirectionOfBridge.value: + bridgeRotation = getBridgeDirection(self.belowLoops, extrudateLoops, self.halfEdgeWidth) + if bridgeRotation != None: + self.distanceFeedRate.addTagBracketedLine('bridgeRotation', bridgeRotation) + self.belowLoops = loopLayer.loops + triangle_mesh.sortLoopsInOrderOfArea(not self.repository.loopOrderAscendingArea.value, extrudateLoops) + for extrudateLoop in extrudateLoops: + self.addGcodeFromRemainingLoop(extrudateLoop, loopLayer, alreadyFilledArounds, self.halfEdgeWidth) + + def getCraftedGcode(self, gcodeText, repository): + "Parse gcode text and store the bevel gcode." + self.repository = repository + self.lines = archive.getTextLines(gcodeText) + self.parseInitialization() + for line in self.lines[self.lineIndex :]: + self.parseLine(line) + return self.distanceFeedRate.output.getvalue() + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '(': + self.addInitializationToOutput() + elif firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('inset') + return + elif firstWord == '(': + layerHeight = float(splitLine[1]) + self.infillWidth = self.repository.infillWidthOverThickness.value * layerHeight + self.distanceFeedRate.addTagRoundedLine('infillWidth', self.infillWidth) + elif firstWord == '(': + self.edgeWidth = float(splitLine[1]) + self.halfEdgeWidth = 0.5 * self.edgeWidth + self.overlapRemovalWidth = self.edgeWidth * self.repository.overlapRemovalWidthOverEdgeWidth.value + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + "Parse a gcode line and add it to the inset skein." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == '(': + location = gcodec.getLocationFromSplitLine(None, splitLine) + self.boundary.append(location.dropAxis()) + elif firstWord == '()': + self.distanceFeedRate.addLine(line) + if self.repository.turnExtruderHeaterOffAtShutDown.value: + self.distanceFeedRate.addLine('M104 S0') # Turn extruder heater off. + return + elif firstWord == '(': + self.layerCount.printProgressIncrement('inset') + self.loopLayer = euclidean.LoopLayer(float(splitLine[1])) + self.distanceFeedRate.addLine(line) + elif firstWord == '()': + self.addInset(self.loopLayer) + self.loopLayer = None + elif firstWord == '()': + self.boundary = [] + self.loopLayer.loops.append(self.boundary) + if self.loopLayer == None: + self.distanceFeedRate.addLine(line) + + +def main(): + "Display the inset dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/jitter.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/jitter.py new file mode 100644 index 0000000..aef5bca --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/jitter.py @@ -0,0 +1,256 @@ +""" +This page is in the table of contents. +This craft tool jitters the loop end position to a different place on each layer to prevent a ridge from being created on the side of the object. + +The jitter manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Jitter + +==Operation== +The default 'Activate Jitter' checkbox is on. When it is on, the functions described below will work, when it is off, nothing will be done. + +==Settings== +===Jitter Over Perimeter Width=== +Default: 2 + +Defines the amount the loop ends will be jittered over the edge width. A high value means the loops will start all over the place and a low value means loops will start at roughly the same place on each layer. + +For example if you turn jitter off and print a cube every outside shell on the cube will start from exactly the same point so you will have a visible "mark/line/seam" on the side of the cube. Using the jitter tool you move that start point around hence you avoid that visible seam. + + +==Examples== +The following examples jitter the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and jitter.py. + +> python jitter.py +This brings up the jitter dialog. + +> python jitter.py Screw Holder Bottom.stl +The jitter tool is parsing the file: +Screw Holder Bottom.stl +.. +The jitter tool has created the file: +.. Screw Holder Bottom_jitter.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import sys + + +__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 getCraftedText( fileName, text, jitterRepository = None ): + 'Jitter a gcode linear move text.' + return getCraftedTextFromText( archive.getTextIfEmpty(fileName, text), jitterRepository ) + +def getCraftedTextFromText( gcodeText, jitterRepository = None ): + 'Jitter a gcode linear move text.' + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'jitter'): + return gcodeText + if jitterRepository == None: + jitterRepository = settings.getReadRepository( JitterRepository() ) + if not jitterRepository.activateJitter.value: + return gcodeText + return JitterSkein().getCraftedGcode( jitterRepository, gcodeText ) + +def getJitteredLoop( jitterDistance, jitterLoop ): + 'Get a jittered loop path.' + loopLength = euclidean.getLoopLength( jitterLoop ) + lastLength = 0.0 + pointIndex = 0 + totalLength = 0.0 + jitterPosition = ( jitterDistance + 256.0 * loopLength ) % loopLength + while totalLength < jitterPosition and pointIndex < len( jitterLoop ): + firstPoint = jitterLoop[pointIndex] + secondPoint = jitterLoop[ (pointIndex + 1) % len( jitterLoop ) ] + pointIndex += 1 + lastLength = totalLength + totalLength += abs(firstPoint - secondPoint) + remainingLength = jitterPosition - lastLength + pointIndex = pointIndex % len( jitterLoop ) + ultimateJitteredPoint = jitterLoop[pointIndex] + penultimateJitteredPointIndex = ( pointIndex + len( jitterLoop ) - 1 ) % len( jitterLoop ) + penultimateJitteredPoint = jitterLoop[ penultimateJitteredPointIndex ] + segment = ultimateJitteredPoint - penultimateJitteredPoint + segmentLength = abs(segment) + originalOffsetLoop = euclidean.getAroundLoop( pointIndex, pointIndex, jitterLoop ) + if segmentLength <= 0.0: + return originalOffsetLoop + newUltimatePoint = penultimateJitteredPoint + segment * remainingLength / segmentLength + return [newUltimatePoint] + originalOffsetLoop + +def getNewRepository(): + 'Get new repository.' + return JitterRepository() + +def isLoopNumberEqual( betweenX, betweenXIndex, loopNumber ): + 'Determine if the loop number is equal.' + if betweenXIndex >= len( betweenX ): + return False + return betweenX[ betweenXIndex ].index == loopNumber + +def writeOutput(fileName, shouldAnalyze=True): + 'Jitter a gcode linear move file.' + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'jitter', shouldAnalyze) + + +class JitterRepository: + 'A class to handle the jitter settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.jitter.html', self) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Jitter', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Jitter') + self.activateJitter = settings.BooleanSetting().getFromValue('Activate Jitter', self, False) + self.jitterOverEdgeWidth = settings.FloatSpin().getFromValue(1.0, 'Jitter Over Perimeter Width (ratio):', self, 3.0, 2.0) + self.executeTitle = 'Jitter' + + def execute(self): + 'Jitter button has been clicked.' + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class JitterSkein: + 'A class to jitter a skein of extrusions.' + def __init__(self): + 'Initialize.' + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.feedRateMinute = None + self.isLoopPerimeter = False + self.layerCount = settings.LayerCount() + self.layerGolden = 0.0 + self.lineIndex = 0 + self.lines = None + self.loopPath = None + self.oldLocation = None + self.operatingFeedRatePerMinute = None + self.travelFeedRateMinute = None + + def addGcodeFromThreadZ( self, thread, z ): + 'Add a gcode thread to the output.' + if len(thread) > 0: + self.addGcodeMovementZ( self.travelFeedRateMinute, thread[0], z ) + else: + print('zero length vertex positions array which was skipped over, this should never happen.') + if len(thread) < 2: + return + self.distanceFeedRate.addLine('M101') + self.addGcodePathZ( self.feedRateMinute, thread[1 :], z ) + + def addGcodeMovementZ(self, feedRateMinute, point, z): + 'Add a movement to the output.' + if feedRateMinute == None: + feedRateMinute = self.operatingFeedRatePerMinute + self.distanceFeedRate.addGcodeMovementZWithFeedRate(feedRateMinute, point, z) + + def addGcodePathZ( self, feedRateMinute, path, z ): + 'Add a gcode path, without modifying the extruder, to the output.' + for point in path: + self.addGcodeMovementZ(feedRateMinute, point, z) + + def addTailoredLoopPath(self): + 'Add a clipped and jittered loop path.' + loop = getJitteredLoop(self.layerJitter, self.loopPath.path[: -1]) + loop = euclidean.getAwayPoints(loop, 0.2 * self.edgeWidth) + self.addGcodeFromThreadZ(loop + [loop[0]], self.loopPath.z) + self.loopPath = None + + def getCraftedGcode(self, jitterRepository, gcodeText): + 'Parse gcode text and store the jitter gcode.' + if jitterRepository.jitterOverEdgeWidth.value == 0.0: + print('Warning, Jitter Over Perimeter Width is zero so thing will be done.') + return gcodeText + self.lines = archive.getTextLines(gcodeText) + self.parseInitialization(jitterRepository) + for self.lineIndex in xrange(self.lineIndex, len(self.lines)): + self.parseLine(self.lines[self.lineIndex]) + return self.distanceFeedRate.output.getvalue() + + def parseInitialization( self, jitterRepository ): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('jitter') + return + elif firstWord == '(': + self.operatingFeedRatePerMinute = 60.0 * float(splitLine[1]) + elif firstWord == '(': + self.edgeWidth = float(splitLine[1]) + self.jitter = jitterRepository.jitterOverEdgeWidth.value * self.edgeWidth + elif firstWord == '(': + self.travelFeedRateMinute = 60.0 * float(splitLine[1]) + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + 'Parse a gcode line, jitter it and add it to the jitter skein.' + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + self.setFeedRateLocationLoopPath(line, splitLine) + if self.loopPath != None: + self.loopPath.path.append(self.oldLocation.dropAxis()) + return + elif firstWord == 'M101': + if self.loopPath != None: + return + elif firstWord == 'M103': + self.isLoopPerimeter = False + if self.loopPath != None: + self.addTailoredLoopPath() + elif firstWord == '(': + self.layerCount.printProgressIncrement('jitter') + self.layerGolden = math.fmod(self.layerGolden + 0.61803398874989479, 1.0) + self.layerJitter = self.jitter * self.layerGolden - 0.5 + elif firstWord == '(' or firstWord == '(': + self.isLoopPerimeter = True + self.distanceFeedRate.addLine(line) + + def setFeedRateLocationLoopPath(self, line, splitLine): + 'Set the feedRateMinute, oldLocation and loopPath.' + self.feedRateMinute = gcodec.getFeedRateMinute(self.feedRateMinute, splitLine) + self.oldLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + if not self.isLoopPerimeter or self.loopPath != None: + return + for afterIndex in xrange(self.lineIndex + 1, len(self.lines)): + line = self.lines[afterIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'G1' or firstWord == 'M103': + return + elif firstWord == 'M101': + self.loopPath = euclidean.PathZ(self.oldLocation.z) + return + + +def main(): + 'Display the jitter dialog.' + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/lash.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/lash.py new file mode 100644 index 0000000..9843d43 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/lash.py @@ -0,0 +1,171 @@ +""" +This page is in the table of contents. +Lash is a script to partially compensate for the backlash of the tool head. + +The lash manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Lash + +The lash tool is ported from Erik de Bruijn's 3D-to-5D-Gcode php GPL'd script at: +http://objects.reprap.org/wiki/3D-to-5D-Gcode.php + +The default values are from the settings in Erik's 3D-to-5D-Gcode, I believe the settings are used on his Darwin reprap. + +==Operation== +The default 'Activate Lash' checkbox is off. When it is on, the functions described below will work, when it is off, nothing will be done. + +==Settings== +===X Backlash=== +Default is 0.2 millimeters. + +Defines the distance the tool head will be lashed in the X direction. + +===Y Backlash=== +Default is 0.2 millimeters. + +Defines the distance the tool head will be lashed in the Y direction. + +==Examples== +The following examples lash the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and lash.py. + +> python lash.py +This brings up the lash dialog. + +> python lash.py Screw Holder Bottom.stl +The lash tool is parsing the file: +Screw Holder Bottom.stl +.. +The lash tool has created the file: +.. Screw Holder Bottom_lash.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import archive +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import sys + + +__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 getCraftedText( fileName, text, lashRepository = None ): + "Get a lashed gcode linear move text." + return getCraftedTextFromText( archive.getTextIfEmpty(fileName, text), lashRepository ) + +def getCraftedTextFromText( gcodeText, lashRepository = None ): + "Get a lashed gcode linear move text from text." + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'lash'): + return gcodeText + if lashRepository == None: + lashRepository = settings.getReadRepository( LashRepository() ) + if not lashRepository.activateLash.value: + return gcodeText + return LashSkein().getCraftedGcode( gcodeText, lashRepository ) + +def getNewRepository(): + 'Get new repository.' + return LashRepository() + +def writeOutput(fileName, shouldAnalyze=True): + "Lash a gcode linear move file." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'lash', shouldAnalyze) + + +class LashRepository: + "A class to handle the lash settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.lash.html', self) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Lash', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Lash') + self.activateLash = settings.BooleanSetting().getFromValue('Activate Lash', self, False ) + self.xBacklash = settings.FloatSpin().getFromValue( 0.1, 'X Backlash (mm):', self, 0.5, 0.2 ) + self.yBacklash = settings.FloatSpin().getFromValue( 0.1, 'Y Backlash (mm):', self, 0.5, 0.3 ) + self.executeTitle = 'Lash' + + def execute(self): + "Lash button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class LashSkein: + "A class to lash a skein of extrusions." + def __init__(self): + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.feedRateMinute = 958.0 + self.lineIndex = 0 + self.lines = None + self.oldLocation = None + + def getCraftedGcode( self, gcodeText, lashRepository ): + "Parse gcode text and store the lash gcode." + self.lines = archive.getTextLines(gcodeText) + self.lashRepository = lashRepository + self.xBacklash = lashRepository.xBacklash.value + self.yBacklash = lashRepository.yBacklash.value + self.parseInitialization() + for self.lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[self.lineIndex] + self.parseLash(line) + return self.distanceFeedRate.output.getvalue() + + def getLashedLine( self, line, location, splitLine ): + "Get lashed gcode line." + if self.oldLocation == None: + return line + if location.x > self.oldLocation.x: + line = self.distanceFeedRate.getLineWithX( line, splitLine, location.x + self.xBacklash ) + else: + line = self.distanceFeedRate.getLineWithX( line, splitLine, location.x - self.xBacklash ) + if location.y > self.oldLocation.y: + line = self.distanceFeedRate.getLineWithY( line, splitLine, location.y + self.yBacklash ) + else: + line = self.distanceFeedRate.getLineWithY( line, splitLine, location.y - self.yBacklash ) + return line + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('lash') + return + self.distanceFeedRate.addLine(line) + + def parseLash(self, line): + "Parse a gcode line and add it to the lash skein." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + line = self.getLashedLine( line, location, splitLine ) + self.oldLocation = location + self.distanceFeedRate.addLine(line) + + +def main(): + "Display the lash dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/lift.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/lift.py new file mode 100644 index 0000000..7e19329 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/lift.py @@ -0,0 +1,195 @@ +""" +This page is in the table of contents. +Lift will change the altitude of the cutting tool when it is on so that it will cut through the slab at the correct altitude. It will also lift the gcode when the tool is off so that the cutting tool will clear the top of the slab. + +==Operation== +The default 'Activate Lift' checkbox is on. When it is on, the functions described below will work, when it is off, the functions will not be called. + +==Settings== +===Cutting Lift over Layer Step=== +Default is minus 0.5, because the end mill is the more common tool. + +Defines the ratio of the amount the cutting tool will be lifted over the layer step. If whittle is off the layer step will be the layer height, if it is on, it will be the layer step from the whittle gcode. If the cutting tool is like an end mill, where the cutting happens until the end of the tool, then the 'Cutting Lift over Layer Step' should be minus 0.5, so that the end mill cuts to the bottom of the slab. If the cutting tool is like a laser, where the cutting happens around the focal point. the 'Cutting Lift over Layer Step' should be zero, so that the cutting action will be focused in the middle of the slab. + +===Clearance above Top=== +Default is 5 millimeters. + +Defines the distance above the top of the slab the cutting tool will be lifted when will tool is off so that the cutting tool will clear the top of the slab. + +==Examples== +The following examples lift the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and lift.py. + +> python lift.py +This brings up the lift dialog. + +> python lift.py Screw Holder Bottom.stl +The lift tool is parsing the file: +Screw Holder Bottom.stl +.. +The lift tool has created the file: +.. Screw Holder Bottom_lift.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import archive +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import sys + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCraftedText( fileName, text='', liftRepository = None ): + "Lift the preface file or text." + return getCraftedTextFromText( archive.getTextIfEmpty(fileName, text), liftRepository ) + +def getCraftedTextFromText( gcodeText, liftRepository = None ): + "Lift the preface gcode text." + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'lift'): + return gcodeText + if liftRepository == None: + liftRepository = settings.getReadRepository( LiftRepository() ) + if not liftRepository.activateLift.value: + return gcodeText + return LiftSkein().getCraftedGcode( liftRepository, gcodeText ) + +def getNewRepository(): + 'Get new repository.' + return LiftRepository() + +def writeOutput(fileName, shouldAnalyze=True): + "Lift the carving of a gcode file." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'lift', shouldAnalyze) + + +class LiftRepository: + "A class to handle the lift settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.lift.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File to be Lifted', self, '') + self.activateLift = settings.BooleanSetting().getFromValue('Activate Lift', self, True ) + self.cuttingLiftOverLayerStep = settings.FloatSpin().getFromValue( - 1.0, 'Cutting Lift over Layer Step (ratio):', self, 1.0, - 0.5 ) + self.clearanceAboveTop = settings.FloatSpin().getFromValue( 0.0, 'Clearance above Top (mm):', self, 10.0, 5.0 ) + self.executeTitle = 'Lift' + + def execute(self): + "Lift button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class LiftSkein: + "A class to lift a skein of extrusions." + def __init__(self): + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.extruderActive = False + self.layerStep = None + self.layerHeight = 0.3333333333 + self.lineIndex = 0 + self.maximumZ = - 912345678.0 + self.oldLocation = None + self.previousActiveMovementLine = None + self.previousInactiveMovementLine = None + + def addPreviousInactiveMovementLineIfNecessary(self): + "Add the previous inactive movement line if necessary." + if self.previousInactiveMovementLine != None: + self.distanceFeedRate.addLine( self.previousInactiveMovementLine ) + self.previousInactiveMovementLine = None + + def getCraftedGcode( self, liftRepository, gcodeText ): + "Parse gcode text and store the lift gcode." + self.liftRepository = liftRepository + self.lines = archive.getTextLines(gcodeText) + self.parseInitialization() + self.oldLocation = None + if self.layerStep == None: + self.layerStep = self.layerHeight + self.cuttingLift = self.layerStep * liftRepository.cuttingLiftOverLayerStep.value + self.setMaximumZ() + self.travelZ = self.maximumZ + 0.5 * self.layerStep + liftRepository.clearanceAboveTop.value + for line in self.lines[self.lineIndex :]: + self.parseLine(line) + return self.distanceFeedRate.output.getvalue() + + def getLinearMove( self, line, location, splitLine ): + "Get the linear move." + if self.extruderActive: + z = location.z + self.cuttingLift + return self.distanceFeedRate.getLineWithZ( line, splitLine, z ) + if self.previousActiveMovementLine != None: + previousActiveMovementLineSplit = self.previousActiveMovementLine.split() + self.distanceFeedRate.addLine( self.distanceFeedRate.getLineWithZ( self.previousActiveMovementLine, previousActiveMovementLineSplit, self.travelZ ) ) + self.previousActiveMovementLine = None + self.distanceFeedRate.addLine( self.distanceFeedRate.getLineWithZ( line, splitLine, self.travelZ ) ) + self.previousInactiveMovementLine = line + return '' + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex].lstrip() + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('lift') + return + elif firstWord == '(': + self.layerHeight = float(splitLine[1]) + elif firstWord == '(': + self.layerStep = float(splitLine[1]) + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + "Parse a gcode line and add it to the lift skein." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + line = self.getLinearMove( line, location, splitLine ) + self.previousActiveMovementLine = line + self.oldLocation = location + elif firstWord == 'M101': + self.addPreviousInactiveMovementLineIfNecessary() + self.extruderActive = True + elif firstWord == 'M103': + self.extruderActive = False + self.distanceFeedRate.addLine(line) + + def setMaximumZ(self): + "Set maximum z." + localOldLocation = None + for line in self.lines[self.lineIndex :]: + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'G1': + location = gcodec.getLocationFromSplitLine( localOldLocation, splitLine ) + self.maximumZ = max( self.maximumZ, location.z ) + localOldLocation = location + + +def main(): + "Display the lift dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/limit.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/limit.py new file mode 100644 index 0000000..48786e6 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/limit.py @@ -0,0 +1,201 @@ +#! /usr/bin/env python +""" +This page is in the table of contents. +This plugin limits the feed rate of the tool head, so that the stepper motors are not driven too fast and skip steps. + +The limit manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Limit + +The maximum z feed rate is defined in speed. + +==Operation== +The default 'Activate Limit' checkbox is on. When it is on, the functions described below will work, when it is off, nothing will be done. + +==Settings== +===Maximum Initial Feed Rate=== +Default is one millimeter per second. + +Defines the maximum speed of the inital tool head move. + +==Examples== +The following examples limit the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and limit.py. + +> python limit.py +This brings up the limit dialog. + +> python limit.py Screw Holder Bottom.stl +The limit tool is parsing the file: +Screw Holder Bottom.stl +.. +The limit tool has created the file: +.. Screw Holder Bottom_limit.gcode + +""" + +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from datetime import date +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import os +import sys + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/28/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCraftedText(fileName, gcodeText='', repository=None): + 'Limit a gcode file or text.' + return getCraftedTextFromText( archive.getTextIfEmpty(fileName, gcodeText), repository ) + +def getCraftedTextFromText(gcodeText, repository=None): + 'Limit a gcode text.' + if gcodec.isProcedureDoneOrFileIsEmpty(gcodeText, 'limit'): + return gcodeText + if repository == None: + repository = settings.getReadRepository(LimitRepository()) + if not repository.activateLimit.value: + return gcodeText + return LimitSkein().getCraftedGcode(gcodeText, repository) + +def getNewRepository(): + 'Get new repository.' + return LimitRepository() + +def writeOutput(fileName, shouldAnalyze=True): + 'Limit a gcode file.' + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'limit', shouldAnalyze) + + +class LimitRepository: + 'A class to handle the limit settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.limit.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Limit', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Limit') + self.activateLimit = settings.BooleanSetting().getFromValue('Activate Limit', self, False) + self.maximumInitialFeedRate = settings.FloatSpin().getFromValue(0.5, 'Maximum Initial Feed Rate (mm/s):', self, 10.0, 1.0) + self.executeTitle = 'Limit' + + def execute(self): + 'Limit button has been clicked.' + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class LimitSkein: + 'A class to limit a skein of extrusions.' + def __init__(self): + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.feedRateMinute = None + self.lineIndex = 0 + self.maximumZDrillFeedRatePerSecond = 987654321.0 + self.oldLocation = None + + def getCraftedGcode(self, gcodeText, repository): + 'Parse gcode text and store the limit gcode.' + self.repository = repository + self.lines = archive.getTextLines(gcodeText) + self.parseInitialization() + self.maximumZDrillFeedRatePerSecond = min(self.maximumZDrillFeedRatePerSecond, self.maximumZFeedRatePerSecond) + self.maximumZCurrentFeedRatePerSecond = self.maximumZFeedRatePerSecond + for lineIndex in xrange(self.lineIndex, len(self.lines)): + self.parseLine( lineIndex ) + return self.distanceFeedRate.output.getvalue() + + def getLimitedInitialMovement(self, line, splitLine): + 'Get a limited linear movement.' + if self.oldLocation == None: + line = self.distanceFeedRate.getLineWithFeedRate(60.0 * self.repository.maximumInitialFeedRate.value, line, splitLine) + return line + + def getZLimitedLine(self, deltaZ, distance, line, splitLine): + 'Get a replaced z limited gcode movement line.' + zFeedRateSecond = self.feedRateMinute * deltaZ / distance / 60.0 + if zFeedRateSecond <= self.maximumZCurrentFeedRatePerSecond: + return line + limitedFeedRateMinute = self.feedRateMinute * self.maximumZCurrentFeedRatePerSecond / zFeedRateSecond + return self.distanceFeedRate.getLineWithFeedRate(limitedFeedRateMinute, line, splitLine) + + def getZLimitedLineArc(self, line, splitLine): + 'Get a replaced z limited gcode arc movement line.' + self.feedRateMinute = gcodec.getFeedRateMinute(self.feedRateMinute, splitLine) + if self.feedRateMinute == None or self.oldLocation == None: + return line + relativeLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + self.oldLocation += relativeLocation + deltaZ = abs(relativeLocation.z) + distance = gcodec.getArcDistance(relativeLocation, splitLine) + return self.getZLimitedLine(deltaZ, distance, line, splitLine) + + def getZLimitedLineLinear(self, line, location, splitLine): + 'Get a replaced z limited gcode linear movement line.' + self.feedRateMinute = gcodec.getFeedRateMinute(self.feedRateMinute, splitLine) + if location == self.oldLocation: + return '' + if self.feedRateMinute == None or self.oldLocation == None: + return line + deltaZ = abs(location.z - self.oldLocation.z) + distance = abs(location - self.oldLocation) + return self.getZLimitedLine(deltaZ, distance, line, splitLine) + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('limit') + return + elif firstWord == '(': + self.maximumZDrillFeedRatePerSecond = float(splitLine[1]) + elif firstWord == '(': + self.maximumZFeedRatePerSecond = float(splitLine[1]) + self.distanceFeedRate.addLine(line) + + def parseLine( self, lineIndex ): + 'Parse a gcode line and add it to the limit skein.' + line = self.lines[lineIndex].lstrip() + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'G1': + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + line = self.getLimitedInitialMovement(line, splitLine) + line = self.getZLimitedLineLinear(line, location, splitLine) + self.oldLocation = location + elif firstWord == 'G2' or firstWord == 'G3': + line = self.getZLimitedLineArc(line, splitLine) + elif firstWord == 'M101': + self.maximumZCurrentFeedRatePerSecond = self.maximumZDrillFeedRatePerSecond + elif firstWord == 'M103': + self.maximumZCurrentFeedRatePerSecond = self.maximumZFeedRatePerSecond + self.distanceFeedRate.addLine(line) + + +def main(): + 'Display the limit dialog.' + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/mill.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/mill.py new file mode 100644 index 0000000..90248e5 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/mill.py @@ -0,0 +1,379 @@ +""" +This page is in the table of contents. +Mill is a script to mill the outlines. + +==Operation== +The default 'Activate Mill' checkbox is on. When it is on, the functions described below will work, when it is off, the functions will not be called. + +==Settings== +===Add Loops=== +====Add Inner Loops==== +Default is on. + +When selected, the inner milling loops will be added. + +====Add Outer Loops==== +Default is on. + +When selected, the outer milling loops will be added. + +===Cross Hatch=== +Default is on. + +When selected, there will be alternating horizontal and vertical milling paths, if it is off there will only be horizontal milling paths. + +===Loop Outset=== +====Loop Inner Outset over Perimeter Width==== +Default is 0.5. + +Defines the ratio of the amount the inner milling loop will be outset over the edge width. + +====Loop Outer Outset over Perimeter Width==== +Default is one. + +Defines the ratio of the amount the outer milling loop will be outset over the edge width. The 'Loop Outer Outset over Perimeter Width' ratio should be greater than the 'Loop Inner Outset over Perimeter Width' ratio. + +===Mill Width over Perimeter Width=== +Default is one. + +Defines the ratio of the mill line width over the edge width. If the ratio is one, all the material will be milled. The greater the 'Mill Width over Perimeter Width' the farther apart the mill lines will be and so less of the material will be directly milled, the remaining material might still be removed in chips if the ratio is not much greater than one. + +==Examples== +The following examples mill the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and mill.py. + +> python mill.py +This brings up the mill dialog. + +> python mill.py Screw Holder Bottom.stl +The mill tool is parsing the file: +Screw Holder Bottom.stl +.. +The mill tool has created the file: +Screw Holder Bottom_mill.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import os +import sys + + +__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 getCraftedText( fileName, gcodeText = '', repository=None): + 'Mill the file or gcodeText.' + return getCraftedTextFromText( archive.getTextIfEmpty(fileName, gcodeText), repository ) + +def getCraftedTextFromText(gcodeText, repository=None): + 'Mill a gcode linear move gcodeText.' + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'mill'): + return gcodeText + if repository == None: + repository = settings.getReadRepository( MillRepository() ) + if not repository.activateMill.value: + return gcodeText + return MillSkein().getCraftedGcode(gcodeText, repository) + +def getNewRepository(): + 'Get new repository.' + return MillRepository() + +def getPointsFromSegmentTable(segmentTable): + 'Get the points from the segment table.' + points = [] + segmentTableKeys = segmentTable.keys() + segmentTableKeys.sort() + for segmentTableKey in segmentTableKeys: + for segment in segmentTable[segmentTableKey]: + for endpoint in segment: + points.append(endpoint.point) + return points + +def isPointOfTableInLoop( loop, pointTable ): + 'Determine if a point in the point table is in the loop.' + for point in loop: + if point in pointTable: + return True + return False + +def writeOutput(fileName, shouldAnalyze=True): + 'Mill a gcode linear move file.' + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'mill', shouldAnalyze) + + +class Average: + 'A class to hold values and get the average.' + def __init__(self): + self.reset() + + def addValue( self, value ): + 'Add a value to the total and the number of values.' + self.numberOfValues += 1 + self.total += value + + def getAverage(self): + 'Get the average.' + if self.numberOfValues == 0: + print('should never happen, self.numberOfValues in Average is zero') + return 0.0 + return self.total / float( self.numberOfValues ) + + def reset(self): + 'Set the number of values and the total to the default.' + self.numberOfValues = 0 + self.total = 0.0 + + +class MillRepository: + 'A class to handle the mill settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.mill.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Mill', self, '') + self.activateMill = settings.BooleanSetting().getFromValue('Activate Mill', self, True ) + settings.LabelDisplay().getFromName('- Add Loops -', self ) + self.addInnerLoops = settings.BooleanSetting().getFromValue('Add Inner Loops', self, True ) + self.addOuterLoops = settings.BooleanSetting().getFromValue('Add Outer Loops', self, True ) + self.crossHatch = settings.BooleanSetting().getFromValue('Cross Hatch', self, True ) + settings.LabelDisplay().getFromName('- Loop Outset -', self ) + self.loopInnerOutsetOverEdgeWidth = settings.FloatSpin().getFromValue( 0.3, 'Loop Inner Outset over Perimeter Width (ratio):', self, 0.7, 0.5 ) + self.loopOuterOutsetOverEdgeWidth = settings.FloatSpin().getFromValue( 0.8, 'Loop Outer Outset over Perimeter Width (ratio):', self, 1.4, 1.0 ) + self.millWidthOverEdgeWidth = settings.FloatSpin().getFromValue( 0.8, 'Mill Width over Edge Width (ratio):', self, 1.8, 1.0 ) + self.executeTitle = 'Mill' + + def execute(self): + 'Mill button has been clicked.' + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + + +class MillSkein: + 'A class to mill a skein of extrusions.' + def __init__(self): + self.aroundPixelTable = {} + self.average = Average() + self.boundaryLayers = [] + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.edgeWidth = 0.6 + self.isExtruderActive = False + self.layerIndex = 0 + self.lineIndex = 0 + self.lines = None + self.oldLocation = None + + def addGcodeFromLoops(self, loops, z): + 'Add gcode from loops.' + if self.oldLocation == None: + self.oldLocation = Vector3() + self.oldLocation.z = z + for loop in loops: + self.distanceFeedRate.addGcodeFromThreadZ(loop, z) + euclidean.addToThreadsFromLoop(self.halfEdgeWidth, 'loop', loop, self.oldLocation, self) + + def addGcodeFromThreadZ( self, thread, z ): + 'Add a thread to the output.' + self.distanceFeedRate.addGcodeFromThreadZ( thread, z ) + + def addMillThreads(self): + 'Add the mill threads to the skein.' + boundaryLayer = self.boundaryLayers[self.layerIndex] + endpoints = euclidean.getEndpointsFromSegmentTable( boundaryLayer.segmentTable ) + if len(endpoints) < 1: + return + paths = euclidean.getPathsFromEndpoints(endpoints, 5.0 * self.millWidth, self.aroundPixelTable, self.aroundWidth) + averageZ = self.average.getAverage() + if self.repository.addInnerLoops.value: + self.addGcodeFromLoops( boundaryLayer.innerLoops, averageZ ) + if self.repository.addOuterLoops.value: + self.addGcodeFromLoops( boundaryLayer.outerLoops, averageZ ) + for path in paths: + simplifiedPath = euclidean.getSimplifiedPath( path, self.millWidth ) + self.distanceFeedRate.addGcodeFromThreadZ( simplifiedPath, averageZ ) + + def addSegmentTableLoops( self, boundaryLayerIndex ): + 'Add the segment tables and loops to the boundary.' + boundaryLayer = self.boundaryLayers[boundaryLayerIndex] + euclidean.subtractXIntersectionsTable(boundaryLayer.outerHorizontalTable, boundaryLayer.innerHorizontalTable) + euclidean.subtractXIntersectionsTable(boundaryLayer.outerVerticalTable, boundaryLayer.innerVerticalTable) + boundaryLayer.horizontalSegmentTable = self.getHorizontalSegmentTableForXIntersectionsTable( + boundaryLayer.outerHorizontalTable) + boundaryLayer.verticalSegmentTable = self.getVerticalSegmentTableForXIntersectionsTable( + boundaryLayer.outerVerticalTable) + betweenPoints = getPointsFromSegmentTable(boundaryLayer.horizontalSegmentTable) + betweenPoints += getPointsFromSegmentTable(boundaryLayer.verticalSegmentTable) + innerPoints = euclidean.getPointsByHorizontalDictionary(self.millWidth, boundaryLayer.innerHorizontalTable) + innerPoints += euclidean.getPointsByVerticalDictionary(self.millWidth, boundaryLayer.innerVerticalTable) + innerPointTable = {} + for innerPoint in innerPoints: + innerPointTable[innerPoint] = None + boundaryLayer.innerLoops = [] + boundaryLayer.outerLoops = [] + millRadius = 0.75 * self.millWidth + loops = triangle_mesh.getDescendingAreaOrientedLoops(betweenPoints, betweenPoints, millRadius) + for loop in loops: + if isPointOfTableInLoop(loop, innerPointTable): + boundaryLayer.innerLoops.append(loop) + else: + boundaryLayer.outerLoops.append(loop) + if self.repository.crossHatch.value and boundaryLayerIndex % 2 == 1: + boundaryLayer.segmentTable = boundaryLayer.verticalSegmentTable + else: + boundaryLayer.segmentTable = boundaryLayer.horizontalSegmentTable + + def getCraftedGcode(self, gcodeText, repository): + 'Parse gcode text and store the mill gcode.' + self.repository = repository + self.lines = archive.getTextLines(gcodeText) + self.parseInitialization() + self.parseBoundaries() + for line in self.lines[self.lineIndex :]: + self.parseLine(line) + return self.distanceFeedRate.output.getvalue() + + def getHorizontalSegmentTableForXIntersectionsTable( self, xIntersectionsTable ): + 'Get the horizontal segment table from the xIntersectionsTable.' + horizontalSegmentTable = {} + xIntersectionsTableKeys = xIntersectionsTable.keys() + xIntersectionsTableKeys.sort() + for xIntersectionsTableKey in xIntersectionsTableKeys: + xIntersections = xIntersectionsTable[ xIntersectionsTableKey ] + segments = euclidean.getSegmentsFromXIntersections( xIntersections, xIntersectionsTableKey * self.millWidth ) + horizontalSegmentTable[ xIntersectionsTableKey ] = segments + return horizontalSegmentTable + + def getHorizontalXIntersectionsTable(self, loops): + 'Get the horizontal x intersections table from the loops.' + horizontalXIntersectionsTable = {} + euclidean.addXIntersectionsFromLoopsForTable(loops, horizontalXIntersectionsTable, self.millWidth) + return horizontalXIntersectionsTable + + def getVerticalSegmentTableForXIntersectionsTable( self, xIntersectionsTable ): + 'Get the vertical segment table from the xIntersectionsTable which has the x and y swapped.' + verticalSegmentTable = {} + xIntersectionsTableKeys = xIntersectionsTable.keys() + xIntersectionsTableKeys.sort() + for xIntersectionsTableKey in xIntersectionsTableKeys: + xIntersections = xIntersectionsTable[ xIntersectionsTableKey ] + segments = euclidean.getSegmentsFromXIntersections( xIntersections, xIntersectionsTableKey * self.millWidth ) + for segment in segments: + for endpoint in segment: + endpoint.point = complex( endpoint.point.imag, endpoint.point.real ) + verticalSegmentTable[ xIntersectionsTableKey ] = segments + return verticalSegmentTable + + def parseBoundaries(self): + 'Parse the boundaries and add them to the boundary layers.' + boundaryLoop = None + boundaryLayer = None + for line in self.lines[self.lineIndex :]: + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == '()': + boundaryLoop = None + elif firstWord == '(': + location = gcodec.getLocationFromSplitLine(None, splitLine) + if boundaryLoop == None: + boundaryLoop = [] + boundaryLayer.loops.append(boundaryLoop) + boundaryLoop.append(location.dropAxis()) + elif firstWord == '(': + boundaryLayer = euclidean.LoopLayer(float(splitLine[1])) + self.boundaryLayers.append(boundaryLayer) + if len(self.boundaryLayers) < 2: + return + for boundaryLayer in self.boundaryLayers: + boundaryLayer.innerOutsetLoops = intercircle.getInsetSeparateLoopsFromLoops(boundaryLayer.loops, -self.loopInnerOutset) + boundaryLayer.outerOutsetLoops = intercircle.getInsetSeparateLoopsFromLoops(boundaryLayer.loops, -self.loopOuterOutset) + boundaryLayer.innerHorizontalTable = self.getHorizontalXIntersectionsTable( boundaryLayer.innerOutsetLoops ) + boundaryLayer.outerHorizontalTable = self.getHorizontalXIntersectionsTable( boundaryLayer.outerOutsetLoops ) + boundaryLayer.innerVerticalTable = self.getHorizontalXIntersectionsTable( euclidean.getDiagonalFlippedLoops( boundaryLayer.innerOutsetLoops ) ) + boundaryLayer.outerVerticalTable = self.getHorizontalXIntersectionsTable( euclidean.getDiagonalFlippedLoops( boundaryLayer.outerOutsetLoops ) ) + for boundaryLayerIndex in xrange( len(self.boundaryLayers) - 2, - 1, - 1 ): + boundaryLayer = self.boundaryLayers[ boundaryLayerIndex ] + boundaryLayerBelow = self.boundaryLayers[ boundaryLayerIndex + 1 ] + euclidean.joinXIntersectionsTables( boundaryLayerBelow.outerHorizontalTable, boundaryLayer.outerHorizontalTable ) + euclidean.joinXIntersectionsTables( boundaryLayerBelow.outerVerticalTable, boundaryLayer.outerVerticalTable ) + for boundaryLayerIndex in xrange( 1, len(self.boundaryLayers) ): + boundaryLayer = self.boundaryLayers[ boundaryLayerIndex ] + boundaryLayerAbove = self.boundaryLayers[ boundaryLayerIndex - 1 ] + euclidean.joinXIntersectionsTables( boundaryLayerAbove.innerHorizontalTable, boundaryLayer.innerHorizontalTable ) + euclidean.joinXIntersectionsTables( boundaryLayerAbove.innerVerticalTable, boundaryLayer.innerVerticalTable ) + for boundaryLayerIndex in xrange( len(self.boundaryLayers) ): + self.addSegmentTableLoops(boundaryLayerIndex) + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('mill') + return + elif firstWord == '(': + self.edgeWidth = float(splitLine[1]) + self.aroundWidth = 0.1 * self.edgeWidth + self.halfEdgeWidth = 0.5 * self.edgeWidth + self.millWidth = self.edgeWidth * self.repository.millWidthOverEdgeWidth.value + self.loopInnerOutset = self.halfEdgeWidth + self.edgeWidth * self.repository.loopInnerOutsetOverEdgeWidth.value + self.loopOuterOutset = self.halfEdgeWidth + self.edgeWidth * self.repository.loopOuterOutsetOverEdgeWidth.value + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + 'Parse a gcode line and add it to the mill skein.' + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + if self.isExtruderActive: + self.average.addValue(location.z) + if self.oldLocation != None: + euclidean.addValueSegmentToPixelTable( self.oldLocation.dropAxis(), location.dropAxis(), self.aroundPixelTable, None, self.aroundWidth ) + self.oldLocation = location + elif firstWord == 'M101': + self.isExtruderActive = True + elif firstWord == 'M103': + self.isExtruderActive = False + elif firstWord == '(': + settings.printProgress(self.layerIndex, 'mill') + self.aroundPixelTable = {} + self.average.reset() + elif firstWord == '()': + if len(self.boundaryLayers) > self.layerIndex: + self.addMillThreads() + self.layerIndex += 1 + self.distanceFeedRate.addLine(line) + + +def main(): + 'Display the mill dialog.' + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/multiply.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/multiply.py new file mode 100644 index 0000000..3ae9461 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/multiply.py @@ -0,0 +1,285 @@ +""" +This page is in the table of contents. +The multiply plugin will take a single object and create an array of objects. It is used when you want to print single object multiple times in a single pass. + +You can also position any object using this plugin by setting the center X and center Y to the desired coordinates (0,0 for the center of the print_bed) and setting the number of rows and columns to 1 (effectively setting a 1x1 matrix - printing only a single object). + +The multiply manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Multiply + +Besides using the multiply tool, another way of printing many copies of the model is to duplicate the model in Art of Illusion, however many times you want, with the appropriate offsets. Then you can either use the Join Objects script in the scripts submenu to create a combined shape or you can export the whole scene as an xml file, which skeinforge can then slice. + +==Operation== +The default 'Activate Multiply' checkbox is on. When it is on, the functions described below will work, when it is off, nothing will be done. + +==Settings== +===Center=== +Default is the origin. + +The center of the shape will be moved to the "Center X" and "Center Y" coordinates. + +====Center X==== +====Center Y==== + +===Number of Cells=== +====Number of Columns==== +Default is one. + +Defines the number of columns in the array table. + +====Number of Rows==== +Default is one. + +Defines the number of rows in the table. + +===Reverse Sequence every Odd Layer=== +Default is off. + +When selected the build sequence will be reversed on every odd layer so that the tool will travel less. The problem is that the builds would be made with different amount of time to cool, so some would be too hot and some too cold, which is why the default is off. + +===Separation over Perimeter Width=== +Default is fifteen. + +Defines the ratio of separation between the shape copies over the edge width. + +==Examples== +The following examples multiply the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and multiply.py. + +> python multiply.py +This brings up the multiply dialog. + +> python multiply.py Screw Holder Bottom.stl +The multiply tool is parsing the file: +Screw Holder Bottom.stl +.. +The multiply tool has created the file: +.. Screw Holder Bottom_multiply.gcode + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import sys + + +__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 getCraftedText(fileName, text='', repository=None): + 'Multiply the fill file or text.' + return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository) + +def getCraftedTextFromText(gcodeText, repository=None): + 'Multiply the fill text.' + if gcodec.isProcedureDoneOrFileIsEmpty(gcodeText, 'multiply'): + return gcodeText + if repository == None: + repository = settings.getReadRepository(MultiplyRepository()) + if not repository.activateMultiply.value: + return gcodeText + return MultiplySkein().getCraftedGcode(gcodeText, repository) + +def getNewRepository(): + 'Get new repository.' + return MultiplyRepository() + +def writeOutput(fileName, shouldAnalyze=True): + 'Multiply a gcode linear move file.' + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'multiply', shouldAnalyze) + + +class MultiplyRepository: + 'A class to handle the multiply settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.multiply.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( + fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Multiply', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Multiply') + self.activateMultiply = settings.BooleanSetting().getFromValue('Activate Multiply', self, True) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Center -', self ) + self.centerX = settings.FloatSpin().getFromValue(-100.0, 'Center X (mm):', self, 100.0, 105.0) + self.centerY = settings.FloatSpin().getFromValue(-100.0, 'Center Y (mm):', self, 100.0, 105.0) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Number of Cells -', self) + self.numberOfColumns = settings.IntSpin().getFromValue(1, 'Number of Columns (integer):', self, 10, 1) + self.numberOfRows = settings.IntSpin().getFromValue(1, 'Number of Rows (integer):', self, 10, 1) + settings.LabelSeparator().getFromRepository(self) + self.reverseSequenceEveryOddLayer = settings.BooleanSetting().getFromValue('Reverse Sequence every Odd Layer', self, False) + self.separationOverEdgeWidth = settings.FloatSpin().getFromValue(5.0, 'Separation over Perimeter Width (ratio):', self, 25.0, 15.0) + self.executeTitle = 'Multiply' + + def execute(self): + 'Multiply button has been clicked.' + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode( + self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class MultiplySkein: + 'A class to multiply a skein of extrusions.' + def __init__(self): + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.isExtrusionActive = False + self.layerIndex = 0 + self.layerLines = [] + self.lineIndex = 0 + self.lines = None + self.oldLocation = None + self.rowIndex = 0 + self.shouldAccumulate = True + + def addElement(self, offset): + 'Add moved element to the output.' + for line in self.layerLines: + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == '(': + movedLocation = self.getMovedLocationSetOldLocation(offset, splitLine) + line = self.distanceFeedRate.getBoundaryLine(movedLocation) + elif firstWord == 'G1': + movedLocation = self.getMovedLocationSetOldLocation(offset, splitLine) + line = self.distanceFeedRate.getLinearGcodeMovement(movedLocation.dropAxis(), movedLocation.z) + elif firstWord == '(': + movedLocation = self.getMovedLocationSetOldLocation(offset, splitLine) + line = self.distanceFeedRate.getInfillBoundaryLine(movedLocation) + self.distanceFeedRate.addLine(line) + + def addLayer(self): + 'Add multiplied layer to the output.' + self.addRemoveThroughLayer() + offset = self.centerOffset - self.arrayCenter - self.shapeCenter + for rowIndex in xrange(self.repository.numberOfRows.value): + yRowOffset = float(rowIndex) * self.extentPlusSeparation.imag + if self.layerIndex % 2 == 1 and self.repository.reverseSequenceEveryOddLayer.value: + yRowOffset = self.arrayExtent.imag - yRowOffset + for columnIndex in xrange(self.repository.numberOfColumns.value): + xColumnOffset = float(columnIndex) * self.extentPlusSeparation.real + if self.rowIndex % 2 == 1: + xColumnOffset = self.arrayExtent.real - xColumnOffset + elementOffset = complex(offset.real + xColumnOffset, offset.imag + yRowOffset) + self.addElement(elementOffset) + self.rowIndex += 1 + settings.printProgress(self.layerIndex, 'multiply') + if len(self.layerLines) > 1: + self.layerIndex += 1 + self.layerLines = [] + + def addRemoveThroughLayer(self): + 'Parse gcode initialization and store the parameters.' + for layerLineIndex in xrange(len(self.layerLines)): + line = self.layerLines[layerLineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.addLine(line) + if firstWord == '(': + self.layerLines = self.layerLines[layerLineIndex + 1 :] + return + + def getCraftedGcode(self, gcodeText, repository): + 'Parse gcode text and store the multiply gcode.' + self.centerOffset = complex(repository.centerX.value, repository.centerY.value) + self.repository = repository + self.numberOfColumns = repository.numberOfColumns.value + self.numberOfRows = repository.numberOfRows.value + self.lines = archive.getTextLines(gcodeText) + self.parseInitialization() + self.setCorners() + for line in self.lines[self.lineIndex :]: + self.parseLine(line) + return self.distanceFeedRate.output.getvalue() + + def getMovedLocationSetOldLocation(self, offset, splitLine): + 'Get the moved location and set the old location.' + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + self.oldLocation = location + return Vector3(location.x + offset.real, location.y + offset.imag, location.z) + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('multiply') + self.distanceFeedRate.addLine(line) + self.lineIndex += 1 + return + elif firstWord == '(': + self.absoluteEdgeWidth = abs(float(splitLine[1])) + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + 'Parse a gcode line and add it to the multiply skein.' + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == '()': + self.addLayer() + self.distanceFeedRate.addLine(line) + return + elif firstWord == '()': + self.shouldAccumulate = False + if self.shouldAccumulate: + self.layerLines.append(line) + return + self.distanceFeedRate.addLine(line) + + def setCorners(self): + 'Set maximum and minimum corners and z.' + cornerMaximumComplex = complex(-987654321.0, -987654321.0) + cornerMinimumComplex = -cornerMaximumComplex + for line in self.lines[self.lineIndex :]: + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'G1': + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + if self.isExtrusionActive: + locationComplex = location.dropAxis() + cornerMaximumComplex = euclidean.getMaximum(locationComplex, cornerMaximumComplex) + cornerMinimumComplex = euclidean.getMinimum(locationComplex, cornerMinimumComplex) + self.oldLocation = location + elif firstWord == 'M101': + self.isExtrusionActive = True + elif firstWord == 'M103': + self.isExtrusionActive = False + self.extent = cornerMaximumComplex - cornerMinimumComplex + self.shapeCenter = 0.5 * (cornerMaximumComplex + cornerMinimumComplex) + self.separation = self.repository.separationOverEdgeWidth.value * self.absoluteEdgeWidth + self.extentPlusSeparation = self.extent + complex(self.separation, self.separation) + columnsMinusOne = self.numberOfColumns - 1 + rowsMinusOne = self.numberOfRows - 1 + self.arrayExtent = complex(self.extentPlusSeparation.real * columnsMinusOne, self.extentPlusSeparation.imag * rowsMinusOne) + self.arrayCenter = 0.5 * self.arrayExtent + + +def main(): + 'Display the multiply dialog.' + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/oozebane.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/oozebane.py new file mode 100644 index 0000000..234b85f --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/oozebane.py @@ -0,0 +1,581 @@ +""" +This page is in the table of contents. +Oozebane is a script to turn off the extruder before the end of a thread and turn it on before the beginning. + +The oozebane manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Oozebane + +After oozebane turns the extruder on, it slows the feed rate down where the thread starts. Then it speeds it up in steps so in theory the thread will remain at roughly the same thickness from the beginning. + +==Operation== +The default 'Activate Oozebane' checkbox is on. When it is on, the functions described below will work, when it is off, the functions will not be called. + +==Settings== +===After Startup Distance=== +Default is 1.2. + +When oozebane reaches the point where the extruder would of turned on, it slows down so that the thread will be thick at that point. Afterwards it speeds the extruder back up to operating speed. The speed up distance is the "After Startup Distance". + +===Early Shutdown Distance=== +Default is 1.2. + +Defines the distance before the end of the thread that the extruder will be turned off. It is the most important oozebane setting. A higher distance means the extruder will turn off sooner and the end of the line will be thinner. + +===Early Startup Maximum Distance=== +Default is 1.2. + +Defines the maximum distance before the thread starts that the extruder will be turned on + +===Early Startup Distance Constant=== +Default is twenty. + +The longer the extruder has been off, the earlier the extruder will turn back on, the ratio is one minus one over e to the power of the distance the extruder has been off over the "Early Startup Distance Constant". + +===First Early Startup Distance=== +Default is twenty five. + +Defines the distance before the first thread starts that the extruder will be turned off. This value should be high because, according to Marius, the extruder takes a second or two to extrude when starting for the first time. + +===Minimum Distance for Early Shutdown=== +Default is zero. + +Defines the minimum distance that the extruder has to be off after the thread end for the early shutdown feature to activate. + +===Minimum Distance for Early Startup=== +Default is zero. + +Defines the minimum distance that the extruder has to be off before the thread begins for the early start up feature to activate. + +===Slowdown Startup Steps=== +Default is three. + +When oozebane turns the extruder off, it slows the feed rate down in steps so in theory the thread will remain at roughly the same thickness until the end. The "Slowdown Startup Steps" setting is the number of steps, the more steps the smaller the size of the step that the feed rate will be decreased and the larger the size of the resulting gcode file. + +==Examples== +The following examples oozebane the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and oozebane.py. + +> python oozebane.py +This brings up the oozebane dialog. + +> python oozebane.py Screw Holder Bottom.stl +The oozebane tool is parsing the file: +Screw Holder Bottom.stl +.. +The oozebane tool has created the file: +.. Screw Holder Bottom_oozebane.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import sys + + +__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 getCraftedText( fileName, text, oozebaneRepository = None ): + "Oozebane a gcode linear move file or text." + return getCraftedTextFromText( archive.getTextIfEmpty(fileName, text), oozebaneRepository ) + +def getCraftedTextFromText( gcodeText, oozebaneRepository = None ): + "Oozebane a gcode linear move text." + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'oozebane'): + return gcodeText + if oozebaneRepository == None: + oozebaneRepository = settings.getReadRepository( OozebaneRepository() ) + if not oozebaneRepository.activateOozebane.value: + return gcodeText + return OozebaneSkein().getCraftedGcode( gcodeText, oozebaneRepository ) + +def getNewRepository(): + 'Get new repository.' + return OozebaneRepository() + +def writeOutput(fileName, shouldAnalyze=True): + "Oozebane a gcode linear move file." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'oozebane', shouldAnalyze) + + +class OozebaneRepository: + "A class to handle the oozebane settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.oozebane.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Oozebane', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Oozebane') + self.activateOozebane = settings.BooleanSetting().getFromValue('Activate Oozebane', self, False ) + self.afterStartupDistance = settings.FloatSpin().getFromValue( 0.7, 'After Startup Distance (millimeters):', self, 1.7, 1.2 ) + self.earlyShutdownDistance = settings.FloatSpin().getFromValue( 0.7, 'Early Shutdown Distance (millimeters):', self, 1.7, 1.2 ) + self.earlyStartupDistanceConstant = settings.FloatSpin().getFromValue( 10.0, 'Early Startup Distance Constant (millimeters):', self, 30.0, 20.0 ) + self.earlyStartupMaximumDistance = settings.FloatSpin().getFromValue( 0.7, 'Early Startup Maximum Distance (millimeters):', self, 1.7, 1.2 ) + self.firstEarlyStartupDistance = settings.FloatSpin().getFromValue( 5.0, 'First Early Startup Distance (millimeters):', self, 45.0, 25.0 ) + self.minimumDistanceForEarlyStartup = settings.FloatSpin().getFromValue( 0.0, 'Minimum Distance for Early Startup (millimeters):', self, 10.0, 0.0 ) + self.minimumDistanceForEarlyShutdown = settings.FloatSpin().getFromValue( 0.0, 'Minimum Distance for Early Shutdown (millimeters):', self, 10.0, 0.0 ) + self.slowdownStartupSteps = settings.IntSpin().getFromValue( 2, 'Slowdown Startup Steps (positive integer):', self, 5, 3 ) + self.executeTitle = 'Oozebane' + + def execute(self): + "Oozebane button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class OozebaneSkein: + "A class to oozebane a skein of extrusions." + def __init__(self): + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.distanceFromThreadEndToThreadBeginning = None + self.earlyStartupDistance = None + self.extruderInactiveLongEnough = True + self.feedRateMinute = 961.0 + self.isExtruderActive = False + self.isFirstExtrusion = True + self.isShutdownEarly = False + self.isStartupEarly = False + self.lineIndex = 0 + self.lines = None + self.oldLocation = None + self.operatingFeedRateMinute = 959.0 + self.shutdownStepIndex = 999999999 + self.startupStepIndex = 999999999 + + def addAfterStartupLine( self, splitLine ): + "Add the after startup lines." + distanceAfterThreadBeginning = self.getDistanceAfterThreadBeginning() + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + segment = self.oldLocation - location + segmentLength = segment.magnitude() + distanceBack = distanceAfterThreadBeginning - self.afterStartupDistances[ self.startupStepIndex ] + if segmentLength > 0.0: + locationBack = location + segment * distanceBack / segmentLength + feedRate = self.operatingFeedRateMinute * self.afterStartupFlowRates[ self.startupStepIndex ] + if not self.isCloseToEither( locationBack, location, self.oldLocation ): + self.distanceFeedRate.addLine( self.getLinearMoveWithFeedRate( feedRate, locationBack ) ) + self.startupStepIndex += 1 + + def addLineSetShutdowns(self, line): + "Add a line and set the shutdown variables." + self.distanceFeedRate.addLine(line) + self.isShutdownEarly = True + + def getActiveFeedRateRatio(self): + "Get the feed rate of the first active move over the operating feed rate." + isSearchExtruderActive = self.isExtruderActive + for afterIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[ afterIndex ] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'G1': + if isSearchExtruderActive: + return gcodec.getFeedRateMinute( self.feedRateMinute, splitLine ) / self.operatingFeedRateMinute + elif firstWord == 'M101': + isSearchExtruderActive = True + print('active feed rate ratio was not found in oozebane.') + return 1.0 + + def getAddAfterStartupLines(self, line): + "Get and / or add after the startup lines." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + while self.isDistanceAfterThreadBeginningGreater(): + self.addAfterStartupLine(splitLine) + if self.startupStepIndex >= len( self.afterStartupDistances ): + self.startupStepIndex = len( self.afterStartupDistances ) + 999999999999 + return self.getLinearMoveWithFeedRateSplitLine( self.operatingFeedRateMinute, splitLine ) + feedRate = self.operatingFeedRateMinute * self.getStartupFlowRateMultiplier( self.getDistanceAfterThreadBeginning() / self.afterStartupDistance, len( self.afterStartupDistances ) ) + return self.getLinearMoveWithFeedRateSplitLine( feedRate, splitLine ) + + def getAddBeforeStartupLines(self, line): + "Get and / or add before the startup lines." + distanceThreadBeginning = self.getDistanceToThreadBeginning() + if distanceThreadBeginning == None: + return line + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + self.extruderInactiveLongEnough = False + self.isStartupEarly = True + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + segment = self.oldLocation - location + segmentLength = segment.magnitude() + distanceBack = self.earlyStartupDistance - distanceThreadBeginning + if segmentLength <= 0.0: + print('This should never happen, segmentLength is zero in getAddBeforeStartupLines in oozebane.') + print(line) + self.extruderInactiveLongEnough = True + self.isStartupEarly = False + return line + locationBack = location + segment * distanceBack / segmentLength + self.distanceFeedRate.addLine( self.getLinearMoveWithFeedRate( gcodec.getFeedRateMinute( self.feedRateMinute, splitLine ) , locationBack ) ) + self.distanceFeedRate.addLine('M101') + if self.isCloseToEither( locationBack, location, self.oldLocation ): + return '' + return self.getLinearMoveWithFeedRate( self.operatingFeedRateMinute, location ) + + def getAddShutSlowDownLine(self, line): + "Add the shutdown and slowdown lines." + if self.shutdownStepIndex >= len( self.earlyShutdownDistances ): + self.shutdownStepIndex = len( self.earlyShutdownDistances ) + 99999999 + return False + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + distanceThreadEnd = self.getDistanceToExtruderOffCommand( self.earlyShutdownDistances[ self.shutdownStepIndex ] ) + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + if distanceThreadEnd == None: + distanceThreadEnd = self.getDistanceToExtruderOffCommand( self.earlyShutdownDistances[0] ) + if distanceThreadEnd != None: + shutdownFlowRateMultiplier = self.getShutdownFlowRateMultiplier( 1.0 - distanceThreadEnd / self.earlyShutdownDistance, len( self.earlyShutdownDistances ) ) + line = self.getLinearMoveWithFeedRate( self.feedRateMinute * shutdownFlowRateMultiplier, location ) + self.distanceFeedRate.addLine(line) + return False + segment = self.oldLocation - location + segmentLength = segment.magnitude() + distanceBack = self.earlyShutdownDistances[ self.shutdownStepIndex ] - distanceThreadEnd + locationBack = location + if segmentLength > 0.0: + locationBack = location + segment * distanceBack / segmentLength + if self.shutdownStepIndex == 0: + if not self.isCloseToEither( locationBack, location, self.oldLocation ): + line = self.getLinearMoveWithFeedRate( self.feedRateMinute, locationBack ) + self.distanceFeedRate.addLine(line) + self.addLineSetShutdowns('M103') + return True + if self.isClose( locationBack, self.oldLocation ): + return True + feedRate = self.feedRateMinute * self.earlyShutdownFlowRates[ self.shutdownStepIndex ] + line = self.getLinearMoveWithFeedRate( feedRate, locationBack ) + if self.isClose( locationBack, location ): + line = self.getLinearMoveWithFeedRate( feedRate, location ) + self.distanceFeedRate.addLine(line) + return True + + def getAddShutSlowDownLines(self, line): + "Get and / or add the shutdown and slowdown lines." + while self.getAddShutSlowDownLine(line): + self.shutdownStepIndex += 1 + return '' + + def getCraftedGcode( self, gcodeText, oozebaneRepository ): + "Parse gcode text and store the oozebane gcode." + self.lines = archive.getTextLines(gcodeText) + self.oozebaneRepository = oozebaneRepository + self.parseInitialization( oozebaneRepository ) + for self.lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[self.lineIndex] + self.parseLine(line) + return self.distanceFeedRate.output.getvalue() + + def getDistanceAfterThreadBeginning(self): + "Get the distance after the beginning of the thread." + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + lastThreadLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + totalDistance = 0.0 + extruderOnReached = False + for beforeIndex in xrange( self.lineIndex - 1, 3, - 1 ): + line = self.lines[ beforeIndex ] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'G1': + location = gcodec.getLocationFromSplitLine( lastThreadLocation, splitLine ) + totalDistance += location.distance( lastThreadLocation ) + lastThreadLocation = location + if extruderOnReached: + return totalDistance + elif firstWord == 'M101': + extruderOnReached = True + return None + + def getDistanceToExtruderOffCommand( self, remainingDistance ): + "Get the distance to the word." + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + lastThreadLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + totalDistance = 0.0 + for afterIndex in xrange( self.lineIndex + 1, len(self.lines) ): + line = self.lines[ afterIndex ] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'G1': + location = gcodec.getLocationFromSplitLine( lastThreadLocation, splitLine ) + totalDistance += location.distance( lastThreadLocation ) + lastThreadLocation = location + if totalDistance >= remainingDistance: + return None + elif firstWord == 'M103': + return totalDistance + return None + + def getDistanceToThreadBeginning(self): + "Get the distance to the beginning of the thread." + if self.earlyStartupDistance == None: + return None + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + lastThreadLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + totalDistance = 0.0 + for afterIndex in xrange( self.lineIndex + 1, len(self.lines) ): + line = self.lines[ afterIndex ] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'G1': + location = gcodec.getLocationFromSplitLine( lastThreadLocation, splitLine ) + totalDistance += location.distance( lastThreadLocation ) + lastThreadLocation = location + if totalDistance >= self.earlyStartupDistance: + return None + elif firstWord == 'M101': + return totalDistance + return None + + def getDistanceToThreadBeginningAfterThreadEnd( self, remainingDistance ): + "Get the distance to the thread beginning after the end of this thread." + extruderOnReached = False + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + lastThreadLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + threadEndReached = False + totalDistance = 0.0 + for afterIndex in xrange( self.lineIndex + 1, len(self.lines) ): + line = self.lines[ afterIndex ] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'G1': + location = gcodec.getLocationFromSplitLine( lastThreadLocation, splitLine ) + if threadEndReached: + totalDistance += location.distance( lastThreadLocation ) + if totalDistance >= remainingDistance: + return None + if extruderOnReached: + return totalDistance + lastThreadLocation = location + elif firstWord == 'M101': + extruderOnReached = True + elif firstWord == 'M103': + threadEndReached = True + return None + + def getDistanceToThreadEnd(self): + "Get the distance to the end of the thread." + if self.shutdownStepIndex >= len( self.earlyShutdownDistances ): + return None + return self.getDistanceToExtruderOffCommand( self.earlyShutdownDistances[ self.shutdownStepIndex ] ) + + def getLinearMoveWithFeedRate( self, feedRate, location ): + "Get a linear move line with the feed rate." + return self.distanceFeedRate.getLinearGcodeMovementWithFeedRate( feedRate, location.dropAxis(), location.z ) + + def getLinearMoveWithFeedRateSplitLine( self, feedRate, splitLine ): + "Get a linear move line with the feed rate and split line." + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + return self.getLinearMoveWithFeedRate( feedRate, location ) + + def getOozebaneLine(self, line): + "Get oozebaned gcode line." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + self.feedRateMinute = gcodec.getFeedRateMinute( self.feedRateMinute, splitLine ) + if self.oldLocation == None: + return line + if self.startupStepIndex < len( self.afterStartupDistances ): + return self.getAddAfterStartupLines(line) + if self.extruderInactiveLongEnough: + return self.getAddBeforeStartupLines(line) + if self.shutdownStepIndex < len( self.earlyShutdownDistances ): + return self.getAddShutSlowDownLines(line) + if self.isStartupEarly: + return self.getLinearMoveWithFeedRateSplitLine( self.operatingFeedRateMinute, splitLine ) + return line + + def getShutdownFlowRateMultiplier( self, along, numberOfDistances ): + "Get the shut down flow rate multipler." + if numberOfDistances <= 0: + return 1.0 + return 1.0 - 0.5 / float( numberOfDistances ) - along * float( numberOfDistances - 1 ) / float( numberOfDistances ) + + def getStartupFlowRateMultiplier( self, along, numberOfDistances ): + "Get the startup flow rate multipler." + if numberOfDistances <= 0: + return 1.0 + return min( 1.0, 0.5 / float( numberOfDistances ) + along ) + + def isClose( self, location, otherLocation ): + "Determine if the location is close to the other location." + return location.distanceSquared( otherLocation ) < self.closeSquared + + def isCloseToEither( self, location, otherLocationFirst, otherLocationSecond ): + "Determine if the location is close to the other locations." + if self.isClose( location, otherLocationFirst ): + return True + return self.isClose( location, otherLocationSecond ) + + def isDistanceAfterThreadBeginningGreater(self): + "Determine if the distance after the thread beginning is greater than the step index after startup distance." + if self.startupStepIndex >= len( self.afterStartupDistances ): + return False + return self.getDistanceAfterThreadBeginning() > self.afterStartupDistances[ self.startupStepIndex ] + + def parseInitialization( self, oozebaneRepository ): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('oozebane') + return + elif firstWord == '(': + self.operatingFeedRateMinute = 60.0 * float(splitLine[1]) + self.feedRateMinute = self.operatingFeedRateMinute + elif firstWord == '(': + self.edgeWidth = float(splitLine[1]) + self.setExtrusionWidth( oozebaneRepository ) + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + "Parse a gcode line and add it to the bevel gcode." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + self.setEarlyStartupDistance(splitLine) + line = self.getOozebaneLine(line) + self.oldLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + elif firstWord == 'M101': + self.isExtruderActive = True + self.extruderInactiveLongEnough = False + if self.getDistanceToExtruderOffCommand( self.earlyShutdownDistance ) == None: + self.setEarlyShutdown() + if self.getDistanceToExtruderOffCommand( 1.03 * ( self.earlyShutdownDistance + self.afterStartupDistance ) ) == None: + afterStartupRatio = 1.0 + if self.minimumDistanceForEarlyStartup > 0.0: + if self.distanceFromThreadEndToThreadBeginning != None: + afterStartupRatio = self.distanceFromThreadEndToThreadBeginning / self.minimumDistanceForEarlyStartup + self.setAfterStartupFlowRates( afterStartupRatio ) + self.startupStepIndex = 9999999999 + if len( self.afterStartupDistances ) > 0: + self.startupStepIndex = 0 + if self.isStartupEarly: + self.isStartupEarly = False + return + elif firstWord == 'M103': + self.isExtruderActive = False + self.shutdownStepIndex = 999999999 + if self.getDistanceToThreadBeginning() == None: + self.extruderInactiveLongEnough = True + self.distanceFromThreadEndToThreadBeginning = None + self.earlyStartupDistance = None + if self.isShutdownEarly: + self.isShutdownEarly = False + return + self.distanceFeedRate.addLine(line) + + def setAfterStartupFlowRates( self, afterStartupRatio ): + "Set the after startup flow rates." + afterStartupRatio = min( 1.0, afterStartupRatio ) + afterStartupRatio = max( 0.0, afterStartupRatio ) + self.afterStartupDistance = afterStartupRatio * self.getActiveFeedRateRatio() * self.oozebaneRepository.afterStartupDistance.value + self.afterStartupDistances = [] + self.afterStartupFlowRate = 1.0 + self.afterStartupFlowRates = [] + afterStartupSteps = int( math.floor( afterStartupRatio * float( self.oozebaneRepository.slowdownStartupSteps.value ) ) ) + if afterStartupSteps < 1: + return + if afterStartupSteps < 2: + afterStartupSteps = 2 + for stepIndex in xrange( afterStartupSteps ): + afterWay = ( stepIndex + 1 ) / float( afterStartupSteps ) + afterMiddleWay = self.getStartupFlowRateMultiplier( stepIndex / float( afterStartupSteps ), afterStartupSteps ) + self.afterStartupDistances.append( afterWay * self.afterStartupDistance ) + if stepIndex == 0: + self.afterStartupFlowRate = afterMiddleWay + else: + self.afterStartupFlowRates.append( afterMiddleWay ) + if afterStartupSteps > 0: + self.afterStartupFlowRates.append(1.0) + + def setEarlyShutdown(self): + "Set the early shutdown variables." + distanceToThreadBeginning = self.getDistanceToThreadBeginningAfterThreadEnd( self.minimumDistanceForEarlyShutdown ) + earlyShutdownRatio = 1.0 + if distanceToThreadBeginning != None: + if self.minimumDistanceForEarlyShutdown > 0.0: + earlyShutdownRatio = distanceToThreadBeginning / self.minimumDistanceForEarlyShutdown + self.setEarlyShutdownFlowRates( earlyShutdownRatio ) + if len( self.earlyShutdownDistances ) > 0: + self.shutdownStepIndex = 0 + + def setEarlyShutdownFlowRates( self, earlyShutdownRatio ): + "Set the extrusion width." + earlyShutdownRatio = min( 1.0, earlyShutdownRatio ) + earlyShutdownRatio = max( 0.0, earlyShutdownRatio ) + self.earlyShutdownDistance = earlyShutdownRatio * self.getActiveFeedRateRatio() * self.oozebaneRepository.earlyShutdownDistance.value + self.earlyShutdownDistances = [] + self.earlyShutdownFlowRates = [] + earlyShutdownSteps = int( math.floor( earlyShutdownRatio * float( self.oozebaneRepository.slowdownStartupSteps.value ) ) ) + if earlyShutdownSteps < 2: + earlyShutdownSteps = 0 + earlyShutdownStepsMinusOne = float( earlyShutdownSteps ) - 1.0 + for stepIndex in xrange( earlyShutdownSteps ): + downMiddleWay = self.getShutdownFlowRateMultiplier( stepIndex / earlyShutdownStepsMinusOne, earlyShutdownSteps ) + downWay = 1.0 - stepIndex / earlyShutdownStepsMinusOne + self.earlyShutdownFlowRates.append( downMiddleWay ) + self.earlyShutdownDistances.append( downWay * self.earlyShutdownDistance ) + + def setEarlyStartupDistance( self, splitLine ): + "Set the early startup distance." + if self.earlyStartupDistance != None: + return + self.distanceFromThreadEndToThreadBeginning = 0.0 + lastThreadLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + if self.oldLocation != None: + self.distanceFromThreadEndToThreadBeginning = lastThreadLocation.distance( self.oldLocation ) + for afterIndex in xrange( self.lineIndex + 1, len(self.lines) ): + line = self.lines[ afterIndex ] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'G1': + location = gcodec.getLocationFromSplitLine( lastThreadLocation, splitLine ) + self.distanceFromThreadEndToThreadBeginning += location.distance( lastThreadLocation ) + lastThreadLocation = location + elif firstWord == 'M101': + distanceConstantRatio = self.distanceFromThreadEndToThreadBeginning / self.earlyStartupDistanceConstant + earlyStartupOperatingDistance = self.earlyStartupMaximumDistance * ( 1.0 - math.exp( - distanceConstantRatio ) ) + if self.isFirstExtrusion: + earlyStartupOperatingDistance = self.oozebaneRepository.firstEarlyStartupDistance.value + self.isFirstExtrusion = False + self.earlyStartupDistance = earlyStartupOperatingDistance * self.getActiveFeedRateRatio() + return + + def setExtrusionWidth( self, oozebaneRepository ): + "Set the extrusion width." + self.closeSquared = 0.01 * self.edgeWidth * self.edgeWidth + self.earlyStartupMaximumDistance = oozebaneRepository.earlyStartupMaximumDistance.value + self.earlyStartupDistanceConstant = oozebaneRepository.earlyStartupDistanceConstant.value + self.minimumDistanceForEarlyStartup = oozebaneRepository.minimumDistanceForEarlyStartup.value + self.minimumDistanceForEarlyShutdown = oozebaneRepository.minimumDistanceForEarlyShutdown.value + self.setEarlyShutdownFlowRates(1.0) + self.setAfterStartupFlowRates(1.0) + + +def main(): + "Display the oozebane dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/outset.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/outset.py new file mode 100644 index 0000000..6bc7529 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/outset.py @@ -0,0 +1,168 @@ +""" +This page is in the table of contents. +Outset outsets the edges of the slices of a gcode file. The outside edges will be outset by half the edge width, and the inside edges will be inset by half the edge width. Outset is needed for subtractive machining, like cutting or milling. + +==Operation== +The default 'Activate Outset' checkbox is on. When it is on, the gcode will be outset, when it is off, the gcode will not be changed. + +==Examples== +The following examples outset the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and outset.py. + +> python outset.py +This brings up the outset dialog. + +> python outset.py Screw Holder Bottom.stl +The outset tool is parsing the file: +Screw Holder Bottom.stl +.. +The outset tool has created the file: +.. Screw Holder Bottom_outset.gcode + +""" + +from __future__ import absolute_import +try: + import psyco + psyco.full() +except: + pass +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import sys + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCraftedText( fileName, text='', repository=None): + 'Outset the preface file or text.' + return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository) + +def getCraftedTextFromText(gcodeText, repository=None): + 'Outset the preface gcode text.' + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'outset'): + return gcodeText + if repository == None: + repository = settings.getReadRepository( OutsetRepository() ) + if not repository.activateOutset.value: + return gcodeText + return OutsetSkein().getCraftedGcode(gcodeText, repository) + +def getNewRepository(): + 'Get new repository.' + return OutsetRepository() + +def writeOutput(fileName, shouldAnalyze=True): + 'Outset the carving of a gcode file.' + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'outset', shouldAnalyze) + + +class OutsetRepository: + 'A class to handle the outset settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.outset.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Outset', self, '') + self.activateOutset = settings.BooleanSetting().getFromValue('Activate Outset', self, True ) + self.executeTitle = 'Outset' + + def execute(self): + 'Outset button has been clicked.' + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class OutsetSkein: + 'A class to outset a skein of extrusions.' + def __init__(self): + self.boundary = None + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.layerCount = settings.LayerCount() + self.lineIndex = 0 + self.loopLayer = None + + def addGcodeFromRemainingLoop( self, loop, radius, z ): + 'Add the remainder of the loop.' + boundary = intercircle.getLargestInsetLoopFromLoopRegardless( loop, radius ) + euclidean.addNestedRingBeginning( self.distanceFeedRate, boundary, z ) + self.distanceFeedRate.addPerimeterBlock(loop, z) + self.distanceFeedRate.addLine('()') + self.distanceFeedRate.addLine('()') + + def addOutset(self, loopLayer): + 'Add outset to the layer.' + extrudateLoops = intercircle.getInsetLoopsFromLoops(loopLayer.loops, -self.absoluteHalfEdgeWidth) + triangle_mesh.sortLoopsInOrderOfArea(False, extrudateLoops) + for extrudateLoop in extrudateLoops: + self.addGcodeFromRemainingLoop(extrudateLoop, self.absoluteHalfEdgeWidth, loopLayer.z) + + def getCraftedGcode(self, gcodeText, repository): + 'Parse gcode text and store the bevel gcode.' + self.repository = repository + self.lines = archive.getTextLines(gcodeText) + self.parseInitialization() + for lineIndex in xrange(self.lineIndex, len(self.lines)): + self.parseLine( lineIndex ) + return self.distanceFeedRate.output.getvalue() + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex].lstrip() + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('outset') + return + elif firstWord == '(': + self.absoluteHalfEdgeWidth = 0.5 * abs(float(splitLine[1])) + self.distanceFeedRate.addLine(line) + + def parseLine( self, lineIndex ): + 'Parse a gcode line and add it to the outset skein.' + line = self.lines[lineIndex].lstrip() + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == '(': + location = gcodec.getLocationFromSplitLine(None, splitLine) + self.boundary.append(location.dropAxis()) + elif firstWord == '(': + self.layerCount.printProgressIncrement('outset') + self.loopLayer = euclidean.LoopLayer(float(splitLine[1])) + self.distanceFeedRate.addLine(line) + elif firstWord == '()': + self.addOutset( self.loopLayer ) + self.loopLayer = None + elif firstWord == '()': + self.boundary = [] + self.loopLayer.loops.append( self.boundary ) + if self.loopLayer == None: + self.distanceFeedRate.addLine(line) + + +def main(): + 'Display the outset dialog.' + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/preface.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/preface.py new file mode 100644 index 0000000..f39a260 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/preface.py @@ -0,0 +1,230 @@ +#! /usr/bin/env python +""" +This page is in the table of contents. +Preface converts the svg slices into gcode extrusion layers, optionally with home, positioning, turn off, and unit commands. + +The preface manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Preface + +==Settings== +===Meta=== +Default is empty. + +The 'Meta' field is to add meta tags or a note to all your files. Whatever is in that field will be added in a meta tagged line to the output. + +===Set Positioning to Absolute=== +Default is on. + +When selected, preface will add the G90 command to set positioning to absolute. + +===Set Units to Millimeters=== +Default is on. + +When selected, preface will add the G21 command to set the units to millimeters. + +===Start at Home=== +Default is off. + +When selected, the G28 go to home gcode will be added at the beginning of the file. + +===Turn Extruder Off=== +====Turn Extruder Off at Shut Down==== +Default is on. + +When selected, the M103 turn extruder off gcode will be added at the end of the file. + +====Turn Extruder Off at Start Up==== +Default is on. + +When selected, the M103 turn extruder off gcode will be added at the beginning of the file. + +==Examples== +The following examples preface the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and preface.py. + +> python preface.py +This brings up the preface dialog. + +> python preface.py Screw Holder Bottom.stl +The preface tool is parsing the file: +Screw Holder Bottom.stl +.. +The preface tool has created the file: +.. Screw Holder Bottom_preface.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from datetime import date, datetime +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities.svg_reader import SVGReader +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from fabmetheus_utilities import svg_writer +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +from time import strftime +import os +import sys + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCraftedText( fileName, text='', repository = None ): + "Preface and convert an svg file or text." + return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository) + +def getCraftedTextFromText( text, repository = None ): + "Preface and convert an svg text." + if gcodec.isProcedureDoneOrFileIsEmpty( text, 'preface'): + return text + if repository == None: + repository = settings.getReadRepository(PrefaceRepository()) + return PrefaceSkein().getCraftedGcode(repository, text) + +def getNewRepository(): + 'Get new repository.' + return PrefaceRepository() + +def writeOutput(fileName, shouldAnalyze=True): + "Preface the carving of a gcode file." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'preface', shouldAnalyze) + + +class PrefaceRepository: + "A class to handle the preface settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.preface.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Preface', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Preface') + self.meta = settings.StringSetting().getFromValue('Meta:', self, '') + self.setPositioningToAbsolute = settings.BooleanSetting().getFromValue('Set Positioning to Absolute', self, True ) + self.setUnitsToMillimeters = settings.BooleanSetting().getFromValue('Set Units to Millimeters', self, True ) + self.startAtHome = settings.BooleanSetting().getFromValue('Start at Home', self, False ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Turn Extruder Off -', self ) + self.turnExtruderOffAtShutDown = settings.BooleanSetting().getFromValue('Turn Extruder Off at Shut Down', self, True ) + self.turnExtruderOffAtStartUp = settings.BooleanSetting().getFromValue('Turn Extruder Off at Start Up', self, True ) + self.executeTitle = 'Preface' + + def execute(self): + "Preface button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class PrefaceSkein: + "A class to preface a skein of extrusions." + def __init__(self): + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.extruderActive = False + self.lineIndex = 0 + self.oldLocation = None + self.svgReader = SVGReader() + + def addInitializationToOutput(self): + "Add initialization gcode to the output." + self.distanceFeedRate.addTagBracketedLine('format', 'skeinforge gcode') + absoluteFilePathUntilDot = archive.getUntilDot(archive.getCraftPluginsDirectoryPath('preface.py')) + dateTodayString = date.today().isoformat().replace('-', '.')[2 :] + if absoluteFilePathUntilDot == '/home/enrique/Desktop/backup/babbleold/script/reprap/fabmetheus/skeinforge_application/skeinforge_plugins/craft_plugins/preface': #is this script on Enrique's computer? + archive.writeFileText(archive.getVersionFileName(), dateTodayString) + versionText = archive.getFileText(archive.getVersionFileName()) + self.distanceFeedRate.addTagBracketedLine('version', versionText) + dateTimeTuple = datetime.now().timetuple() + created = dateTodayString + '|%s:%s' % (dateTimeTuple[3], dateTimeTuple[4]) + self.distanceFeedRate.addTagBracketedLine('created', created) + self.distanceFeedRate.addLine('()') + if self.repository.setPositioningToAbsolute.value: + self.distanceFeedRate.addLine('G90 ;set positioning to absolute') # Set positioning to absolute. + if self.repository.setUnitsToMillimeters.value: + self.distanceFeedRate.addLine('G21 ;set units to millimeters') # Set units to millimeters. + if self.repository.startAtHome.value: + self.distanceFeedRate.addLine('G28 ;start at home') # Start at home. + if self.repository.turnExtruderOffAtStartUp.value: + self.distanceFeedRate.addLine('M103') # Turn extruder off. + craftTypeName = skeinforge_profile.getCraftTypeName() + self.distanceFeedRate.addTagBracketedLine('craftTypeName', craftTypeName) + self.distanceFeedRate.addTagBracketedLine('decimalPlacesCarried', self.distanceFeedRate.decimalPlacesCarried) + layerHeight = float(self.svgReader.sliceDictionary['layerHeight']) + self.distanceFeedRate.addTagRoundedLine('layerThickness', layerHeight) + self.distanceFeedRate.addTagRoundedLine('layerHeight', layerHeight) + if self.repository.meta.value: + self.distanceFeedRate.addTagBracketedLine('meta', self.repository.meta.value) + edgeWidth = float(self.svgReader.sliceDictionary['edgeWidth']) + self.distanceFeedRate.addTagRoundedLine('edgeWidth', edgeWidth) + self.distanceFeedRate.addTagRoundedLine('perimeterWidth', edgeWidth) + self.distanceFeedRate.addTagBracketedLine('profileName', skeinforge_profile.getProfileName(craftTypeName)) + self.distanceFeedRate.addLine('()') + pluginFileNames = skeinforge_craft.getPluginFileNames() + for pluginFileName in pluginFileNames: + self.addToolSettingLines(pluginFileName) + self.distanceFeedRate.addLine('()') + self.distanceFeedRate.addTagBracketedLine('timeStampPreface', strftime('%Y%m%d_%H%M%S')) + procedureNames = self.svgReader.sliceDictionary['procedureName'].replace(',', ' ').split() + for procedureName in procedureNames: + self.distanceFeedRate.addTagBracketedProcedure(procedureName) + self.distanceFeedRate.addTagBracketedProcedure('preface') + self.distanceFeedRate.addLine('()') # Initialization is finished, extrusion is starting. + self.distanceFeedRate.addLine('()') # Initialization is finished, crafting is starting. + + def addPreface( self, loopLayer ): + "Add preface to the carve layer." + self.distanceFeedRate.addLine('( %s )' % loopLayer.z ) # Indicate that a new layer is starting. + for loop in loopLayer.loops: + self.distanceFeedRate.addGcodeFromLoop(loop, loopLayer.z) + self.distanceFeedRate.addLine('()') + + def addShutdownToOutput(self): + "Add shutdown gcode to the output." + self.distanceFeedRate.addLine('()') # GCode formatted comment + if self.repository.turnExtruderOffAtShutDown.value: + self.distanceFeedRate.addLine('M103') # Turn extruder motor off. + + def addToolSettingLines(self, pluginName): + "Add tool setting lines." + preferences = skeinforge_craft.getCraftPreferences(pluginName) + if skeinforge_craft.getCraftValue('Activate %s' % pluginName.capitalize(), preferences) != True: + return + for preference in preferences: + valueWithoutReturn = str(preference.value).replace('\n', ' ').replace('\r', ' ') + if preference.name != 'WindowPosition' and not preference.name.startswith('Open File'): + line = '%s %s %s' % (pluginName, preference.name.replace(' ', '_'), valueWithoutReturn) + self.distanceFeedRate.addTagBracketedLine('setting', line) + + def getCraftedGcode( self, repository, gcodeText ): + "Parse gcode text and store the bevel gcode." + self.repository = repository + self.svgReader.parseSVG('', gcodeText) + if self.svgReader.sliceDictionary == None: + print('Warning, nothing will be done because the sliceDictionary could not be found getCraftedGcode in preface.') + return '' + self.distanceFeedRate.decimalPlacesCarried = int(self.svgReader.sliceDictionary['decimalPlacesCarried']) + self.addInitializationToOutput() + for loopLayerIndex, loopLayer in enumerate(self.svgReader.loopLayers): + settings.printProgressByNumber(loopLayerIndex, len(self.svgReader.loopLayers), 'preface') + self.addPreface( loopLayer ) + self.addShutdownToOutput() + return self.distanceFeedRate.output.getvalue() + + +def main(): + "Display the preface dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/raft.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/raft.py new file mode 100644 index 0000000..bf6fbb2 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/raft.py @@ -0,0 +1,1104 @@ +""" +This page is in the table of contents. +Raft is a plugin to create a raft, elevate the nozzle and set the temperature. A raft is a flat base structure on top of which your object is being build and has a few different purposes. It fills irregularities like scratches and pits in your printbed and gives you a nice base parallel to the printheads movement. It also glues your object to the bed so to prevent warping in bigger object. The rafts base layer performs these tricks while the sparser interface layer(s) help you removing the object from the raft after printing. It is based on the Nophead's reusable raft, which has a base layer running one way, and a couple of perpendicular layers above. Each set of layers can be set to a different temperature. There is the option of having the extruder orbit the raft for a while, so the heater barrel has time to reach a different temperature, without ooze accumulating around the nozzle. + +The raft manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Raft + +The important values for the raft settings are the temperatures of the raft, the first layer and the next layers. These will be different for each material. The default settings for ABS, HDPE, PCL & PLA are extrapolated from Nophead's experiments. + +You don't necessarily need a raft and especially small object will print fine on a flat bed without one, sometimes its even better when you need a water tight base to print directly on the bed. If you want to only set the temperature or only create support material or only elevate the nozzle without creating a raft, set the Base Layers and Interface Layers to zero. + + +Image:Raft.jpg|Raft + + +Example of a raft on the left with the interface layers partially removed exposing the base layer. Notice that the first line of the base is rarely printed well because of the startup time of the extruder. On the right you see an object with its raft still attached. + +The Raft panel has some extra settings, it probably made sense to have them there but they have not that much to do with the actual Raft. First are the Support material settings. Since close to all RepRap style printers have no second extruder for support material Skeinforge offers the option to print support structures with the same material set at a different speed and temperature. The idea is that the support sticks less to the actual object when it is extruded around the minimum possible working temperature. This results in a temperature change EVERY layer so build time will increase seriously. + +Allan Ecker aka The Masked Retriever's has written two quicktips for raft which follow below. +"Skeinforge Quicktip: The Raft, Part 1" at: +http://blog.thingiverse.com/2009/07/14/skeinforge-quicktip-the-raft-part-1/ +"Skeinforge Quicktip: The Raft, Part II" at: +http://blog.thingiverse.com/2009/08/04/skeinforge-quicktip-the-raft-part-ii/ + +Nophead has written about rafts on his blog: +http://hydraraptor.blogspot.com/2009/07/thoughts-on-rafts.html + +More pictures of rafting in action are available from the Metalab blog at: +http://reprap.soup.io/?search=rafting + +==Operation== +Default: On + +When it is on, the functions described below will work, when it is off, nothing will be done, so no temperatures will be set, nozzle will not be lifted.. + +==Settings== +===Add Raft, Elevate Nozzle, Orbit=== +Default: On + +When selected, the script will also create a raft, elevate the nozzle, orbit and set the altitude of the bottom of the raft. It also turns on support generation. + +===Base=== +Base layer is the part of the raft that touches the bed. + +====Base Feed Rate Multiplier==== +Default is one. + +Defines the base feed rate multiplier. The greater the 'Base Feed Rate Multiplier', the thinner the base, the lower the 'Base Feed Rate Multiplier', the thicker the base. + +====Base Flow Rate Multiplier==== +Default is one. + +Defines the base flow rate multiplier. The greater the 'Base Flow Rate Multiplier', the thicker the base, the lower the 'Base Flow Rate Multiplier', the thinner the base. + +====Base Infill Density==== +Default is 0.5. + +Defines the infill density ratio of the base of the raft. + +====Base Layer Height over Layer Thickness==== +Default is two. + +Defines the ratio of the height & width of the base layer compared to the height and width of the object infill. The feed rate will be slower for raft layers which have thicker extrusions than the object infill. + +====Base Layers==== +Default is one. + +Defines the number of base layers. + +====Base Nozzle Lift over Base Layer Thickness==== +Default is 0.4. + +Defines the amount the nozzle is above the center of the base extrusion divided by the base layer thickness. + +===Initial Circling=== +Default is off. + +When selected, the extruder will initially circle around until it reaches operating temperature. + +===Infill Overhang over Extrusion Width=== +Default is 0.05. + +Defines the ratio of the infill overhang over the the extrusion width of the raft. + +===Interface=== +====Interface Feed Rate Multiplier==== +Default is one. + +Defines the interface feed rate multiplier. The greater the 'Interface Feed Rate Multiplier', the thinner the interface, the lower the 'Interface Feed Rate Multiplier', the thicker the interface. + +====Interface Flow Rate Multiplier==== +Default is one. + +Defines the interface flow rate multiplier. The greater the 'Interface Flow Rate Multiplier', the thicker the interface, the lower the 'Interface Flow Rate Multiplier', the thinner the interface. + +====Interface Infill Density==== +Default is 0.5. + +Defines the infill density ratio of the interface of the raft. + +====Interface Layer Thickness over Extrusion Height==== +Default is one. + +Defines the ratio of the height & width of the interface layer compared to the height and width of the object infill. The feed rate will be slower for raft layers which have thicker extrusions than the object infill. + +====Interface Layers==== +Default is two. + +Defines the number of interface layers to print. + +====Interface Nozzle Lift over Interface Layer Thickness==== +Default is 0.45. + +Defines the amount the nozzle is above the center of the interface extrusion divided by the interface layer thickness. + +===Name of Alteration Files=== +If support material is generated, raft looks for alteration files in the alterations folder in the .skeinforge folder in the home directory. Raft does not care if the text file names are capitalized, but some file systems do not handle file name cases properly, so to be on the safe side you should give them lower case names. If it doesn't find the file it then looks in the alterations folder in the skeinforge_plugins folder. + +====Name of Support End File==== +Default is support_end.gcode. + +If support material is generated and if there is a file with the name of the "Name of Support End File" setting, it will be added to the end of the support gcode. + +====Name of Support Start File==== +If support material is generated and if there is a file with the name of the "Name of Support Start File" setting, it will be added to the start of the support gcode. + +===Operating Nozzle Lift over Layer Thickness=== +Default is 0.5. + +Defines the amount the nozzle is above the center of the operating extrusion divided by the layer height. + +===Raft Size=== +The raft fills a rectangle whose base size is the rectangle around the bottom layer of the object expanded on each side by the 'Raft Margin' plus the 'Raft Additional Margin over Length (%)' percentage times the length of the side. + +====Raft Additional Margin over Length==== +Default is 1 percent. + +====Raft Margin==== +Default is three millimeters. + +===Support=== +Good articles on support material are at: +http://davedurant.wordpress.com/2010/07/31/skeinforge-support-part-1/ +http://davedurant.wordpress.com/2010/07/31/skeinforge-support-part-2/ + +====Support Cross Hatch==== +Default is off. + +When selected, the support material will cross hatched. Cross hatching the support makes it stronger and harder to remove, which is why the default is off. + +====Support Flow Rate over Operating Flow Rate==== +Default: 0.9. + +Defines the ratio of the flow rate when the support is extruded over the operating flow rate. With a number less than one, the support flow rate will be smaller so the support will be thinner and easier to remove. + +====Support Gap over Perimeter Extrusion Width==== +Default: 0.5. + +Defines the gap between the support material and the object over the edge extrusion width. + +====Support Material Choice==== +Default is 'None' because the raft takes time to generate. + +=====Empty Layers Only===== +When selected, support material will be only on the empty layers. This is useful when making identical objects in a stack. + +=====Everywhere===== +When selected, support material will be added wherever there are overhangs, even inside the object. Because support material inside objects is hard or impossible to remove, this option should only be chosen if the object has a cavity that needs support and there is some way to extract the support material. + +=====Exterior Only===== +When selected, support material will be added only the exterior of the object. This is the best option for most objects which require support material. + +=====None===== +When selected, raft will not add support material. + +====Support Minimum Angle==== +Default is sixty degrees. + +Defines the minimum angle that a surface overhangs before support material is added. If angle is lower then this value the support will be generated. This angle is defined from the vertical, so zero is a vertical wall, ten is a wall with a bit of overhang, thirty is the typical safe angle for filament extrusion, sixty is a really high angle for extrusion and ninety is an unsupported horizontal ceiling. + +==Examples== +The following examples raft the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and raft.py. + +> python raft.py +This brings up the raft dialog. + +> python raft.py Screw Holder Bottom.stl +The raft tool is parsing the file: +Screw Holder Bottom.stl +.. +The raft tool has created the file: +Screw Holder Bottom_raft.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import os +import sys + + +__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' + + +#maybe later wide support +#raft outline temperature http://hydraraptor.blogspot.com/2008/09/screw-top-pot.html +def getCraftedText( fileName, text='', repository=None): + 'Raft the file or text.' + return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository) + +def getCraftedTextFromText(gcodeText, repository=None): + 'Raft a gcode linear move text.' + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'raft'): + return gcodeText + if repository == None: + repository = settings.getReadRepository( RaftRepository() ) + if not repository.activateRaft.value: + return gcodeText + return RaftSkein().getCraftedGcode(gcodeText, repository) + +def getCrossHatchPointLine( crossHatchPointLineTable, y ): + 'Get the cross hatch point line.' + if not crossHatchPointLineTable.has_key(y): + crossHatchPointLineTable[ y ] = {} + return crossHatchPointLineTable[ y ] + +def getEndpointsFromYIntersections( x, yIntersections ): + 'Get endpoints from the y intersections.' + endpoints = [] + for yIntersectionIndex in xrange( 0, len( yIntersections ), 2 ): + firstY = yIntersections[ yIntersectionIndex ] + secondY = yIntersections[ yIntersectionIndex + 1 ] + if firstY != secondY: + firstComplex = complex( x, firstY ) + secondComplex = complex( x, secondY ) + endpointFirst = euclidean.Endpoint() + endpointSecond = euclidean.Endpoint().getFromOtherPoint( endpointFirst, secondComplex ) + endpointFirst.getFromOtherPoint( endpointSecond, firstComplex ) + endpoints.append( endpointFirst ) + endpoints.append( endpointSecond ) + return endpoints + +def getExtendedLineSegment(extensionDistance, lineSegment, loopXIntersections): + 'Get extended line segment.' + pointBegin = lineSegment[0].point + pointEnd = lineSegment[1].point + segment = pointEnd - pointBegin + segmentLength = abs(segment) + if segmentLength <= 0.0: + print('This should never happen in getExtendedLineSegment in raft, the segment should have a length greater than zero.') + print(lineSegment) + return None + segmentExtend = segment * extensionDistance / segmentLength + lineSegment[0].point -= segmentExtend + lineSegment[1].point += segmentExtend + for loopXIntersection in loopXIntersections: + setExtendedPoint(lineSegment[0], pointBegin, loopXIntersection) + setExtendedPoint(lineSegment[1], pointEnd, loopXIntersection) + return lineSegment + +def getLoopsBySegmentsDictionary(segmentsDictionary, width): + 'Get loops from a horizontal segments dictionary.' + points = [] + for endpoint in getVerticalEndpoints(segmentsDictionary, width, 0.1 * width, width): + points.append(endpoint.point) + for endpoint in euclidean.getEndpointsFromSegmentTable(segmentsDictionary): + points.append(endpoint.point) + return triangle_mesh.getDescendingAreaOrientedLoops(points, points, width + width) + +def getNewRepository(): + 'Get new repository.' + return RaftRepository() + +def getVerticalEndpoints(horizontalSegmentsTable, horizontalStep, verticalOverhang, verticalStep): + 'Get vertical endpoints.' + interfaceSegmentsTableKeys = horizontalSegmentsTable.keys() + interfaceSegmentsTableKeys.sort() + verticalTableTable = {} + for interfaceSegmentsTableKey in interfaceSegmentsTableKeys: + interfaceSegments = horizontalSegmentsTable[interfaceSegmentsTableKey] + for interfaceSegment in interfaceSegments: + begin = int(round(interfaceSegment[0].point.real / verticalStep)) + end = int(round(interfaceSegment[1].point.real / verticalStep)) + for stepIndex in xrange(begin, end + 1): + if stepIndex not in verticalTableTable: + verticalTableTable[stepIndex] = {} + verticalTableTable[stepIndex][interfaceSegmentsTableKey] = None + verticalTableTableKeys = verticalTableTable.keys() + verticalTableTableKeys.sort() + verticalEndpoints = [] + for verticalTableTableKey in verticalTableTableKeys: + verticalTable = verticalTableTable[verticalTableTableKey] + verticalTableKeys = verticalTable.keys() + verticalTableKeys.sort() + xIntersections = [] + for verticalTableKey in verticalTableKeys: + y = verticalTableKey * horizontalStep + if verticalTableKey - 1 not in verticalTableKeys: + xIntersections.append(y - verticalOverhang) + if verticalTableKey + 1 not in verticalTableKeys: + xIntersections.append(y + verticalOverhang) + for segment in euclidean.getSegmentsFromXIntersections(xIntersections, verticalTableTableKey * verticalStep): + for endpoint in segment: + endpoint.point = complex(endpoint.point.imag, endpoint.point.real) + verticalEndpoints.append(endpoint) + return verticalEndpoints + +def setExtendedPoint( lineSegmentEnd, pointOriginal, x ): + 'Set the point in the extended line segment.' + if x > min( lineSegmentEnd.point.real, pointOriginal.real ) and x < max( lineSegmentEnd.point.real, pointOriginal.real ): + lineSegmentEnd.point = complex( x, pointOriginal.imag ) + +def writeOutput(fileName, shouldAnalyze=True): + 'Raft a gcode linear move file.' + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'raft', shouldAnalyze) + + +class RaftRepository: + 'A class to handle the raft settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.raft.html', self) + self.fileNameInput = settings.FileNameInput().getFromFileName( + fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Raft', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute( + 'http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Raft') + self.activateRaft = settings.BooleanSetting().getFromValue('Activate Raft', self, True) + self.addRaftElevateNozzleOrbitSetAltitude = settings.BooleanSetting().getFromValue( + 'Add Raft, Elevate Nozzle, Orbit:', self, True) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Base -', self) + self.baseFeedRateMultiplier = settings.FloatSpin().getFromValue(0.7, 'Base Feed Rate Multiplier (ratio):', self, 1.1, 1.0) + self.baseFlowRateMultiplier = settings.FloatSpin().getFromValue(0.7, 'Base Flow Rate Multiplier (ratio):', self, 1.1, 1.0) + self.baseInfillDensity = settings.FloatSpin().getFromValue(0.3, 'Base Infill Density (ratio):', self, 0.9, 0.5) + self.baseLayerThicknessOverLayerThickness = settings.FloatSpin().getFromValue( + 1.0, 'Base Layer Thickness over Layer Thickness:', self, 3.0, 2.0) + self.baseLayers = settings.IntSpin().getFromValue(0, 'Base Layers (integer):', self, 3, 0) + self.baseNozzleLiftOverBaseLayerThickness = settings.FloatSpin().getFromValue( + 0.2, 'Base Nozzle Lift over Base Layer Thickness (ratio):', self, 0.8, 0.4) + settings.LabelSeparator().getFromRepository(self) + self.initialCircling = settings.BooleanSetting().getFromValue('Initial Circling:', self, False) + self.infillOverhangOverExtrusionWidth = settings.FloatSpin().getFromValue( + 0.0, 'Infill Overhang over Extrusion Width (ratio):', self, 0.5, 0.05) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Interface -', self) + self.interfaceFeedRateMultiplier = settings.FloatSpin().getFromValue( + 0.7, 'Interface Feed Rate Multiplier (ratio):', self, 1.1, 1.0) + self.interfaceFlowRateMultiplier = settings.FloatSpin().getFromValue( + 0.7, 'Interface Flow Rate Multiplier (ratio):', self, 1.1, 1.0) + self.interfaceInfillDensity = settings.FloatSpin().getFromValue( + 0.3, 'Interface Infill Density (ratio):', self, 0.9, 0.5) + self.interfaceLayerThicknessOverLayerThickness = settings.FloatSpin().getFromValue( + 1.0, 'Interface Layer Thickness over Layer Thickness:', self, 3.0, 1.0) + self.interfaceLayers = settings.IntSpin().getFromValue( + 0, 'Interface Layers (integer):', self, 3, 0) + self.interfaceNozzleLiftOverInterfaceLayerThickness = settings.FloatSpin().getFromValue( + 0.25, 'Interface Nozzle Lift over Interface Layer Thickness (ratio):', self, 0.85, 0.45) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Name of Alteration Files -', self) + self.nameOfSupportEndFile = settings.StringSetting().getFromValue('Name of Support End File:', self, 'support_end.gcode') + self.nameOfSupportStartFile = settings.StringSetting().getFromValue( + 'Name of Support Start File:', self, 'support_start.gcode') + settings.LabelSeparator().getFromRepository(self) + self.operatingNozzleLiftOverLayerThickness = settings.FloatSpin().getFromValue( + 0.3, 'Operating Nozzle Lift over Layer Thickness (ratio):', self, 0.7, 0.5) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Raft Size -', self) + self.raftAdditionalMarginOverLengthPercent = settings.FloatSpin().getFromValue( + 0.5, 'Raft Additional Margin over Length (%):', self, 1.5, 1.0) + self.raftMargin = settings.FloatSpin().getFromValue( + 1.0, 'Raft Margin (mm):', self, 5.0, 3.0) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Support -', self) + self.supportCrossHatch = settings.BooleanSetting().getFromValue('Support Cross Hatch', self, False) + self.supportFlowRateOverOperatingFlowRate = settings.FloatSpin().getFromValue( + 0.7, 'Support Flow Rate over Operating Flow Rate (ratio):', self, 1.1, 1.0) + self.supportGapOverPerimeterExtrusionWidth = settings.FloatSpin().getFromValue( + 0.5, 'Support Gap over Perimeter Extrusion Width (ratio):', self, 1.5, 1.0) + self.supportMaterialChoice = settings.MenuButtonDisplay().getFromName('Support Material Choice: ', self) + self.supportChoiceNone = settings.MenuRadio().getFromMenuButtonDisplay(self.supportMaterialChoice, 'None', self, True) + self.supportChoiceEmptyLayersOnly = settings.MenuRadio().getFromMenuButtonDisplay(self.supportMaterialChoice, 'Empty Layers Only', self, False) + self.supportChoiceEverywhere = settings.MenuRadio().getFromMenuButtonDisplay(self.supportMaterialChoice, 'Everywhere', self, False) + self.supportChoiceExteriorOnly = settings.MenuRadio().getFromMenuButtonDisplay(self.supportMaterialChoice, 'Exterior Only', self, False) + self.supportMinimumAngle = settings.FloatSpin().getFromValue(40.0, 'Support Minimum Angle (degrees):', self, 80.0, 60.0) + self.executeTitle = 'Raft' + + def execute(self): + 'Raft button has been clicked.' + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class RaftSkein: + 'A class to raft a skein of extrusions.' + def __init__(self): + self.addLineLayerStart = True + self.baseTemperature = None + self.beginLoop = None + self.boundaryLayers = [] + self.coolingRate = None + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.edgeWidth = 0.6 + self.extrusionStart = True + self.extrusionTop = 0.0 + self.feedRateMinute = 961.0 + self.heatingRate = None + self.insetTable = {} + self.interfaceTemperature = None + self.isEdgePath = False + self.isNestedRing = True + self.isStartupEarly = False + self.layerIndex = - 1 + self.layerStarted = False + self.layerHeight = 0.4 + self.lineIndex = 0 + self.lines = None + self.objectFirstLayerInfillTemperature = None + self.objectFirstLayerPerimeterTemperature = None + self.objectNextLayersTemperature = None + self.oldFlowRate = None + self.oldLocation = None + self.oldTemperatureOutputString = None + self.operatingFeedRateMinute = None + self.operatingLayerEndLine = '( )' + self.operatingJump = None + self.orbitalFeedRatePerSecond = 2.01 + self.supportFlowRate = None + self.supportLayers = [] + self.supportLayersTemperature = None + self.supportedLayersTemperature = None + self.travelFeedRateMinute = None + + def addBaseLayer(self): + 'Add a base layer.' + baseLayerThickness = self.layerHeight * self.baseLayerThicknessOverLayerThickness + zCenter = self.extrusionTop + 0.5 * baseLayerThickness + z = zCenter + baseLayerThickness * self.repository.baseNozzleLiftOverBaseLayerThickness.value + if len(self.baseEndpoints) < 1: + print('This should never happen, the base layer has a size of zero.') + return + self.addLayerFromEndpoints( + self.baseEndpoints, + self.repository.baseFeedRateMultiplier.value, + self.repository.baseFlowRateMultiplier.value, + baseLayerThickness, + self.baseLayerThicknessOverLayerThickness, + self.baseStep, + z) + + def addBaseSegments(self, baseExtrusionWidth): + 'Add the base segments.' + baseOverhang = self.repository.infillOverhangOverExtrusionWidth.value * baseExtrusionWidth + self.baseEndpoints = getVerticalEndpoints(self.interfaceSegmentsTable, self.interfaceStep, baseOverhang, self.baseStep) + + def addEmptyLayerSupport( self, boundaryLayerIndex ): + 'Add support material to a layer if it is empty.' + supportLayer = SupportLayer([]) + self.supportLayers.append(supportLayer) + if len( self.boundaryLayers[ boundaryLayerIndex ].loops ) > 0: + return + aboveXIntersectionsTable = {} + euclidean.addXIntersectionsFromLoopsForTable( self.getInsetLoopsAbove(boundaryLayerIndex), aboveXIntersectionsTable, self.interfaceStep ) + belowXIntersectionsTable = {} + euclidean.addXIntersectionsFromLoopsForTable( self.getInsetLoopsBelow(boundaryLayerIndex), belowXIntersectionsTable, self.interfaceStep ) + supportLayer.xIntersectionsTable = euclidean.getIntersectionOfXIntersectionsTables( [ aboveXIntersectionsTable, belowXIntersectionsTable ] ) + + def addFlowRate(self, flowRate): + 'Add a flow rate value if different.' + if flowRate != None: + self.distanceFeedRate.addLine('M108 S' + euclidean.getFourSignificantFigures(flowRate)) + + def addInterfaceLayer(self): + 'Add an interface layer.' + interfaceLayerThickness = self.layerHeight * self.interfaceLayerThicknessOverLayerThickness + zCenter = self.extrusionTop + 0.5 * interfaceLayerThickness + z = zCenter + interfaceLayerThickness * self.repository.interfaceNozzleLiftOverInterfaceLayerThickness.value + if len(self.interfaceEndpoints) < 1: + print('This should never happen, the interface layer has a size of zero.') + return + self.addLayerFromEndpoints( + self.interfaceEndpoints, + self.repository.interfaceFeedRateMultiplier.value, + self.repository.interfaceFlowRateMultiplier.value, + interfaceLayerThickness, + self.interfaceLayerThicknessOverLayerThickness, + self.interfaceStep, + z) + + def addInterfaceTables(self, interfaceExtrusionWidth): + 'Add interface tables.' + overhang = self.repository.infillOverhangOverExtrusionWidth.value * interfaceExtrusionWidth + self.interfaceEndpoints = [] + self.interfaceIntersectionsTableKeys = self.interfaceIntersectionsTable.keys() + self.interfaceSegmentsTable = {} + for yKey in self.interfaceIntersectionsTableKeys: + self.interfaceIntersectionsTable[yKey].sort() + y = yKey * self.interfaceStep + lineSegments = euclidean.getSegmentsFromXIntersections(self.interfaceIntersectionsTable[yKey], y) + xIntersectionIndexList = [] + for lineSegmentIndex in xrange(len(lineSegments)): + lineSegment = lineSegments[lineSegmentIndex] + endpointBegin = lineSegment[0] + endpointEnd = lineSegment[1] + endpointBegin.point = complex(self.baseStep * math.floor(endpointBegin.point.real / self.baseStep) - overhang, y) + endpointEnd.point = complex(self.baseStep * math.ceil(endpointEnd.point.real / self.baseStep) + overhang, y) + if endpointEnd.point.real > endpointBegin.point.real: + euclidean.addXIntersectionIndexesFromSegment(lineSegmentIndex, lineSegment, xIntersectionIndexList) + xIntersections = euclidean.getJoinOfXIntersectionIndexes(xIntersectionIndexList) + joinedSegments = euclidean.getSegmentsFromXIntersections(xIntersections, y) + if len(joinedSegments) > 0: + self.interfaceSegmentsTable[yKey] = joinedSegments + for joinedSegment in joinedSegments: + self.interfaceEndpoints += joinedSegment + + def addLayerFromEndpoints( + self, + endpoints, + feedRateMultiplier, + flowRateMultiplier, + layerLayerThickness, + layerThicknessRatio, + step, + z): + 'Add a layer from endpoints and raise the extrusion top.' + layerThicknessRatioSquared = layerThicknessRatio * layerThicknessRatio + feedRateMinute = self.feedRateMinute * feedRateMultiplier / layerThicknessRatioSquared + if len(endpoints) < 1: + return + aroundPixelTable = {} + aroundWidth = 0.34321 * step + paths = euclidean.getPathsFromEndpoints(endpoints, 1.5 * step, aroundPixelTable, aroundWidth) + self.addLayerLine(z) + if self.oldFlowRate != None: + self.addFlowRate(flowRateMultiplier * self.oldFlowRate) + for path in paths: + simplifiedPath = euclidean.getSimplifiedPath(path, step) + self.distanceFeedRate.addGcodeFromFeedRateThreadZ(feedRateMinute, simplifiedPath, self.travelFeedRateMinute, z) + self.extrusionTop += layerLayerThickness + self.addFlowRate(self.oldFlowRate) + + def addLayerLine(self, z): + 'Add the layer gcode line and close the last layer gcode block.' + if self.layerStarted: + self.distanceFeedRate.addLine('()') + self.distanceFeedRate.addLine('( %s )' % self.distanceFeedRate.getRounded(z)) # Indicate that a new layer is starting. + if self.beginLoop != None: + zBegin = self.extrusionTop + self.layerHeight + intercircle.addOrbitsIfLarge(self.distanceFeedRate, self.beginLoop, self.orbitalFeedRatePerSecond, self.temperatureChangeTimeBeforeRaft, zBegin) + self.beginLoop = None + self.layerStarted = True + + def addOperatingOrbits(self, boundaryLoops, pointComplex, temperatureChangeTime, z): + 'Add the orbits before the operating layers.' + if len(boundaryLoops) < 1: + return + insetBoundaryLoops = intercircle.getInsetLoopsFromLoops(boundaryLoops, self.edgeWidth) + if len(insetBoundaryLoops) < 1: + insetBoundaryLoops = boundaryLoops + largestLoop = euclidean.getLargestLoop(insetBoundaryLoops) + if pointComplex != None: + largestLoop = euclidean.getLoopStartingClosest(self.edgeWidth, pointComplex, largestLoop) + intercircle.addOrbitsIfLarge(self.distanceFeedRate, largestLoop, self.orbitalFeedRatePerSecond, temperatureChangeTime, z) + + def addRaft(self): + 'Add the raft.' + if len(self.boundaryLayers) < 0: + print('this should never happen, there are no boundary layers in addRaft') + return + self.baseLayerThicknessOverLayerThickness = self.repository.baseLayerThicknessOverLayerThickness.value + baseExtrusionWidth = self.edgeWidth * self.baseLayerThicknessOverLayerThickness + self.baseStep = baseExtrusionWidth / self.repository.baseInfillDensity.value + self.interfaceLayerThicknessOverLayerThickness = self.repository.interfaceLayerThicknessOverLayerThickness.value + interfaceExtrusionWidth = self.edgeWidth * self.interfaceLayerThicknessOverLayerThickness + self.interfaceStep = interfaceExtrusionWidth / self.repository.interfaceInfillDensity.value + self.setCornersZ() + self.cornerMinimumComplex = self.cornerMinimum.dropAxis() + originalExtent = self.cornerMaximumComplex - self.cornerMinimumComplex + self.raftOutsetRadius = self.repository.raftMargin.value + self.repository.raftAdditionalMarginOverLengthPercent.value * 0.01 * max(originalExtent.real, originalExtent.imag) + self.setBoundaryLayers() + outsetSeparateLoops = intercircle.getInsetSeparateLoopsFromLoops(self.boundaryLayers[0].loops, -self.raftOutsetRadius, 0.8) + self.interfaceIntersectionsTable = {} + euclidean.addXIntersectionsFromLoopsForTable(outsetSeparateLoops, self.interfaceIntersectionsTable, self.interfaceStep) + if len(self.supportLayers) > 0: + supportIntersectionsTable = self.supportLayers[0].xIntersectionsTable + euclidean.joinXIntersectionsTables(supportIntersectionsTable, self.interfaceIntersectionsTable) + self.addInterfaceTables(interfaceExtrusionWidth) + self.addRaftPerimeters() + self.baseIntersectionsTable = {} + complexRadius = complex(self.raftOutsetRadius, self.raftOutsetRadius) + self.complexHigh = complexRadius + self.cornerMaximumComplex + self.complexLow = self.cornerMinimumComplex - complexRadius + self.beginLoop = euclidean.getSquareLoopWiddershins(self.cornerMinimumComplex, self.cornerMaximumComplex) + if not intercircle.orbitsAreLarge(self.beginLoop, self.temperatureChangeTimeBeforeRaft): + self.beginLoop = None + if self.repository.baseLayers.value > 0: + self.addTemperatureLineIfDifferent(self.baseTemperature) + self.addBaseSegments(baseExtrusionWidth) + for baseLayerIndex in xrange(self.repository.baseLayers.value): + self.addBaseLayer() + if self.repository.interfaceLayers.value > 0: + self.addTemperatureLineIfDifferent(self.interfaceTemperature) + self.interfaceIntersectionsTableKeys.sort() + for interfaceLayerIndex in xrange(self.repository.interfaceLayers.value): + self.addInterfaceLayer() + self.operatingJump = self.extrusionTop + self.layerHeight * self.repository.operatingNozzleLiftOverLayerThickness.value + for boundaryLayer in self.boundaryLayers: + if self.operatingJump != None: + boundaryLayer.z += self.operatingJump + if self.repository.baseLayers.value > 0 or self.repository.interfaceLayers.value > 0: + boundaryZ = self.boundaryLayers[0].z + if self.layerStarted: + self.distanceFeedRate.addLine('()') + self.layerStarted = False + self.distanceFeedRate.addLine('( )') + self.addLayerLine(boundaryZ) + temperatureChangeTimeBeforeFirstLayer = self.getTemperatureChangeTime(self.objectFirstLayerPerimeterTemperature) + self.addTemperatureLineIfDifferent(self.objectFirstLayerPerimeterTemperature) + largestOutsetLoop = intercircle.getLargestInsetLoopFromLoop(euclidean.getLargestLoop(outsetSeparateLoops), -self.raftOutsetRadius) + intercircle.addOrbitsIfLarge(self.distanceFeedRate, largestOutsetLoop, self.orbitalFeedRatePerSecond, temperatureChangeTimeBeforeFirstLayer, boundaryZ) + self.addLineLayerStart = False + + def addRaftedLine( self, splitLine ): + 'Add elevated gcode line with operating feed rate.' + self.oldLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + self.feedRateMinute = gcodec.getFeedRateMinute(self.feedRateMinute, splitLine) + z = self.oldLocation.z + if self.operatingJump != None: + z += self.operatingJump + temperature = self.objectNextLayersTemperature + if self.layerIndex == 0: + if self.isEdgePath: + temperature = self.objectFirstLayerPerimeterTemperature + else: + temperature = self.objectFirstLayerInfillTemperature + self.addTemperatureLineIfDifferent(temperature) + self.distanceFeedRate.addGcodeMovementZWithFeedRate(self.feedRateMinute, self.oldLocation.dropAxis(), z) + + def addRaftPerimeters(self): + 'Add raft edges if there is a raft.' + interfaceOutset = self.halfEdgeWidth * self.interfaceLayerThicknessOverLayerThickness + for supportLayer in self.supportLayers: + supportSegmentTable = supportLayer.supportSegmentTable + if len(supportSegmentTable) > 0: + outset = interfaceOutset + self.addRaftPerimetersByLoops(getLoopsBySegmentsDictionary(supportSegmentTable, self.interfaceStep), outset) + if self.repository.baseLayers.value < 1 and self.repository.interfaceLayers.value < 1: + return + overhangMultiplier = 1.0 + self.repository.infillOverhangOverExtrusionWidth.value + self.repository.infillOverhangOverExtrusionWidth.value + outset = self.halfEdgeWidth + if self.repository.interfaceLayers.value > 0: + outset = max(interfaceOutset * overhangMultiplier, outset) + if self.repository.baseLayers.value > 0: + outset = max(self.halfEdgeWidth * self.baseLayerThicknessOverLayerThickness * overhangMultiplier, outset) + self.addRaftPerimetersByLoops(getLoopsBySegmentsDictionary(self.interfaceSegmentsTable, self.interfaceStep), outset) + + def addRaftPerimetersByLoops(self, loops, outset): + 'Add raft edges to the gcode for loops.' + loops = intercircle.getInsetSeparateLoopsFromLoops(loops, -outset) + for loop in loops: + self.distanceFeedRate.addLine('()') + for point in loop: + roundedX = self.distanceFeedRate.getRounded(point.real) + roundedY = self.distanceFeedRate.getRounded(point.imag) + self.distanceFeedRate.addTagBracketedLine('raftPoint', 'X%s Y%s' % (roundedX, roundedY)) + self.distanceFeedRate.addLine('()') + + def addSegmentTablesToSupportLayers(self): + 'Add segment tables to the support layers.' + for supportLayer in self.supportLayers: + supportLayer.supportSegmentTable = {} + xIntersectionsTable = supportLayer.xIntersectionsTable + for xIntersectionsTableKey in xIntersectionsTable: + y = xIntersectionsTableKey * self.interfaceStep + supportLayer.supportSegmentTable[ xIntersectionsTableKey ] = euclidean.getSegmentsFromXIntersections( xIntersectionsTable[ xIntersectionsTableKey ], y ) + + def addSupportLayerTemperature(self, endpoints, z): + 'Add support layer and temperature before the object layer.' + self.distanceFeedRate.addLine('()') + self.distanceFeedRate.addLinesSetAbsoluteDistanceMode(self.supportStartLines) + self.addTemperatureOrbits(endpoints, self.supportedLayersTemperature, z) + aroundPixelTable = {} + aroundWidth = 0.34321 * self.interfaceStep + boundaryLoops = self.boundaryLayers[self.layerIndex].loops + halfSupportOutset = 0.5 * self.supportOutset + aroundBoundaryLoops = intercircle.getAroundsFromLoops(boundaryLoops, halfSupportOutset) + for aroundBoundaryLoop in aroundBoundaryLoops: + euclidean.addLoopToPixelTable(aroundBoundaryLoop, aroundPixelTable, aroundWidth) + paths = euclidean.getPathsFromEndpoints(endpoints, 1.5 * self.interfaceStep, aroundPixelTable, aroundWidth) + feedRateMinuteMultiplied = self.operatingFeedRateMinute + supportFlowRateMultiplied = self.supportFlowRate + if self.layerIndex == 0: + feedRateMinuteMultiplied *= self.objectFirstLayerFeedRateInfillMultiplier + if supportFlowRateMultiplied != None: + supportFlowRateMultiplied *= self.objectFirstLayerFlowRateInfillMultiplier + self.addFlowRate(supportFlowRateMultiplied) + for path in paths: + self.distanceFeedRate.addGcodeFromFeedRateThreadZ(feedRateMinuteMultiplied, path, self.travelFeedRateMinute, z) + self.addFlowRate(self.oldFlowRate) + self.addTemperatureOrbits(endpoints, self.supportLayersTemperature, z) + self.distanceFeedRate.addLinesSetAbsoluteDistanceMode(self.supportEndLines) + self.distanceFeedRate.addLine('()') + + def addSupportSegmentTable( self, layerIndex ): + 'Add support segments from the boundary layers.' + aboveLayer = self.boundaryLayers[ layerIndex + 1 ] + aboveLoops = aboveLayer.loops + supportLayer = self.supportLayers[layerIndex] + if len( aboveLoops ) < 1: + return + boundaryLayer = self.boundaryLayers[layerIndex] + rise = aboveLayer.z - boundaryLayer.z + outsetSupportLoops = intercircle.getInsetSeparateLoopsFromLoops(boundaryLayer.loops, -self.minimumSupportRatio * rise) + numberOfSubSteps = 4 + subStepSize = self.interfaceStep / float( numberOfSubSteps ) + aboveIntersectionsTable = {} + euclidean.addXIntersectionsFromLoopsForTable( aboveLoops, aboveIntersectionsTable, subStepSize ) + outsetIntersectionsTable = {} + euclidean.addXIntersectionsFromLoopsForTable( outsetSupportLoops, outsetIntersectionsTable, subStepSize ) + euclidean.subtractXIntersectionsTable( aboveIntersectionsTable, outsetIntersectionsTable ) + for aboveIntersectionsTableKey in aboveIntersectionsTable.keys(): + supportIntersectionsTableKey = int( round( float( aboveIntersectionsTableKey ) / numberOfSubSteps ) ) + xIntersectionIndexList = [] + if supportIntersectionsTableKey in supportLayer.xIntersectionsTable: + euclidean.addXIntersectionIndexesFromXIntersections( 0, xIntersectionIndexList, supportLayer.xIntersectionsTable[ supportIntersectionsTableKey ] ) + euclidean.addXIntersectionIndexesFromXIntersections( 1, xIntersectionIndexList, aboveIntersectionsTable[ aboveIntersectionsTableKey ] ) + supportLayer.xIntersectionsTable[ supportIntersectionsTableKey ] = euclidean.getJoinOfXIntersectionIndexes( xIntersectionIndexList ) + + def addTemperatureLineIfDifferent(self, temperature): + 'Add a line of temperature if different.' + if temperature == None: + return + temperatureOutputString = euclidean.getRoundedToThreePlaces(temperature) + if temperatureOutputString == self.oldTemperatureOutputString: + return + if temperatureOutputString != None: + self.distanceFeedRate.addLine('M104 S' + temperatureOutputString) # Set temperature. + self.oldTemperatureOutputString = temperatureOutputString + + def addTemperatureOrbits( self, endpoints, temperature, z ): + 'Add the temperature and orbits around the support layer.' + if self.layerIndex < 0: + return + boundaryLoops = self.boundaryLayers[self.layerIndex].loops + temperatureTimeChange = self.getTemperatureChangeTime( temperature ) + self.addTemperatureLineIfDifferent( temperature ) + if len( boundaryLoops ) < 1: + layerCornerHigh = complex(-987654321.0, -987654321.0) + layerCornerLow = complex(987654321.0, 987654321.0) + for endpoint in endpoints: + layerCornerHigh = euclidean.getMaximum( layerCornerHigh, endpoint.point ) + layerCornerLow = euclidean.getMinimum( layerCornerLow, endpoint.point ) + squareLoop = euclidean.getSquareLoopWiddershins( layerCornerLow, layerCornerHigh ) + intercircle.addOrbitsIfLarge( self.distanceFeedRate, squareLoop, self.orbitalFeedRatePerSecond, temperatureTimeChange, z ) + return + edgeInset = 0.4 * self.edgeWidth + insetBoundaryLoops = intercircle.getInsetLoopsFromLoops(boundaryLoops, edgeInset) + if len( insetBoundaryLoops ) < 1: + insetBoundaryLoops = boundaryLoops + largestLoop = euclidean.getLargestLoop( insetBoundaryLoops ) + intercircle.addOrbitsIfLarge( self.distanceFeedRate, largestLoop, self.orbitalFeedRatePerSecond, temperatureTimeChange, z ) + + def addToFillXIntersectionIndexTables( self, supportLayer ): + 'Add fill segments from the boundary layers.' + supportLoops = supportLayer.supportLoops + supportLayer.fillXIntersectionsTable = {} + if len(supportLoops) < 1: + return + euclidean.addXIntersectionsFromLoopsForTable( supportLoops, supportLayer.fillXIntersectionsTable, self.interfaceStep ) + + def extendXIntersections( self, loops, radius, xIntersectionsTable ): + 'Extend the support segments.' + xIntersectionsTableKeys = xIntersectionsTable.keys() + for xIntersectionsTableKey in xIntersectionsTableKeys: + lineSegments = euclidean.getSegmentsFromXIntersections( xIntersectionsTable[ xIntersectionsTableKey ], xIntersectionsTableKey ) + xIntersectionIndexList = [] + loopXIntersections = [] + euclidean.addXIntersectionsFromLoops( loops, loopXIntersections, xIntersectionsTableKey ) + for lineSegmentIndex in xrange( len( lineSegments ) ): + lineSegment = lineSegments[ lineSegmentIndex ] + extendedLineSegment = getExtendedLineSegment( radius, lineSegment, loopXIntersections ) + if extendedLineSegment != None: + euclidean.addXIntersectionIndexesFromSegment( lineSegmentIndex, extendedLineSegment, xIntersectionIndexList ) + xIntersections = euclidean.getJoinOfXIntersectionIndexes( xIntersectionIndexList ) + if len( xIntersections ) > 0: + xIntersectionsTable[ xIntersectionsTableKey ] = xIntersections + else: + del xIntersectionsTable[ xIntersectionsTableKey ] + + def getCraftedGcode(self, gcodeText, repository): + 'Parse gcode text and store the raft gcode.' + self.repository = repository + self.minimumSupportRatio = math.tan( math.radians( repository.supportMinimumAngle.value ) ) + self.supportEndLines = settings.getAlterationFileLines(repository.nameOfSupportEndFile.value) + self.supportStartLines = settings.getAlterationFileLines(repository.nameOfSupportStartFile.value) + self.lines = archive.getTextLines(gcodeText) + self.parseInitialization() + self.temperatureChangeTimeBeforeRaft = 0.0 + if self.repository.initialCircling.value: + maxBaseInterfaceTemperature = max(self.baseTemperature, self.interfaceTemperature) + firstMaxTemperature = max(maxBaseInterfaceTemperature, self.objectFirstLayerPerimeterTemperature) + self.temperatureChangeTimeBeforeRaft = self.getTemperatureChangeTime(firstMaxTemperature) + if repository.addRaftElevateNozzleOrbitSetAltitude.value: + self.addRaft() + self.addTemperatureLineIfDifferent( self.objectFirstLayerPerimeterTemperature ) + for line in self.lines[self.lineIndex :]: + self.parseLine(line) + return gcodec.getGcodeWithoutDuplication('M108', self.distanceFeedRate.output.getvalue()) + + def getElevatedBoundaryLine( self, splitLine ): + 'Get elevated boundary gcode line.' + location = gcodec.getLocationFromSplitLine(None, splitLine) + if self.operatingJump != None: + location.z += self.operatingJump + return self.distanceFeedRate.getBoundaryLine( location ) + + def getInsetLoops( self, boundaryLayerIndex ): + 'Inset the support loops if they are not already inset.' + if boundaryLayerIndex not in self.insetTable: + self.insetTable[ boundaryLayerIndex ] = intercircle.getInsetSeparateLoopsFromLoops(self.boundaryLayers[ boundaryLayerIndex ].loops, self.quarterEdgeWidth) + return self.insetTable[ boundaryLayerIndex ] + + def getInsetLoopsAbove( self, boundaryLayerIndex ): + 'Get the inset loops above the boundary layer index.' + for aboveLayerIndex in xrange( boundaryLayerIndex + 1, len(self.boundaryLayers) ): + if len( self.boundaryLayers[ aboveLayerIndex ].loops ) > 0: + return self.getInsetLoops( aboveLayerIndex ) + return [] + + def getInsetLoopsBelow( self, boundaryLayerIndex ): + 'Get the inset loops below the boundary layer index.' + for belowLayerIndex in xrange( boundaryLayerIndex - 1, - 1, - 1 ): + if len( self.boundaryLayers[ belowLayerIndex ].loops ) > 0: + return self.getInsetLoops( belowLayerIndex ) + return [] + + def getStepsUntilEnd( self, begin, end, stepSize ): + 'Get steps from the beginning until the end.' + step = begin + steps = [] + while step < end: + steps.append( step ) + step += stepSize + return steps + + def getSupportEndpoints(self): + 'Get the support layer segments.' + if len(self.supportLayers) <= self.layerIndex: + return [] + supportSegmentTable = self.supportLayers[self.layerIndex].supportSegmentTable + if self.layerIndex % 2 == 1 and self.repository.supportCrossHatch.value: + return getVerticalEndpoints(supportSegmentTable, self.interfaceStep, 0.1 * self.edgeWidth, self.interfaceStep) + return euclidean.getEndpointsFromSegmentTable(supportSegmentTable) + + def getTemperatureChangeTime( self, temperature ): + 'Get the temperature change time.' + if temperature == None: + return 0.0 + oldTemperature = 25.0 # typical chamber temperature + if self.oldTemperatureOutputString != None: + oldTemperature = float( self.oldTemperatureOutputString ) + if temperature == oldTemperature: + return 0.0 + if temperature > oldTemperature: + return ( temperature - oldTemperature ) / self.heatingRate + return ( oldTemperature - temperature ) / abs( self.coolingRate ) + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '(': + self.baseTemperature = float(splitLine[1]) + elif firstWord == '(': + self.coolingRate = float(splitLine[1]) + elif firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('raft') + elif firstWord == '(': + self.heatingRate = float(splitLine[1]) + elif firstWord == '(': + self.interfaceTemperature = float(splitLine[1]) + elif firstWord == '(': + return + elif firstWord == '(': + self.layerHeight = float(splitLine[1]) + elif firstWord == '(': + self.objectFirstLayerFeedRateInfillMultiplier = float(splitLine[1]) + elif firstWord == '(': + self.objectFirstLayerFlowRateInfillMultiplier = float(splitLine[1]) + elif firstWord == '(': + self.objectFirstLayerInfillTemperature = float(splitLine[1]) + elif firstWord == '(': + self.objectFirstLayerPerimeterTemperature = float(splitLine[1]) + elif firstWord == '(': + self.objectNextLayersTemperature = float(splitLine[1]) + elif firstWord == '(': + self.orbitalFeedRatePerSecond = float(splitLine[1]) + elif firstWord == '(': + self.operatingFeedRateMinute = 60.0 * float(splitLine[1]) + self.feedRateMinute = self.operatingFeedRateMinute + elif firstWord == '(': + self.oldFlowRate = float(splitLine[1]) + self.supportFlowRate = self.oldFlowRate * self.repository.supportFlowRateOverOperatingFlowRate.value + elif firstWord == '(': + self.edgeWidth = float(splitLine[1]) + self.halfEdgeWidth = 0.5 * self.edgeWidth + self.quarterEdgeWidth = 0.25 * self.edgeWidth + self.supportOutset = self.edgeWidth + self.edgeWidth * self.repository.supportGapOverPerimeterExtrusionWidth.value + elif firstWord == '(': + self.supportLayersTemperature = float(splitLine[1]) + elif firstWord == '(': + self.supportedLayersTemperature = float(splitLine[1]) + elif firstWord == '(': + self.travelFeedRateMinute = 60.0 * float(splitLine[1]) + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + 'Parse a gcode line and add it to the raft skein.' + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + if self.extrusionStart: + self.addRaftedLine(splitLine) + return + elif firstWord == 'M101': + if self.isStartupEarly: + self.isStartupEarly = False + return + elif firstWord == 'M108': + self.oldFlowRate = float(splitLine[1][1 :]) + elif firstWord == '(': + line = self.getElevatedBoundaryLine(splitLine) + elif firstWord == '()': + self.extrusionStart = False + self.distanceFeedRate.addLine( self.operatingLayerEndLine ) + elif firstWord == '(': + self.layerIndex += 1 + settings.printProgress(self.layerIndex, 'raft') + boundaryLayer = None + layerZ = self.extrusionTop + float(splitLine[1]) + if len(self.boundaryLayers) > 0: + boundaryLayer = self.boundaryLayers[self.layerIndex] + layerZ = boundaryLayer.z + if self.operatingJump != None: + line = '( %s )' % self.distanceFeedRate.getRounded( layerZ ) + if self.layerStarted and self.addLineLayerStart: + self.distanceFeedRate.addLine('()') + self.layerStarted = False + if self.layerIndex > len(self.supportLayers) + 1: + self.distanceFeedRate.addLine( self.operatingLayerEndLine ) + self.operatingLayerEndLine = '' + if self.addLineLayerStart: + self.distanceFeedRate.addLine(line) + self.addLineLayerStart = True + line = '' + endpoints = self.getSupportEndpoints() + if self.layerIndex == 1: + if len(endpoints) < 1: + temperatureChangeTimeBeforeNextLayers = self.getTemperatureChangeTime( self.objectNextLayersTemperature ) + self.addTemperatureLineIfDifferent( self.objectNextLayersTemperature ) + if self.repository.addRaftElevateNozzleOrbitSetAltitude.value and len( boundaryLayer.loops ) > 0: + self.addOperatingOrbits( boundaryLayer.loops, euclidean.getXYComplexFromVector3( self.oldLocation ), temperatureChangeTimeBeforeNextLayers, layerZ ) + if len(endpoints) > 0: + self.addSupportLayerTemperature( endpoints, layerZ ) + elif firstWord == '(' or firstWord == '()': + self.isEdgePath = True + elif firstWord == '()' or firstWord == '()': + self.isEdgePath = False + self.distanceFeedRate.addLine(line) + + def setBoundaryLayers(self): + 'Set the boundary layers.' + if self.repository.supportChoiceNone.value: + return + if len(self.boundaryLayers) < 2: + return + if self.repository.supportChoiceEmptyLayersOnly.value: + supportLayer = SupportLayer([]) + self.supportLayers.append(supportLayer) + for boundaryLayerIndex in xrange(1, len(self.boundaryLayers) -1): + self.addEmptyLayerSupport(boundaryLayerIndex) + self.truncateSupportSegmentTables() + self.addSegmentTablesToSupportLayers() + return + for boundaryLayer in self.boundaryLayers: + # thresholdRadius of 0.8 is needed to avoid the ripple inset bug http://hydraraptor.blogspot.com/2010/12/crackers.html + supportLoops = intercircle.getInsetSeparateLoopsFromLoops(boundaryLayer.loops, -self.supportOutset, 0.8) + supportLayer = SupportLayer(supportLoops) + self.supportLayers.append(supportLayer) + for supportLayerIndex in xrange(len(self.supportLayers) - 1): + self.addSupportSegmentTable(supportLayerIndex) + self.truncateSupportSegmentTables() + for supportLayerIndex in xrange(len(self.supportLayers) - 1): + boundaryLoops = self.boundaryLayers[supportLayerIndex].loops + self.extendXIntersections( boundaryLoops, self.supportOutset, self.supportLayers[supportLayerIndex].xIntersectionsTable) + for supportLayer in self.supportLayers: + self.addToFillXIntersectionIndexTables(supportLayer) + if self.repository.supportChoiceExteriorOnly.value: + for supportLayerIndex in xrange(1, len(self.supportLayers)): + self.subtractJoinedFill(supportLayerIndex) + for supportLayer in self.supportLayers: + euclidean.subtractXIntersectionsTable(supportLayer.xIntersectionsTable, supportLayer.fillXIntersectionsTable) + for supportLayerIndex in xrange(len(self.supportLayers) - 2, -1, -1): + xIntersectionsTable = self.supportLayers[supportLayerIndex].xIntersectionsTable + aboveXIntersectionsTable = self.supportLayers[supportLayerIndex + 1].xIntersectionsTable + euclidean.joinXIntersectionsTables(aboveXIntersectionsTable, xIntersectionsTable) + for supportLayerIndex in xrange(len(self.supportLayers)): + supportLayer = self.supportLayers[supportLayerIndex] + self.extendXIntersections(supportLayer.supportLoops, self.raftOutsetRadius, supportLayer.xIntersectionsTable) + for supportLayer in self.supportLayers: + euclidean.subtractXIntersectionsTable(supportLayer.xIntersectionsTable, supportLayer.fillXIntersectionsTable) + self.addSegmentTablesToSupportLayers() + + def setCornersZ(self): + 'Set maximum and minimum corners and z.' + boundaryLoop = None + boundaryLayer = None + layerIndex = - 1 + self.cornerMaximumComplex = complex(-912345678.0, -912345678.0) + self.cornerMinimum = Vector3(912345678.0, 912345678.0, 912345678.0) + self.firstLayerLoops = [] + for line in self.lines[self.lineIndex :]: + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == '()': + boundaryLoop = None + elif firstWord == '(': + location = gcodec.getLocationFromSplitLine(None, splitLine) + if boundaryLoop == None: + boundaryLoop = [] + boundaryLayer.loops.append(boundaryLoop) + boundaryLoop.append(location.dropAxis()) + self.cornerMaximumComplex = euclidean.getMaximum(self.cornerMaximumComplex, location.dropAxis()) + self.cornerMinimum.minimize(location) + elif firstWord == '(': + z = float(splitLine[1]) + boundaryLayer = euclidean.LoopLayer(z) + self.boundaryLayers.append(boundaryLayer) + elif firstWord == '(': + layerIndex += 1 + if self.repository.supportChoiceNone.value: + if layerIndex > 1: + return + + def subtractJoinedFill( self, supportLayerIndex ): + 'Join the fill then subtract it from the support layer table.' + supportLayer = self.supportLayers[supportLayerIndex] + fillXIntersectionsTable = supportLayer.fillXIntersectionsTable + belowFillXIntersectionsTable = self.supportLayers[ supportLayerIndex - 1 ].fillXIntersectionsTable + euclidean.joinXIntersectionsTables( belowFillXIntersectionsTable, supportLayer.fillXIntersectionsTable ) + euclidean.subtractXIntersectionsTable( supportLayer.xIntersectionsTable, supportLayer.fillXIntersectionsTable ) + + def truncateSupportSegmentTables(self): + 'Truncate the support segments after the last support segment which contains elements.' + for supportLayerIndex in xrange( len(self.supportLayers) - 1, - 1, - 1 ): + if len( self.supportLayers[supportLayerIndex].xIntersectionsTable ) > 0: + self.supportLayers = self.supportLayers[ : supportLayerIndex + 1 ] + return + self.supportLayers = [] + + +class SupportLayer: + 'Support loops with segment tables.' + def __init__( self, supportLoops ): + self.supportLoops = supportLoops + self.supportSegmentTable = {} + self.xIntersectionsTable = {} + + def __repr__(self): + 'Get the string representation of this loop layer.' + return '%s' % ( self.supportLoops ) + + +def main(): + 'Display the raft dialog.' + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/scale.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/scale.py new file mode 100644 index 0000000..c3acb7c --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/scale.py @@ -0,0 +1,164 @@ +#! /usr/bin/env python +""" +This page is in the table of contents. +Scale scales the carving to compensate for shrinkage after the extrusion has cooled. + +The scale manual page is at: + +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Scale + +It is best to only change the XY Plane Scale, because that does not affect other variables. If you choose to change the Z Axis Scale, that increases the layer height so you must increase the feed rate in speed by the same amount and maybe some other variables which depend on layer height. + +==Operation== +The default 'Activate Scale' checkbox is off. When it is on, the functions described below will work, when it is off, nothing will be done. + +==Settings== +===XY Plane Scale=== +Default is 1.01. + +Defines the amount the xy plane of the carving will be scaled. The xy coordinates will be scaled, but the edge width is not changed, so this can be changed without affecting other variables. + +===Z Axis Scale=== +Default is one. + +Defines the amount the z axis of the carving will be scaled. The default is one because changing this changes many variables related to the layer height. For example, the feedRate should be multiplied by the Z Axis Scale because the layers would be farther apart. + +===SVG Viewer=== +Default is webbrowser. + +If the 'SVG Viewer' is set to the default 'webbrowser', the scalable vector graphics file will be sent to the default browser to be opened. If the 'SVG Viewer' is set to a program name, the scalable vector graphics file will be sent to that program to be opened. + +==Examples== +The following examples scale the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and scale.py. + +> python scale.py +This brings up the scale dialog. + +> python scale.py Screw Holder Bottom.stl +The scale tool is parsing the file: +Screw Holder Bottom.stl +.. +The scale tool has created the file: +.. Screw Holder Bottom_scale.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from datetime import date +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities.svg_reader import SVGReader +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from fabmetheus_utilities import svg_writer +from fabmetheus_utilities import xml_simple_writer +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import cStringIO +import os +import sys +import time + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCraftedText(fileName, svgText='', repository=None): + "Scale and convert an svg file or svgText." + return getCraftedTextFromText(fileName, archive.getTextIfEmpty(fileName, svgText), repository) + +def getCraftedTextFromText(fileName, svgText, repository=None): + "Scale and convert an svgText." + if gcodec.isProcedureDoneOrFileIsEmpty(svgText, 'scale'): + return svgText + if repository == None: + repository = settings.getReadRepository(ScaleRepository()) + if repository.activateScale.value: + return ScaleSkein().getCraftedGcode(fileName, repository, svgText) + return svgText + +def getNewRepository(): + 'Get new repository.' + return ScaleRepository() + +def setLoopLayerScale(loopLayer, xyPlaneScale, zAxisScale): + "Set the slice element scale." + for loop in loopLayer.loops: + for pointIndex in xrange(len(loop)): + loop[pointIndex] *= xyPlaneScale + loopLayer.z *= zAxisScale + +def writeOutput(fileName, shouldAnalyze=True): + 'Scale the carving.' + skeinforge_craft.writeSVGTextWithNounMessage(fileName, ScaleRepository(), shouldAnalyze) + + +class ScaleRepository: + "A class to handle the scale settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.scale.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName(fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Scale', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Scale') + self.activateScale = settings.BooleanSetting().getFromValue('Activate Scale', self, False) + self.xyPlaneScale = settings.FloatSpin().getFromValue(0.99, 'XY Plane Scale (ratio):', self, 1.03, 1.01) + self.zAxisScale = settings.FloatSpin().getFromValue(0.99, 'Z Axis Scale (ratio):', self, 1.02, 1.0) + self.svgViewer = settings.StringSetting().getFromValue('SVG Viewer:', self, 'webbrowser') + self.executeTitle = 'Scale' + + def execute(self): + "Scale button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class ScaleSkein: + "A class to scale a skein of extrusions." + def getCraftedGcode(self, fileName, repository, svgText): + "Parse svgText and store the scale svgText." + svgReader = SVGReader() + svgReader.parseSVG('', svgText) + if svgReader.sliceDictionary == None: + print('Warning, nothing will be done because the sliceDictionary could not be found getCraftedGcode in preface.') + return '' + xyPlaneScale = repository.xyPlaneScale.value + zAxisScale = repository.zAxisScale.value + decimalPlacesCarried = int(svgReader.sliceDictionary['decimalPlacesCarried']) + layerHeight = zAxisScale * float(svgReader.sliceDictionary['layerHeight']) + edgeWidth = float(svgReader.sliceDictionary['edgeWidth']) + loopLayers = svgReader.loopLayers + for loopLayer in loopLayers: + setLoopLayerScale(loopLayer, xyPlaneScale, zAxisScale) + cornerMaximum = Vector3(-912345678.0, -912345678.0, -912345678.0) + cornerMinimum = Vector3(912345678.0, 912345678.0, 912345678.0) + svg_writer.setSVGCarvingCorners(cornerMaximum, cornerMinimum, layerHeight, loopLayers) + svgWriter = svg_writer.SVGWriter( + True, + cornerMaximum, + cornerMinimum, + decimalPlacesCarried, + layerHeight, + edgeWidth) + commentElement = svg_writer.getCommentElement(svgReader.documentElement) + procedureNameString = svgReader.sliceDictionary['procedureName'] + ',scale' + return svgWriter.getReplacedSVGTemplate(fileName, loopLayers, procedureNameString, commentElement) + + +def main(): + "Display the scale dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/skin.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/skin.py new file mode 100644 index 0000000..37e0841 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/skin.py @@ -0,0 +1,392 @@ +""" +This page is in the table of contents. +Skin is a plugin to smooth the surface skin of an object by replacing the edge surface with a surface printed at a fraction of the carve +height. This gives the impression that the object was carved at a much thinner height giving a high-quality finish, but still prints +in a relatively short time. The latest process has some similarities with a description at: + +http://adventuresin3-dprinting.blogspot.com/2011/05/skinning.html + +The skin manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Skin + +==Operation== +The default 'Activate Skin' checkbox is off. When it is on, the functions described below will work, when it is off, nothing will be done. + +==Settings== +===Division=== +====Horizontal Infill Divisions==== +Default: 2 + +Defines the number of times the skinned infill is divided horizontally. + +====Horizontal Perimeter Divisions==== +Default: 1 + +Defines the number of times the skinned edges are divided horizontally. + +====Vertical Divisions==== +Default: 2 + +Defines the number of times the skinned infill and edges are divided vertically. + +===Hop When Extruding Infill=== +Default is off. + +When selected, the extruder will hop before and after extruding the lower infill in order to avoid the regular thickness threads. + +===Layers From=== +Default: 1 + +Defines which layer of the print the skinning process starts from. It is not wise to set this to zero, skinning the bottom layer is likely to cause the bottom edge not to adhere well to the print surface. + +==Tips== +Due to the very small Z-axis moves skinning can generate as it prints the edge, it can cause the Z-axis speed to be limited by the Limit plug-in, if you have it enabled. This can cause some printers to pause excessively during each layer change. To overcome this, ensure that the Z-axis max speed in the Limit tool is set to an appropriate value for your printer, e.g. 10mm/s + +Since Skin prints a number of fractional-height edge layers for each layer, printing the edge last causes the print head to travel down from the current print height. Depending on the shape of your extruder nozzle, you may get higher quality prints if you print the edges first, so the print head always travels up. This is set via the Thread Sequence Choice setting in the Fill tool. + +==Examples== +The following examples skin the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and skin.py. + +> python skin.py +This brings up the skin dialog. + +> python skin.py Screw Holder Bottom.stl +The skin tool is parsing the file: +Screw Holder Bottom.stl +.. +The skin tool has created the file: +.. Screw Holder Bottom_skin.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import sys + + +__author__ = 'Enrique Perez (perez_enrique aht yahoo.com) & James Blackwell (jim_blag ahht hotmail.com)' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCraftedText(fileName, gcodeText, repository=None): + 'Skin a gcode linear move text.' + return getCraftedTextFromText(archive.getTextIfEmpty(fileName, gcodeText), repository) + +def getCraftedTextFromText(gcodeText, repository=None): + 'Skin a gcode linear move text.' + if gcodec.isProcedureDoneOrFileIsEmpty(gcodeText, 'skin'): + return gcodeText + if repository == None: + repository = settings.getReadRepository(SkinRepository()) + if not repository.activateSkin.value: + return gcodeText + return SkinSkein().getCraftedGcode(gcodeText, repository) + +def getIsMinimumSides(loops, sides=3): + 'Determine if all the loops have at least the given number of sides.' + for loop in loops: + if len(loop) < sides: + return False + return True + +def getNewRepository(): + 'Get new repository.' + return SkinRepository() + +def writeOutput(fileName, shouldAnalyze=True): + 'Skin a gcode linear move file. Chain skin the gcode if it is not already skinned.' + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'skin', shouldAnalyze) + + +class SkinRepository: + 'A class to handle the skin settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.skin.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Skin', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Skin') + self.activateSkin = settings.BooleanSetting().getFromValue('Activate Skin', self, False) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Division -', self) + self.horizontalInfillDivisions = settings.IntSpin().getSingleIncrementFromValue(1, 'Horizontal Infill Divisions (integer):', self, 3, 2) + self.horizontalPerimeterDivisions = settings.IntSpin().getSingleIncrementFromValue(1, 'Horizontal Perimeter Divisions (integer):', self, 3, 1) + self.verticalDivisions = settings.IntSpin().getSingleIncrementFromValue(1, 'Vertical Divisions (integer):', self, 3, 2) + settings.LabelSeparator().getFromRepository(self) + self.hopWhenExtrudingInfill = settings.BooleanSetting().getFromValue('Hop When Extruding Infill', self, False) + self.layersFrom = settings.IntSpin().getSingleIncrementFromValue(0, 'Layers From (index):', self, 912345678, 1) + self.executeTitle = 'Skin' + + def execute(self): + 'Skin button has been clicked.' + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class SkinSkein: + 'A class to skin a skein of extrusions.' + def __init__(self): + 'Initialize.' + self.clipOverEdgeWidth = 0.0 + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.edge = None + self.feedRateMinute = 959.0 + self.infill = None + self.infillBoundaries = None + self.infillBoundary = None + self.layerIndex = -1 + self.lineIndex = 0 + self.lines = None + self.maximumZFeedRateMinute = 60.0 + self.oldFlowRate = None + self.oldLocation = None + self.travelFeedRateMinute = 957.0 + + def addFlowRateLine(self, flowRate): + 'Add a flow rate line.' + self.distanceFeedRate.addLine('M108 S' + euclidean.getFourSignificantFigures(flowRate)) + + def addPerimeterLoop(self, thread, z): + 'Add the edge loop to the gcode.' + self.distanceFeedRate.addGcodeFromFeedRateThreadZ(self.feedRateMinute, thread, self.travelFeedRateMinute, z) + + def addSkinnedInfill(self): + 'Add skinned infill.' + if self.infillBoundaries == None: + return + bottomZ = self.oldLocation.z + self.layerHeight / self.verticalDivisionsFloat - self.layerHeight + offsetY = 0.5 * self.skinInfillWidth + self.addFlowRateLine(self.oldFlowRate / self.verticalDivisionsFloat / self.horizontalInfillDivisionsFloat) + for verticalDivisionIndex in xrange(self.verticalDivisions): + z = bottomZ + self.layerHeight / self.verticalDivisionsFloat * float(verticalDivisionIndex) + self.addSkinnedInfillBoundary(self.infillBoundaries, offsetY * (verticalDivisionIndex % 2 == 0), self.oldLocation.z, z) + self.addFlowRateLine(self.oldFlowRate) + self.infillBoundaries = None + + def addSkinnedInfillBoundary(self, infillBoundaries, offsetY, upperZ, z): + 'Add skinned infill boundary.' + arounds = [] + aroundWidth = 0.34321 * self.skinInfillInset + endpoints = [] + pixelTable = {} + rotatedLoops = [] + for infillBoundary in infillBoundaries: + infillBoundaryRotated = euclidean.getRotatedComplexes(self.reverseRotation, infillBoundary) + if offsetY != 0.0: + for infillPointRotatedIndex, infillPointRotated in enumerate(infillBoundaryRotated): + infillBoundaryRotated[infillPointRotatedIndex] = complex(infillPointRotated.real, infillPointRotated.imag - offsetY) + rotatedLoops.append(infillBoundaryRotated) + infillDictionary = triangle_mesh.getInfillDictionary( + arounds, aroundWidth, self.skinInfillInset, self.skinInfillWidth, pixelTable, rotatedLoops) + for infillDictionaryKey in infillDictionary.keys(): + xIntersections = infillDictionary[infillDictionaryKey] + xIntersections.sort() + for segment in euclidean.getSegmentsFromXIntersections(xIntersections, infillDictionaryKey * self.skinInfillWidth): + for endpoint in segment: + endpoint.point = complex(endpoint.point.real, endpoint.point.imag + offsetY) + endpoints.append(endpoint) + infillPaths = euclidean.getPathsFromEndpoints(endpoints, 5.0 * self.skinInfillWidth, pixelTable, aroundWidth) + for infillPath in infillPaths: + addPointBeforeThread = True + infillRotated = euclidean.getRotatedComplexes(self.rotation, infillPath) + if upperZ > z and self.repository.hopWhenExtrudingInfill.value: + feedRateMinute = self.travelFeedRateMinute + infillRotatedFirst = infillRotated[0] + location = Vector3(infillRotatedFirst.real, infillRotatedFirst.imag, upperZ) + distance = abs(location - self.oldLocation) + if distance > 0.0: + deltaZ = abs(upperZ - self.oldLocation.z) + zFeedRateComponent = feedRateMinute * deltaZ / distance + if zFeedRateComponent > self.maximumZFeedRateMinute: + feedRateMinute *= self.maximumZFeedRateMinute / zFeedRateComponent + self.distanceFeedRate.addGcodeMovementZWithFeedRate(feedRateMinute, infillRotatedFirst, upperZ) + self.distanceFeedRate.addGcodeMovementZWithFeedRate(self.maximumZFeedRateMinute, infillRotatedFirst, z) + addPointBeforeThread = False + if addPointBeforeThread: + self.distanceFeedRate.addGcodeMovementZ(infillRotated[0], z) + self.distanceFeedRate.addLine('M101') + for point in infillRotated[1 :]: + self.distanceFeedRate.addGcodeMovementZ(point, z) + self.distanceFeedRate.addLine('M103') + lastPointRotated = infillRotated[-1] + self.oldLocation = Vector3(lastPointRotated.real, lastPointRotated.imag, upperZ) + if upperZ > z and self.repository.hopWhenExtrudingInfill.value: + self.distanceFeedRate.addGcodeMovementZWithFeedRate(self.maximumZFeedRateMinute, lastPointRotated, upperZ) + + def addSkinnedPerimeter(self): + 'Add skinned edge.' + if self.edge == None: + return + bottomZ = self.oldLocation.z + self.layerHeight / self.verticalDivisionsFloat - self.layerHeight + edgeThread = self.edge[: -1] + edges = [] + radiusAddition = self.edgeWidth / self.horizontalPerimeterDivisionsFloat + radius = 0.5 * radiusAddition - self.halfEdgeWidth + for division in xrange(self.repository.horizontalPerimeterDivisions.value): + edges.append(self.getClippedSimplifiedLoopPathByLoop(intercircle.getLargestInsetLoopFromLoop(edgeThread, radius))) + radius += radiusAddition + skinnedPerimeterFlowRate = self.oldFlowRate / self.verticalDivisionsFloat + if getIsMinimumSides(edges): + self.addFlowRateLine(skinnedPerimeterFlowRate / self.horizontalPerimeterDivisionsFloat) + for verticalDivisionIndex in xrange(self.verticalDivisions): + z = bottomZ + self.layerHeight / self.verticalDivisionsFloat * float(verticalDivisionIndex) + for edge in edges: + self.addPerimeterLoop(edge, z) + else: + self.addFlowRateLine(skinnedPerimeterFlowRate) + for verticalDivisionIndex in xrange(self.verticalDivisions): + z = bottomZ + self.layerHeight / self.verticalDivisionsFloat * float(verticalDivisionIndex) + self.addPerimeterLoop(self.edge, z) + self.addFlowRateLine(self.oldFlowRate) + self.edge = None + + def getClippedSimplifiedLoopPathByLoop(self, loop): + 'Get clipped and simplified loop path from a loop.' + if len(loop) == 0: + return [] + loopPath = loop + [loop[0]] + return euclidean.getClippedSimplifiedLoopPath(self.clipLength, loopPath, self.halfEdgeWidth) + + def getCraftedGcode( self, gcodeText, repository ): + 'Parse gcode text and store the skin gcode.' + self.lines = archive.getTextLines(gcodeText) + self.repository = repository + self.layersFromBottom = repository.layersFrom.value + self.horizontalInfillDivisionsFloat = float(repository.horizontalInfillDivisions.value) + self.horizontalPerimeterDivisionsFloat = float(repository.horizontalPerimeterDivisions.value) + self.verticalDivisions = max(repository.verticalDivisions.value, 1) + self.verticalDivisionsFloat = float(self.verticalDivisions) + self.parseInitialization() + self.clipLength = 0.5 * self.clipOverEdgeWidth * self.edgeWidth + self.skinInfillInset = 0.5 * (self.infillWidth + self.skinInfillWidth) * (1.0 - self.infillPerimeterOverlap) + self.parseBoundaries() + for self.lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[self.lineIndex] + self.parseLine(line) + return gcodec.getGcodeWithoutDuplication('M108', self.distanceFeedRate.output.getvalue()) + + def parseBoundaries(self): + 'Parse the boundaries and add them to the boundary layers.' + self.boundaryLayers = [] + self.layerIndexTop = -1 + boundaryLoop = None + boundaryLayer = None + for line in self.lines[self.lineIndex :]: + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == '()': + boundaryLoop = None + elif firstWord == '(': + location = gcodec.getLocationFromSplitLine(None, splitLine) + if boundaryLoop == None: + boundaryLoop = [] + boundaryLayer.loops.append(boundaryLoop) + boundaryLoop.append(location.dropAxis()) + elif firstWord == '(': + boundaryLayer = euclidean.LoopLayer(float(splitLine[1])) + self.boundaryLayers.append(boundaryLayer) + self.layerIndexTop += 1 + for boundaryLayerIndex, boundaryLayer in enumerate(self.boundaryLayers): + if len(boundaryLayer.loops) > 0: + self.layersFromBottom += boundaryLayerIndex + return + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '(': + self.clipOverEdgeWidth = float(splitLine[1]) + elif firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('skin') + return + elif firstWord == '(': + self.infillPerimeterOverlap = float(splitLine[1]) + elif firstWord == '(': + self.infillWidth = float(splitLine[1]) + self.skinInfillWidth = self.infillWidth / self.horizontalInfillDivisionsFloat + elif firstWord == '(': + self.layerHeight = float(splitLine[1]) + elif firstWord == '(': + self.maximumZFeedRateMinute = 60.0 * float(splitLine[1]) + elif firstWord == '(': + self.oldFlowRate = float(splitLine[1]) + elif firstWord == '(': + self.edgeWidth = float(splitLine[1]) + self.halfEdgeWidth = 0.5 * self.edgeWidth + elif firstWord == '(': + self.travelFeedRateMinute = 60.0 * float(splitLine[1]) + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + 'Parse a gcode line and add it to the skin skein.' + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + self.feedRateMinute = gcodec.getFeedRateMinute(self.feedRateMinute, splitLine) + self.oldLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + if self.infillBoundaries != None: + return + if self.edge != None: + self.edge.append(self.oldLocation.dropAxis()) + return + elif firstWord == '()': + if self.layerIndex >= self.layersFromBottom and self.layerIndex == self.layerIndexTop: + self.infillBoundaries = [] + elif firstWord == '()': + self.addSkinnedInfill() + elif firstWord == '()': + if self.infillBoundaries != None: + self.infillBoundary = [] + self.infillBoundaries.append(self.infillBoundary) + elif firstWord == '(': + if self.infillBoundaries != None: + location = gcodec.getLocationFromSplitLine(None, splitLine) + self.infillBoundary.append(location.dropAxis()) + elif firstWord == '(': + self.layerIndex += 1 + settings.printProgress(self.layerIndex, 'skin') + elif firstWord == 'M101' or firstWord == 'M103': + if self.infillBoundaries != None or self.edge != None: + return + elif firstWord == 'M108': + self.oldFlowRate = gcodec.getDoubleAfterFirstLetter(splitLine[1]) + elif firstWord == '(': + if self.layerIndex >= self.layersFromBottom: + self.edge = [] + elif firstWord == '(': + self.rotation = gcodec.getRotationBySplitLine(splitLine) + self.reverseRotation = complex(self.rotation.real, -self.rotation.imag) + elif firstWord == '()': + self.addSkinnedPerimeter() + self.distanceFeedRate.addLine(line) + + +def main(): + 'Display the skin dialog.' + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/skirt.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/skirt.py new file mode 100644 index 0000000..21bc7d9 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/skirt.py @@ -0,0 +1,349 @@ +""" +This page is in the table of contents. +Skirt is a plugin to give the extruder some extra time to begin extruding properly before beginning the object, and to put a baffle around the model in order to keep the extrusion warm. + +The skirt manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Skirt + +It is loosely based on Lenbook's outline plugin: + +http://www.thingiverse.com/thing:4918 + +it is also loosely based on the outline that Nophead sometimes uses: + +http://hydraraptor.blogspot.com/2010/01/hot-metal-and-serendipity.html + +and also loosely based on the baffles that Nophead made to keep corners warm: + +http://hydraraptor.blogspot.com/2010/09/some-corners-like-it-hot.html + +If you want only an outline, set 'Layers To' to one. This gives the extruder some extra time to begin extruding properly before beginning your object, and gives you an early verification of where your object will be extruded. + +If you also want an insulating skirt around the entire object, set 'Layers To' to a huge number, like 912345678. This will additionally make an insulating baffle around the object; to prevent moving air from cooling the object, which increases warping, especially in corners. + +==Operation== +The default 'Activate Skirt' checkbox is off. When it is on, the functions described below will work, when it is off, nothing will be done. + +==Settings== +===Convex=== +Default is on. + +When selected, the skirt will be convex, going around the model with only convex angles. If convex is not selected, the skirt will hug the model, going into every nook and cranny. + +===Gap over Perimeter Width=== +Default is three. + +Defines the ratio of the gap between the object and the skirt over the edge width. If the ratio is too low, the skirt will connect to the object, if the ratio is too high, the skirt willl not provide much insulation for the object. + +===Layers To=== +Default is a one. + +Defines the number of layers of the skirt. If you want only an outline, set 'Layers To' to one. If you want an insulating skirt around the entire object, set 'Layers To' to a huge number, like 912345678. + +==Examples== +The following examples skirt the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and skirt.py. + +> python skirt.py +This brings up the skirt dialog. + +> python skirt.py Screw Holder Bottom.stl +The skirt tool is parsing the file: +Screw Holder Bottom.stl +.. +The skirt tool has created the file: +.. Screw Holder Bottom_skirt.gcode + +""" + + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import sys + + +__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 getCraftedText(fileName, text='', repository=None): + 'Skirt the fill file or text.' + return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository) + +def getCraftedTextFromText(gcodeText, repository=None): + 'Skirt the fill text.' + if gcodec.isProcedureDoneOrFileIsEmpty(gcodeText, 'skirt'): + return gcodeText + if repository == None: + repository = settings.getReadRepository(SkirtRepository()) + if not repository.activateSkirt.value: + return gcodeText + return SkirtSkein().getCraftedGcode(gcodeText, repository) + +def getNewRepository(): + 'Get new repository.' + return SkirtRepository() + +def getOuterLoops(loops): + 'Get widdershins outer loops.' + outerLoops = [] + for loop in loops: + if not euclidean.isPathInsideLoops(outerLoops, loop): + outerLoops.append(loop) + intercircle.directLoops(True, outerLoops) + return outerLoops + +def writeOutput(fileName, shouldAnalyze=True): + 'Skirt a gcode linear move file.' + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'skirt', shouldAnalyze) + + +class LoopCrossDictionary: + 'Loop with a horizontal and vertical dictionary.' + def __init__(self): + 'Initialize LoopCrossDictionary.' + self.loop = [] + + def __repr__(self): + 'Get the string representation of this LoopCrossDictionary.' + return str(self.loop) + + +class SkirtRepository: + 'A class to handle the skirt settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.skirt.html', self) + self.fileNameInput = settings.FileNameInput().getFromFileName( + fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Skirt', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Skirt') + self.activateSkirt = settings.BooleanSetting().getFromValue('Activate Skirt', self, False) + self.convex = settings.BooleanSetting().getFromValue('Convex:', self, True) + self.gapOverEdgeWidth = settings.FloatSpin().getFromValue(1.0, 'Gap over Perimeter Width (ratio):', self, 5.0, 3.0) + self.layersTo = settings.IntSpin().getSingleIncrementFromValue(0, 'Layers To (index):', self, 912345678, 1) + self.executeTitle = 'Skirt' + + def execute(self): + 'Skirt button has been clicked.' + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode( + self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class SkirtSkein: + 'A class to skirt a skein of extrusions.' + def __init__(self): + 'Initialize variables.' + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.feedRateMinute = 961.0 + self.isExtruderActive = False + self.isSupportLayer = False + self.layerIndex = -1 + self.lineIndex = 0 + self.lines = None + self.oldFlowRate = None + self.oldLocation = None + self.oldTemperatureInput = None + self.skirtFlowRate = None + self.skirtTemperature = None + self.travelFeedRateMinute = 957.0 + self.unifiedLoop = LoopCrossDictionary() + + def addFlowRate(self, flowRate): + 'Add a line of temperature if different.' + if flowRate != None: + self.distanceFeedRate.addLine('M108 S' + euclidean.getFourSignificantFigures(flowRate)) + + def addSkirt(self, z): + 'At skirt at z to gcode output.' + self.setSkirtFeedFlowTemperature() + self.distanceFeedRate.addLine('()') + oldTemperature = self.oldTemperatureInput + self.addTemperatureLineIfDifferent(self.skirtTemperature) + self.addFlowRate(self.skirtFlowRate) + for outsetLoop in self.outsetLoops: + closedLoop = outsetLoop + [outsetLoop[0]] + self.distanceFeedRate.addGcodeFromFeedRateThreadZ(self.feedRateMinute, closedLoop, self.travelFeedRateMinute, z) + self.addFlowRate(self.oldFlowRate) + self.addTemperatureLineIfDifferent(oldTemperature) + self.distanceFeedRate.addLine('()') + + def addTemperatureLineIfDifferent(self, temperature): + 'Add a line of temperature if different.' + if temperature == None or temperature == self.oldTemperatureInput: + return + self.distanceFeedRate.addLine('M104 S' + euclidean.getRoundedToThreePlaces(temperature)) + self.oldTemperatureInput = temperature + + def createSegmentDictionaries(self, loopCrossDictionary): + 'Create horizontal and vertical segment dictionaries.' + loopCrossDictionary.horizontalDictionary = self.getHorizontalXIntersectionsTable(loopCrossDictionary.loop) + flippedLoop = euclidean.getDiagonalFlippedLoop(loopCrossDictionary.loop) + loopCrossDictionary.verticalDictionary = self.getHorizontalXIntersectionsTable(flippedLoop) + + def createSkirtLoops(self): + 'Create the skirt loops.' + points = euclidean.getPointsByHorizontalDictionary(self.edgeWidth, self.unifiedLoop.horizontalDictionary) + points += euclidean.getPointsByVerticalDictionary(self.edgeWidth, self.unifiedLoop.verticalDictionary) + loops = triangle_mesh.getDescendingAreaOrientedLoops(points, points, 2.5 * self.edgeWidth) + outerLoops = getOuterLoops(loops) + outsetLoops = intercircle.getInsetSeparateLoopsFromLoops(outerLoops, -self.skirtOutset) + self.outsetLoops = getOuterLoops(outsetLoops) + if self.repository.convex.value: + self.outsetLoops = [euclidean.getLoopConvex(euclidean.getConcatenatedList(self.outsetLoops))] + + def getCraftedGcode(self, gcodeText, repository): + 'Parse gcode text and store the skirt gcode.' + self.repository = repository + self.lines = archive.getTextLines(gcodeText) + self.parseInitialization() + self.parseBoundaries() + self.createSkirtLoops() + for self.lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[self.lineIndex] + self.parseLine(line) + return gcodec.getGcodeWithoutDuplication('M108', self.distanceFeedRate.output.getvalue()) + + def getHorizontalXIntersectionsTable(self, loop): + 'Get the horizontal x intersections table from the loop.' + horizontalXIntersectionsTable = {} + euclidean.addXIntersectionsFromLoopForTable(loop, horizontalXIntersectionsTable, self.edgeWidth) + return horizontalXIntersectionsTable + + def parseBoundaries(self): + 'Parse the boundaries and union them.' + self.createSegmentDictionaries(self.unifiedLoop) + if self.repository.layersTo.value < 1: + return + loopCrossDictionary = None + layerIndex = -1 + for line in self.lines[self.lineIndex :]: + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == '()' or firstWord == '()': + self.createSegmentDictionaries(loopCrossDictionary) + self.unifyLayer(loopCrossDictionary) + loopCrossDictionary = None + elif firstWord == '(' or firstWord == '(': + location = gcodec.getLocationFromSplitLine(None, splitLine) + if loopCrossDictionary == None: + loopCrossDictionary = LoopCrossDictionary() + loopCrossDictionary.loop.append(location.dropAxis()) + elif firstWord == '(': + layerIndex += 1 + if layerIndex > self.repository.layersTo.value: + return + settings.printProgress(layerIndex, 'skirt') + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('skirt') + return + elif firstWord == '(': + self.oldTemperatureInput = float(splitLine[1]) + self.skirtTemperature = self.oldTemperatureInput + elif firstWord == '(': + self.feedRateMinute = 60.0 * float(splitLine[1]) + elif firstWord == '(': + self.oldFlowRate = float(splitLine[1]) + self.skirtFlowRate = self.oldFlowRate + elif firstWord == '(': + self.edgeWidth = float(splitLine[1]) + self.skirtOutset = (self.repository.gapOverEdgeWidth.value + 0.5) * self.edgeWidth + self.distanceFeedRate.addTagRoundedLine('skirtOutset', self.skirtOutset) + elif firstWord == '(': + self.travelFeedRateMinute = 60.0 * float(splitLine[1]) + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + 'Parse a gcode line and add it to the skirt skein.' + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == '()' or firstWord == '()' or firstWord == '(': + return + self.distanceFeedRate.addLine(line) + if firstWord == 'G1': + self.feedRateMinute = gcodec.getFeedRateMinute(self.feedRateMinute, splitLine) + elif firstWord == '(': + self.layerIndex += 1 + if self.layerIndex < self.repository.layersTo.value: + self.addSkirt(float(splitLine[1])) + elif firstWord == 'M101': + self.isExtruderActive = True + elif firstWord == 'M103': + self.isExtruderActive = False + elif firstWord == 'M104': + self.oldTemperatureInput = gcodec.getDoubleAfterFirstLetter(splitLine[1]) + self.skirtTemperature = self.oldTemperatureInput + elif firstWord == 'M108': + self.oldFlowRate = gcodec.getDoubleAfterFirstLetter(splitLine[1]) + self.skirtFlowRate = self.oldFlowRate + elif firstWord == '()': + self.isSupportLayer = True + elif firstWord == '()': + self.isSupportLayer = False + + def setSkirtFeedFlowTemperature(self): + 'Set the skirt feed rate, flow rate and temperature to that of the next extrusion.' + isExtruderActive = self.isExtruderActive + isSupportLayer = self.isSupportLayer + for lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'G1': + self.feedRateMinute = gcodec.getFeedRateMinute(self.feedRateMinute, splitLine) + if isExtruderActive: + if not isSupportLayer: + return + elif firstWord == 'M101': + isExtruderActive = True + elif firstWord == 'M103': + isExtruderActive = False + elif firstWord == 'M104': + self.skirtTemperature = gcodec.getDoubleAfterFirstLetter(splitLine[1]) + elif firstWord == 'M108': + self.skirtFlowRate = gcodec.getDoubleAfterFirstLetter(splitLine[1]) + elif firstWord == '()': + isSupportLayer = True + elif firstWord == '()': + isSupportLayer = False + + def unifyLayer(self, loopCrossDictionary): + 'Union the loopCrossDictionary with the unifiedLoop.' + euclidean.joinXIntersectionsTables(loopCrossDictionary.horizontalDictionary, self.unifiedLoop.horizontalDictionary) + euclidean.joinXIntersectionsTables(loopCrossDictionary.verticalDictionary, self.unifiedLoop.verticalDictionary) + + +def main(): + 'Display the skirt dialog.' + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/smooth.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/smooth.py new file mode 100644 index 0000000..0a386e3 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/smooth.py @@ -0,0 +1,241 @@ +""" +This page is in the table of contents. +This plugin smooths jagged extruder paths. It takes shortcuts through jagged paths and decreases the feed rate to compensate. + +Smooth is based on ideas in Nophead's frequency limit post: + +http://hydraraptor.blogspot.com/2010/12/frequency-limit.html + +The smooth manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Smooth + +==Operation== +The default 'Activate Smooth' checkbox is off. When it is on, the functions described below will work, when it is off, nothing will be done. + +==Settings== +===Layers From=== +Default: 1 + +Defines which layer of the print the smoothing process starts from. If this is set this to zero, that might cause the smoothed parts of the bottom edge not to adhere well to the print surface. However, this is just a potential problem in theory, no bottom adhesion problem has been reported. + +===Maximum Shortening over Width=== +Default: 1.2 + +Defines the maximum shortening of the shortcut compared to the original path. Smooth goes over the path and if the shortcut between the midpoint of one line and the midpoint of the second line after is not too short compared to the original and the shortcut is not too long, it replaces the jagged original with the shortcut. If the maximum shortening is too much, smooth will shorten paths which should not of been shortened and will leave blobs and holes in the model. If the maximum shortening is too little, even jagged paths that could be shortened safely won't be smoothed. + +==Examples== +The following examples smooth the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and smooth.py. + +> python smooth.py +This brings up the smooth dialog. + +> python smooth.py Screw Holder Bottom.stl +The smooth tool is parsing the file: +Screw Holder Bottom.stl +.. +The smooth tool has created the file: +.. Screw Holder Bottom_smooth.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import sys + + +__author__ = 'Enrique Perez (perez_enrique aht yahoo.com) & James Blackwell (jim_blag ahht hotmail.com)' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCraftedText(fileName, gcodeText, repository=None): + 'Smooth a gcode linear move text.' + return getCraftedTextFromText(archive.getTextIfEmpty(fileName, gcodeText), repository) + +def getCraftedTextFromText(gcodeText, repository=None): + 'Smooth a gcode linear move text.' + if gcodec.isProcedureDoneOrFileIsEmpty(gcodeText, 'smooth'): + return gcodeText + if repository == None: + repository = settings.getReadRepository(SmoothRepository()) + if not repository.activateSmooth.value: + return gcodeText + return SmoothSkein().getCraftedGcode(gcodeText, repository) + +def getNewRepository(): + 'Get new repository.' + return SmoothRepository() + +def writeOutput(fileName, shouldAnalyze=True): + 'Smooth a gcode linear move file. Chain smooth the gcode if it is not already smoothed.' + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'smooth', shouldAnalyze) + + +class SmoothRepository: + 'A class to handle the smooth settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.smooth.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Smooth', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Smooth') + self.activateSmooth = settings.BooleanSetting().getFromValue('Activate Smooth', self, False) + self.layersFrom = settings.IntSpin().getSingleIncrementFromValue(0, 'Layers From (index):', self, 912345678, 1) + self.maximumShorteningOverWidth = settings.FloatSpin().getFromValue(0.2, 'Maximum Shortening over Width (float):', self, 2.0, 1.2) + self.executeTitle = 'Smooth' + + def execute(self): + 'Smooth button has been clicked.' + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class SmoothSkein: + 'A class to smooth a skein of extrusions.' + def __init__(self): + 'Initialize.' + self.boundaryLayerIndex = -1 + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.feedRateMinute = 959.0 + self.infill = None + self.layerCount = settings.LayerCount() + self.lineIndex = 0 + self.lines = None + self.oldLocation = None + self.travelFeedRateMinute = 957.0 + + def addSmoothedInfill(self): + 'Add smoothed infill.' + if len(self.infill) < 4: + self.distanceFeedRate.addGcodeFromFeedRateThreadZ(self.feedRateMinute, self.infill, self.travelFeedRateMinute, self.oldLocation.z) + return + self.distanceFeedRate.addGcodeMovementZWithFeedRate(self.travelFeedRateMinute, self.infill[0], self.oldLocation.z) + self.distanceFeedRate.addLine('M101') + lengthMinusOne = len(self.infill) - 1 + lengthMinusTwo = lengthMinusOne - 1 + wasOriginalPoint = True + pointIndex = 0 + while pointIndex < lengthMinusOne: + nextPoint = self.infill[pointIndex + 1] + afterNextIndex = pointIndex + 2 + if afterNextIndex < lengthMinusTwo: + point = self.infill[pointIndex] + midpoint = 0.5 * (point + nextPoint) + afterNextPoint = self.infill[afterNextIndex] + afterNextNextPoint = self.infill[afterNextIndex + 1] + afterNextMidpoint = 0.5 * (afterNextPoint + afterNextNextPoint) + shortcutDistance = abs(afterNextMidpoint - midpoint) + originalDistance = abs(midpoint - point) + abs(afterNextPoint - nextPoint) + abs(afterNextMidpoint - afterNextPoint) + segment = euclidean.getNormalized(nextPoint - point) + afterNextSegment = euclidean.getNormalized(afterNextNextPoint - afterNextPoint) + sameDirection = self.getIsParallelToRotation(segment) and self.getIsParallelToRotation(afterNextSegment) + if originalDistance - shortcutDistance < self.maximumShortening and shortcutDistance < self.maximumDistance and sameDirection: + if wasOriginalPoint: + self.distanceFeedRate.addGcodeMovementZWithFeedRate(self.feedRateMinute, midpoint, self.oldLocation.z) + feedrate = self.feedRateMinute + if originalDistance != 0.0: + feedrate *= shortcutDistance / originalDistance + self.distanceFeedRate.addGcodeMovementZWithFeedRate(feedrate, afterNextMidpoint, self.oldLocation.z) + wasOriginalPoint = False + pointIndex += 1 + else: + self.distanceFeedRate.addGcodeMovementZWithFeedRate(self.feedRateMinute, nextPoint, self.oldLocation.z) + wasOriginalPoint = True + else: + self.distanceFeedRate.addGcodeMovementZWithFeedRate(self.feedRateMinute, nextPoint, self.oldLocation.z) + wasOriginalPoint = True + pointIndex += 1 + self.distanceFeedRate.addLine('M103') + + def getCraftedGcode( self, gcodeText, repository ): + 'Parse gcode text and store the smooth gcode.' + self.lines = archive.getTextLines(gcodeText) + self.repository = repository + self.layersFromBottom = repository.layersFrom.value + self.parseInitialization() + for self.lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[self.lineIndex] + self.parseLine(line) + return self.distanceFeedRate.output.getvalue() + + def getIsParallelToRotation(self, segment): + 'Determine if the segment is parallel to the rotation.' + return abs(euclidean.getDotProduct(segment, self.rotation)) > 0.99999 + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('smooth') + return + elif firstWord == '(': + self.infillWidth = float(splitLine[1]) + self.maximumShortening = self.repository.maximumShorteningOverWidth.value * self.infillWidth + self.maximumDistance = 1.5 * self.maximumShortening + elif firstWord == '(': + self.travelFeedRateMinute = 60.0 * float(splitLine[1]) + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + 'Parse a gcode line and add it to the smooth skein.' + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == '()': + if self.boundaryLayerIndex < 0: + self.boundaryLayerIndex = 0 + elif firstWord == 'G1': + self.feedRateMinute = gcodec.getFeedRateMinute(self.feedRateMinute, splitLine) + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + self.oldLocation = location + if self.infill != None: + self.infill.append(location.dropAxis()) + return + elif firstWord == '()': + if self.boundaryLayerIndex >= self.layersFromBottom: + self.infill = [] + elif firstWord == '()': + self.infill = None + elif firstWord == '(': + self.layerCount.printProgressIncrement('smooth') + if self.boundaryLayerIndex >= 0: + self.boundaryLayerIndex += 1 + elif firstWord == 'M101': + if self.infill != None: + if len(self.infill) > 1: + self.infill = [self.infill[0]] + return + elif firstWord == 'M103': + if self.infill != None: + self.addSmoothedInfill() + self.infill = [] + return + elif firstWord == '(': + self.rotation = gcodec.getRotationBySplitLine(splitLine) + self.distanceFeedRate.addLine(line) + + +def main(): + 'Display the smooth dialog.' + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/speed.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/speed.py new file mode 100644 index 0000000..56b8066 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/speed.py @@ -0,0 +1,356 @@ +""" +This page is in the table of contents. +Speed is a plugin to set the feed rate and flow rate. + +The speed manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Speed + +==Operation== +The default 'Activate Speed' checkbox is on. When it is on, the functions described below will work, when it is off, nothing will be done. + +==Settings== +===Add Flow Rate=== +Default is on. + +When selected, the flow rate will be added to the gcode. + +===Bridge=== +====Bridge Feed Rate Multiplier==== +Default is one. + +Defines the ratio of the feed rate (head speed) on the bridge layers over the feed rate of the typical non bridge layers. + +====Bridge Flow Rate Multiplier==== +Default is one. + +Defines the ratio of the flow rate (extruder speed) on the bridge layers over the flow rate of the typical non bridge layers. + +===Duty Cyle=== +====Duty Cyle at Beginning==== +Default is one, which will set the extruder motor to full current. + +Defines the duty cycle of the stepper motor pulse width modulation by adding an M113 command toward the beginning of the gcode text. If the hardware has the option of using a potentiometer to set the duty cycle, to select the potentiometer option set 'Duty Cyle at Beginning' to an empty string. To turn off the extruder, set the 'Duty Cyle at Beginning' to zero. + +====Duty Cyle at Ending==== +Default is zero, which will turn off the extruder motor. + +Defines the duty cycle of the stepper motor pulse width modulation by adding an M113 command toward the ending of the gcode text. If the hardware has the option of using a potentiometer to set the duty cycle, to select the potentiometer option set 'Duty Cyle at Beginning' to an empty string. To turn off the extruder, set the 'Duty Cyle at Ending' to zero. + +===Feed Rate=== +Default is sixteen millimeters per second. + +Defines the operating feed rate, the speed your printing head moves in XY plane, before any modifiers. + +===Flow Rate Setting=== +Default is 210. + +Defines the operating flow rate. + +RapMan uses this parameter to define the RPM of the extruder motor. The extruder motor RPM is flow rate / 10 so if your flow rate is 150.0 that will set the extruder stepper to run at 15 RPM, different printers might read this value differently. + +===Maximum Z Feed Rate=== +Default is one millimeter per second. + +Defines the speed of a vertical hop, like the infill hop in skin. Also, if the Limit plugin is activated, it will limit the maximum speed of the tool head in the z direction to this value. + +===Object First Layer=== + +====Object First Layer Feed Rate Infill Multiplier==== +Default is 0.4. + +Defines the object first layer infill feed rate multiplier. The greater the 'Object First Layer Feed Rate Infill Multiplier, the thinner the infill, the lower the 'Object First Layer Feed Rate Infill Multiplier', the thicker the infill. + +====Object First Layer Feed Rate Perimeter Multiplier==== +Default is 0.4. + +Defines the object first layer edge feed rate multiplier. The greater the 'Object First Layer Feed Rate Perimeter Multiplier, the thinner the edge, the lower the 'Object First Layer Feed Rate Perimeter Multiplier', the thicker the edge. + +====Object First Layer Flow Rate Infill Multiplier==== +Default is 0.4. + +Defines the object first layer infill flow rate multiplier. The greater the 'Object First Layer Flow Rate Infill Multiplier', the thicker the infill, the lower the 'Object First Layer Flow Rate Infill Multiplier, the thinner the infill. + +====Object First Layer Flow Rate Perimeter Multiplier==== +Default is 0.4. + +Defines the object first layer edge flow rate multiplier. The greater the 'Object First Layer Flow Rate Perimeter Multiplier', the thicker the edge, the lower the 'Object First Layer Flow Rate Perimeter Multiplier, the thinner the edge. + +===Orbital Feed Rate over Operating Feed Rate=== +Default is 0.5. + +Defines the speed when the head is orbiting compared to the operating extruder speed. If you want the orbit to be very short, set the "Orbital Feed Rate over Operating Feed Rate" setting to a low value like 0.1. + +===Perimeter=== +To have higher build quality on the outside at the expense of slower build speed, a typical setting for the 'Perimeter Feed Rate over Operating Feed Rate' would be 0.5. To go along with that, if you are using a speed controlled extruder like a stepper extruder, the 'Perimeter Flow Rate over Operating Flow Rate' should also be 0.5. + +A stepper motor is the best way of driving the extruder; however, if you are stuck with a DC motor extruder using Pulse Width Modulation to control the speed, then you'll probably need a slightly higher ratio because there is a minimum voltage 'Flow Rate PWM Setting' required for the extruder motor to turn. The flow rate PWM ratio would be determined by trial and error, with the first trial being: +Perimeter Flow Rate over Operating Flow Rate ~ Perimeter Feed Rate over Operating Feed Rate * (Flow Rate PWM Setting - Minimum Flow Rate PWM Setting) + Minimum Flow Rate PWM Setting + +====Perimeter Feed Rate Multiplier==== +Default: 1.0 + +Defines the ratio of the feed rate of the edge (outside shell) over the feed rate of the infill. If you for example set this to 0.8 you will have a "stronger" outside edge than inside extrusion as the outside edge will be printed slower hence better lamination will occur and more filament will be placed there. + +====Perimeter Flow Rate Multiplier==== +Default: 1.0 + +Defines the ratio of the flow rate of the edge (outside shell) over the flow rate of the infill. If you want the same thickness of the edge but better lamination you need to compensate for the slower feed rate by slowing down the flow rate, but all combinations are possible for different results. + +===Travel Feed Rate=== +Default is sixteen millimeters per second. + +Defines the feed rate when the extruder is off (not printing). The 'Travel Feed Rate' could be set as high as the extruder can be moved, it is not limited by the maximum extrusion rate. + +==Examples== +The following examples speed the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and speed.py. + +> python speed.py +This brings up the speed dialog. + +> python speed.py Screw Holder Bottom.stl +The speed tool is parsing the file: +Screw Holder Bottom.stl +.. +The speed tool has created the file: +.. Screw Holder Bottom_speed.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import sys + + +__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 getCraftedText( fileName, text='', repository=None): + "Speed the file or text." + return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository) + +def getCraftedTextFromText(gcodeText, repository=None): + "Speed a gcode linear move text." + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'speed'): + return gcodeText + if repository == None: + repository = settings.getReadRepository( SpeedRepository() ) + if not repository.activateSpeed.value: + return gcodeText + return SpeedSkein().getCraftedGcode(gcodeText, repository) + +def getNewRepository(): + 'Get new repository.' + return SpeedRepository() + +def writeOutput(fileName, shouldAnalyze=True): + "Speed a gcode linear move file." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'speed', shouldAnalyze) + + +class SpeedRepository: + "A class to handle the speed settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.speed.html', self ) + self.baseNameSynonymDictionary = { + 'Object First Layer Feed Rate Infill Multiplier (ratio):' : 'raft.csv', + 'Object First Layer Feed Rate Perimeter Multiplier (ratio):' : 'raft.csv', + 'Object First Layer Flow Rate Infill Multiplier (ratio):' : 'raft.csv', + 'Object First Layer Flow Rate Perimeter Multiplier (ratio):' : 'raft.csv'} + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Speed', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Speed') + self.activateSpeed = settings.BooleanSetting().getFromValue('Activate Speed', self, True ) + self.addFlowRate = settings.BooleanSetting().getFromValue('Add Flow Rate:', self, True ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Bridge -', self ) + self.bridgeFeedRateMultiplier = settings.FloatSpin().getFromValue( 0.8, 'Bridge Feed Rate Multiplier (ratio):', self, 1.2, 1.0 ) + self.bridgeFlowRateMultiplier = settings.FloatSpin().getFromValue( 0.8, 'Bridge Flow Rate Multiplier (ratio):', self, 1.2, 1.0 ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Duty Cyle -', self ) + self.dutyCycleAtBeginning = settings.FloatSpin().getFromValue( 0.0, 'Duty Cyle at Beginning (portion):', self, 1.0, 1.0 ) + self.dutyCycleAtEnding = settings.FloatSpin().getFromValue( 0.0, 'Duty Cyle at Ending (portion):', self, 1.0, 0.0 ) + settings.LabelSeparator().getFromRepository(self) + self.feedRatePerSecond = settings.FloatSpin().getFromValue( 2.0, 'Feed Rate (mm/s):', self, 250.0, 50.0 ) + self.flowRateSetting = settings.FloatSpin().getFromValue( 50.0, 'Flow Rate Setting (float):', self, 250.0, 50.0 ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Object First Layers -', self) + self.objectFirstLayerFeedRateInfillMultiplier = settings.FloatSpin().getFromValue( + 0.2, 'Object First Layer Feed Rate Infill Multiplier (ratio):', self, 1.0, 0.4) + self.objectFirstLayerFeedRatePerimeterMultiplier = settings.FloatSpin().getFromValue( + 0.2, 'Object First Layer Feed Rate Perimeter Multiplier (ratio):', self, 1.0, 0.4) + self.objectFirstLayerFeedRateTravelMultiplier = settings.FloatSpin().getFromValue( + 0.2, 'Object First Layer Feed Rate Travel Multiplier (ratio):', self, 1.0, 0.4) + self.objectFirstLayerFlowRateInfillMultiplier = settings.FloatSpin().getFromValue( + 0.2, 'Object First Layer Flow Rate Infill Multiplier (ratio):', self, 1.0, 0.4) + self.objectFirstLayerFlowRatePerimeterMultiplier = settings.FloatSpin().getFromValue( + 0.2, 'Object First Layer Flow Rate Perimeter Multiplier (ratio):', self, 1.0, 0.4) + self.objectFirstLayersLayerAmount = settings.IntSpin().getFromValue( + 1, 'Object First Layers Amount Of Layers For Speed Change:', self, 10, 3) + settings.LabelSeparator().getFromRepository(self) + self.orbitalFeedRateOverOperatingFeedRate = settings.FloatSpin().getFromValue( 0.1, 'Orbital Feed Rate over Operating Feed Rate (ratio):', self, 0.9, 0.5 ) + self.maximumZFeedRatePerSecond = settings.FloatSpin().getFromValue(0.5, 'Maximum Z Feed Rate (mm/s):', self, 10.0, 1.0) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Perimeter -', self ) + self.perimeterFeedRateMultiplier = settings.FloatSpin().getFromValue(0.5, 'Perimeter Feed Rate Multiplier (ratio):', self, 1.0, 1.0) + self.perimeterFlowRateMultiplier = settings.FloatSpin().getFromValue(0.5, 'Perimeter Flow Rate Multiplier (ratio):', self, 1.0, 1.0) + settings.LabelSeparator().getFromRepository(self) + self.travelFeedRatePerSecond = settings.FloatSpin().getFromValue( 2.0, 'Travel Feed Rate (mm/s):', self, 350.0, 250.0 ) + self.executeTitle = 'Speed' + + def execute(self): + "Speed button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class SpeedSkein: + "A class to speed a skein of extrusions." + def __init__(self): + 'Initialize.' + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.feedRatePerSecond = 16.0 + self.isBridgeLayer = False + self.isEdgePath = False + self.isExtruderActive = False + self.layerIndex = -1 + self.lineIndex = 0 + self.lines = None + self.oldFlowRate = None + + def addFlowRateLine(self): + "Add flow rate line." + if not self.repository.addFlowRate.value: + return + flowRate = self.repository.flowRateSetting.value + if self.isBridgeLayer: + flowRate *= self.repository.bridgeFlowRateMultiplier.value + if self.isEdgePath: + flowRate *= self.repository.perimeterFlowRateMultiplier.value + if self.layerIndex < self.repository.objectFirstLayersLayerAmount.value: + if self.isEdgePath: + flowRate *= ((self.repository.objectFirstLayerFlowRatePerimeterMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value + else: + flowRate *= ((self.repository.objectFirstLayerFlowRateInfillMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value + if flowRate != self.oldFlowRate: + self.distanceFeedRate.addLine('M108 S' + euclidean.getFourSignificantFigures(flowRate)) + self.oldFlowRate = flowRate + + def addParameterString( self, firstWord, parameterWord ): + "Add parameter string." + if parameterWord == '': + self.distanceFeedRate.addLine(firstWord) + return + self.distanceFeedRate.addParameter( firstWord, parameterWord ) + + def getCraftedGcode(self, gcodeText, repository): + "Parse gcode text and store the speed gcode." + self.repository = repository + self.feedRatePerSecond = repository.feedRatePerSecond.value + self.travelFeedRateMinute = 60.0 * self.repository.travelFeedRatePerSecond.value + self.lines = archive.getTextLines(gcodeText) + self.parseInitialization() + for line in self.lines[self.lineIndex :]: + self.parseLine(line) + self.addParameterString('M113', self.repository.dutyCycleAtEnding.value ) # Set duty cycle . + return self.distanceFeedRate.output.getvalue() + + def getSpeededLine(self, line, splitLine): + 'Get gcode line with feed rate.' + if gcodec.getIndexOfStartingWithSecond('F', splitLine) > 0: + return line + feedRateMinute = 60.0 * self.feedRatePerSecond + if self.isBridgeLayer: + feedRateMinute *= self.repository.bridgeFeedRateMultiplier.value + if self.isEdgePath: + feedRateMinute *= self.repository.perimeterFeedRateMultiplier.value + if self.layerIndex < self.repository.objectFirstLayersLayerAmount.value: + if self.isEdgePath: + feedRateMinute *= ((self.repository.objectFirstLayerFeedRatePerimeterMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value + else: + feedRateMinute *= ((self.repository.objectFirstLayerFeedRateInfillMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value + self.addFlowRateLine() + if not self.isExtruderActive: + feedRateMinute = self.travelFeedRateMinute + if self.layerIndex < self.repository.objectFirstLayersLayerAmount.value: + feedRateMinute *= ((self.repository.objectFirstLayerFeedRateTravelMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value + return self.distanceFeedRate.getLineWithFeedRate(feedRateMinute, line, splitLine) + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '(': + self.layerHeight = float(splitLine[1]) + elif firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('speed') + return + elif firstWord == '(': + self.absoluteEdgeWidth = abs(float(splitLine[1])) + self.distanceFeedRate.addTagBracketedLine('maximumZFeedRatePerSecond', self.repository.maximumZFeedRatePerSecond.value ) + self.distanceFeedRate.addTagBracketedLine('objectFirstLayerFeedRateInfillMultiplier', self.repository.objectFirstLayerFeedRateInfillMultiplier.value) + self.distanceFeedRate.addTagBracketedLine('operatingFeedRatePerSecond', self.feedRatePerSecond ) + if self.repository.addFlowRate.value: + self.distanceFeedRate.addTagBracketedLine('objectFirstLayerFlowRateInfillMultiplier', self.repository.objectFirstLayerFlowRateInfillMultiplier.value) + self.distanceFeedRate.addTagBracketedLine('operatingFlowRate', self.repository.flowRateSetting.value ) + orbitalFeedRatePerSecond = self.feedRatePerSecond * self.repository.orbitalFeedRateOverOperatingFeedRate.value + self.distanceFeedRate.addTagBracketedLine('orbitalFeedRatePerSecond', orbitalFeedRatePerSecond ) + self.distanceFeedRate.addTagBracketedLine('travelFeedRatePerSecond', self.repository.travelFeedRatePerSecond.value ) + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + "Parse a gcode line and add it to the speed skein." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == '()': + self.distanceFeedRate.addLine(line) + self.addParameterString('M113', self.repository.dutyCycleAtBeginning.value ) # Set duty cycle . + return + elif firstWord == 'G1': + line = self.getSpeededLine(line, splitLine) + elif firstWord == 'M101': + self.isExtruderActive = True + elif firstWord == 'M103': + self.isExtruderActive = False + elif firstWord == '(': + self.isBridgeLayer = True + elif firstWord == '(': + self.layerIndex += 1 + settings.printProgress(self.layerIndex, 'speed') + self.isBridgeLayer = False + self.addFlowRateLine() + elif firstWord == '(' or firstWord == '()': + self.isEdgePath = True + elif firstWord == '()' or firstWord == '()': + self.isEdgePath = False + self.distanceFeedRate.addLine(line) + + +def main(): + "Display the speed dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/splodge.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/splodge.py new file mode 100644 index 0000000..d13a958 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/splodge.py @@ -0,0 +1,327 @@ +""" +This page is in the table of contents. +Splodge turns the extruder on just before the start of a thread. This is to give the extrusion a bit anchoring at the beginning. + +The splodge manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Splodge + +==Operation== +The default 'Activate Splodge' checkbox is on. When it is on, the functions described below will work, when it is off, the functions will not be called. + +==Settings== +===Initial=== +====Initial Lift over Extra Thickness==== +Default is one. + +Defines the amount the extruder will be lifted over the extra thickness of the initial splodge thread. The higher the ratio, the more the extruder will be lifted over the splodge, if the ratio is too low the extruder might plow through the splodge extrusion. + +====Initial Splodge Feed Rate==== +Default is one millimeter per second. + +Defines the feed rate at which the initial extra extrusion will be added. With the default feed rate, the splodge will be added slower so it will be thicker than the regular extrusion. + +====Initial Splodge Quantity Length==== +Default is thirty millimeters. + +Defines the quantity length of extra extrusion at the operating feed rate that will be added to the initial thread. If a splodge quantity length is smaller than 0.1 times the edge width, no splodge of that type will be added. + +===Operating=== +====Operating Lift over Extra Thickness==== +Default is one. + +Defines the amount the extruder will be lifted over the extra thickness of the operating splodge thread. + +====Operating Splodge Feed Rate==== +Default is one millimeter per second. + +Defines the feed rate at which the next extra extrusions will be added. + +====Operating Splodge Quantity Length==== +Default is thirty millimeters. + +Defines the quantity length of extra extrusion at the operating feed rate that will be added for the next threads. + +==Examples== +The following examples splodge the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and splodge.py. + +> python splodge.py +This brings up the splodge dialog. + +> python splodge.py Screw Holder Bottom.stl +The splodge tool is parsing the file: +Screw Holder Bottom.stl +.. +The splodge tool has created the file: +.. Screw Holder Bottom_splodge.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import sys + + +__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 getCraftedText( fileName, text, splodgeRepository = None ): + "Splodge a gcode linear move file or text." + return getCraftedTextFromText( archive.getTextIfEmpty(fileName, text), splodgeRepository ) + +def getCraftedTextFromText( gcodeText, splodgeRepository = None ): + "Splodge a gcode linear move text." + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'splodge'): + return gcodeText + if splodgeRepository == None: + splodgeRepository = settings.getReadRepository( SplodgeRepository() ) + if not splodgeRepository.activateSplodge.value: + return gcodeText + return SplodgeSkein().getCraftedGcode( gcodeText, splodgeRepository ) + +def getNewRepository(): + 'Get new repository.' + return SplodgeRepository() + +def writeOutput(fileName, shouldAnalyze=True): + "Splodge a gcode linear move file." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'splodge', shouldAnalyze) + + +class SplodgeRepository: + "A class to handle the splodge settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.splodge.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Splodge', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Splodge') + self.activateSplodge = settings.BooleanSetting().getFromValue('Activate Splodge', self, False ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Initial -', self ) + self.initialLiftOverExtraThickness = settings.FloatSpin().getFromValue( 0.5, 'Initial Lift over Extra Thickness (ratio):', self, 1.5, 1.0 ) + self.initialSplodgeFeedRate = settings.FloatSpin().getFromValue( 0.4, 'Initial Splodge Feed Rate (mm/s):', self, 2.4, 1.0 ) + self.initialSplodgeQuantityLength = settings.FloatSpin().getFromValue( 10.0, 'Initial Splodge Quantity Length (millimeters):', self, 90.0, 30.0 ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Operating -', self ) + self.operatingLiftOverExtraThickness = settings.FloatSpin().getFromValue( 0.5, 'Operating Lift over Extra Thickness (ratio):', self, 1.5, 1.0 ) + self.operatingSplodgeFeedRate = settings.FloatSpin().getFromValue( 0.4, 'Operating Splodge Feed Rate (mm/s):', self, 2.4, 1.0 ) + self.operatingSplodgeQuantityLength = settings.FloatSpin().getFromValue( 0.4, 'Operating Splodge Quantity Length (millimeters):', self, 2.4, 1.0 ) + settings.LabelSeparator().getFromRepository(self) + self.executeTitle = 'Splodge' + + def execute(self): + "Splodge button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class SplodgeSkein: + "A class to splodge a skein of extrusions." + def __init__(self): + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.feedRateMinute = 961.0 + self.isExtruderActive = False + self.hasInitialSplodgeBeenAdded = False + self.isLastExtruderCommandActivate = False + self.lastLineOutput = None + self.lineIndex = 0 + self.lines = None + self.oldLocation = None + self.operatingFeedRatePerSecond = 15.0 + + def addLineUnlessIdentical(self, line): + "Add a line, unless it is identical to the last line." + if line == self.lastLineOutput: + return + self.lastLineOutput = line + self.distanceFeedRate.addLine(line) + + def addLineUnlessIdenticalReactivate(self, line): + "Add a line, unless it is identical to the last line or another M101." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'M101': + if not self.isLastExtruderCommandActivate: + self.addLineUnlessIdentical(line) + self.isLastExtruderCommandActivate = True + return + if firstWord == 'M103': + self.isLastExtruderCommandActivate = False + self.addLineUnlessIdentical(line) + + def getCraftedGcode( self, gcodeText, splodgeRepository ): + "Parse gcode text and store the splodge gcode." + self.lines = archive.getTextLines(gcodeText) + self.setRotations() + self.splodgeRepository = splodgeRepository + self.parseInitialization( splodgeRepository ) + self.boundingRectangle = gcodec.BoundingRectangle().getFromGcodeLines( self.lines[self.lineIndex :], 0.5 * self.edgeWidth ) + self.initialSplodgeFeedRateMinute = 60.0 * splodgeRepository.initialSplodgeFeedRate.value + self.initialStartupDistance = splodgeRepository.initialSplodgeQuantityLength.value * splodgeRepository.initialSplodgeFeedRate.value / self.operatingFeedRatePerSecond + self.operatingSplodgeFeedRateMinute = 60.0 * splodgeRepository.operatingSplodgeFeedRate.value + self.operatingStartupDistance = splodgeRepository.operatingSplodgeQuantityLength.value * splodgeRepository.operatingSplodgeFeedRate.value / self.operatingFeedRatePerSecond + for self.lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[self.lineIndex] + self.parseLine(line) + return self.distanceFeedRate.output.getvalue() + + def getInitialSplodgeLine( self, line, location ): + "Add the initial splodge line." + if not self.isJustBeforeExtrusion(): + return line + self.hasInitialSplodgeBeenAdded = True + if self.splodgeRepository.initialSplodgeQuantityLength.value < self.minimumQuantityLength: + return line + return self.getSplodgeLineGivenDistance( self.initialSplodgeFeedRateMinute, line, self.splodgeRepository.initialLiftOverExtraThickness.value, location, self.initialStartupDistance ) + + def getNextActiveLocationComplex(self): + "Get the next active line." + isActive = False + for lineIndex in xrange( self.lineIndex + 1, len(self.lines) ): + line = self.lines[lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'M101': + isActive = True + if firstWord == 'G1' and isActive: + return gcodec.getLocationFromSplitLine(self.oldLocation, splitLine).dropAxis() + return None + + def getOperatingSplodgeLine( self, line, location ): + "Get the operating splodge line." + if not self.isJustBeforeExtrusion(): + return line + if self.splodgeRepository.operatingSplodgeQuantityLength.value < self.minimumQuantityLength: + return line + return self.getSplodgeLineGivenDistance( self.operatingSplodgeFeedRateMinute, line, self.splodgeRepository.operatingLiftOverExtraThickness.value, location, self.operatingStartupDistance ) + + def getSplodgeLine(self, line, location, splitLine): + "Get splodged gcode line." + self.feedRateMinute = gcodec.getFeedRateMinute(self.feedRateMinute, splitLine) + if self.hasInitialSplodgeBeenAdded: + return self.getOperatingSplodgeLine(line, location) + return self.getInitialSplodgeLine(line, location) + + def getSplodgeLineGivenDistance( self, feedRateMinute, line, liftOverExtraThickness, location, startupDistance ): + "Add the splodge line." + locationComplex = location.dropAxis() + relativeStartComplex = None + nextLocationComplex = self.getNextActiveLocationComplex() + if nextLocationComplex != None: + if nextLocationComplex != locationComplex: + relativeStartComplex = locationComplex - nextLocationComplex + if relativeStartComplex == None: + relativeStartComplex = complex( 19.9, 9.9 ) + if self.oldLocation != None: + oldLocationComplex = self.oldLocation.dropAxis() + if oldLocationComplex != locationComplex: + relativeStartComplex = oldLocationComplex - locationComplex + relativeStartComplex *= startupDistance / abs( relativeStartComplex ) + startComplex = self.getStartInsideBoundingRectangle( locationComplex, relativeStartComplex ) + feedRateMultiplier = feedRateMinute / self.operatingFeedRatePerSecond / 60.0 + splodgeLayerThickness = self.layerHeight / math.sqrt( feedRateMultiplier ) + extraLayerThickness = splodgeLayerThickness - self.layerHeight + lift = extraLayerThickness * liftOverExtraThickness + startLine = self.distanceFeedRate.getLinearGcodeMovementWithFeedRate( self.feedRateMinute, startComplex, location.z + lift ) + self.addLineUnlessIdenticalReactivate( startLine ) + self.addLineUnlessIdenticalReactivate('M101') + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + lineLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + self.distanceFeedRate.addGcodeMovementZWithFeedRate( feedRateMinute, locationComplex, lineLocation.z + lift ) + return '' + + def getStartInsideBoundingRectangle( self, locationComplex, relativeStartComplex ): + "Get a start inside the bounding rectangle." + startComplex = locationComplex + relativeStartComplex + if self.boundingRectangle.isPointInside( startComplex ): + return startComplex + for rotation in self.rotations: + rotatedRelativeStartComplex = relativeStartComplex * rotation + startComplex = locationComplex + rotatedRelativeStartComplex + if self.boundingRectangle.isPointInside( startComplex ): + return startComplex + return startComplex + + def isJustBeforeExtrusion(self): + "Determine if activate command is before linear move command." + for lineIndex in xrange(self.lineIndex + 1, len(self.lines)): + line = self.lines[lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'G1' or firstWord == 'M103': + return False + if firstWord == 'M101': + return True + return False + + def parseInitialization( self, splodgeRepository ): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.addLineUnlessIdenticalReactivate(gcodec.getTagBracketedProcedure('splodge')) + return + elif firstWord == '(': + self.layerHeight = float(splitLine[1]) + elif firstWord == '(': + self.operatingFeedRatePerSecond = float(splitLine[1]) + elif firstWord == '(': + self.edgeWidth = float(splitLine[1]) + self.minimumQuantityLength = 0.1 * self.edgeWidth + self.addLineUnlessIdenticalReactivate(line) + + def parseLine(self, line): + "Parse a gcode line and add it to the bevel gcode." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + line = self.getSplodgeLine(line, location, splitLine) + self.oldLocation = location + elif firstWord == 'M101': + self.isExtruderActive = True + elif firstWord == 'M103': + self.isExtruderActive = False + self.addLineUnlessIdenticalReactivate(line) + + def setRotations(self): + "Set the rotations." + self.rootHalf = math.sqrt( 0.5 ) + self.rotations = [] + self.rotations.append( complex( self.rootHalf, self.rootHalf ) ) + self.rotations.append( complex( self.rootHalf, - self.rootHalf ) ) + self.rotations.append( complex( 0.0, 1.0 ) ) + self.rotations.append( complex(0.0, -1.0) ) + self.rotations.append( complex( - self.rootHalf, self.rootHalf ) ) + self.rotations.append( complex( - self.rootHalf, - self.rootHalf ) ) + + +def main(): + "Display the splodge dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/stretch.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/stretch.py new file mode 100644 index 0000000..cb82ed3 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/stretch.py @@ -0,0 +1,427 @@ +""" +This page is in the table of contents. +Stretch is very important Skeinforge plugin that allows you to partially compensate for the fact that extruded holes are smaller then they should be. It stretches the threads to partially compensate for filament shrinkage when extruded. + +The stretch manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Stretch + +Extruded holes are smaller than the model because while printing an arc the head is depositing filament on both sides of the arc but in the inside of the arc you actually need less material then on the outside of the arc. You can read more about this on the RepRap ArcCompensation page: +http://reprap.org/bin/view/Main/ArcCompensation + +In general, stretch will widen holes and push corners out. In practice the filament contraction will not be identical to the algorithm, so even once the optimal parameters are determined, the stretch script will not be able to eliminate the inaccuracies caused by contraction, but it should reduce them. + +All the defaults assume that the thread sequence choice setting in fill is the edge being extruded first, then the loops, then the infill. If the thread sequence choice is different, the optimal thread parameters will also be different. In general, if the infill is extruded first, the infill would have to be stretched more so that even after the filament shrinkage, it would still be long enough to connect to the loop or edge. + +Holes should be made with the correct area for their radius. In other words, for example if your modeling program approximates a hole of radius one (area = pi) by making a square with the points at [(1,0), (0,1), (-1,0), (0,-1)] (area = 2), the radius should be increased by sqrt(pi/2). This can be done in fabmetheus xml by writing: +radiusAreal='True' + +in the attributes of the object or any parent of that object. In other modeling programs, you'll have to this manually or make a script. If area compensation is not done, then changing the stretch parameters to over compensate for too small hole areas will lead to incorrect compensation in other shapes. + +==Operation== +The default 'Activate Stretch' checkbox is off. When it is on, the functions described below will work, when it is off, the functions will not be called. + +==Settings== +===Loop Stretch Over Perimeter Width=== +Default is 0.1. + +Defines the ratio of the maximum amount the loop aka inner shell threads will be stretched compared to the edge width, in general this value should be the same as the 'Perimeter Outside Stretch Over Perimeter Width' setting. + +===Path Stretch Over Perimeter Width=== +Default is zero. + +Defines the ratio of the maximum amount the threads which are not loops, like the infill threads, will be stretched compared to the edge width. + +===Perimeter=== +====Perimeter Inside Stretch Over Perimeter Width==== +Default is 0.32. + +Defines the ratio of the maximum amount the inside edge thread will be stretched compared to the edge width, this is the most important setting in stretch. The higher the value the more it will stretch the edge and the wider holes will be. If the value is too small, the holes could be drilled out after fabrication, if the value is too high, the holes would be too wide and the part would have to junked. + +====Perimeter Outside Stretch Over Perimeter Width==== +Default is 0.1. + +Defines the ratio of the maximum amount the outside edge thread will be stretched compared to the edge width, in general this value should be around a third of the 'Perimeter Inside Stretch Over Perimeter Width' setting. + +===Stretch from Distance over Perimeter Width=== +Default is two. + +The stretch algorithm works by checking at each turning point on the extrusion path what the direction of the thread is at a distance of 'Stretch from Distance over Perimeter Width' times the edge width, on both sides, and moves the thread in the opposite direction. So it takes the current turning-point, goes "Stretch from Distance over Perimeter Width" * "Perimeter Width" ahead, reads the direction at that point. Then it goes the same distance in back in time, reads the direction at that other point. It then moves the thread in the opposite direction, away from the center of the arc formed by these 2 points+directions. + +The magnitude of the stretch increases with: +the amount that the direction of the two threads is similar and +by the '..Stretch Over Perimeter Width' ratio. + +==Examples== +The following examples stretch the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and stretch.py. + +> python stretch.py +This brings up the stretch dialog. + +> python stretch.py Screw Holder Bottom.stl +The stretch tool is parsing the file: +Screw Holder Bottom.stl +.. +The stretch tool has created the file: +.. Screw Holder Bottom_stretch.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import sys + + +__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' + + +#maybe speed up feedRate option +def getCraftedText( fileName, gcodeText, stretchRepository = None ): + "Stretch a gcode linear move text." + return getCraftedTextFromText( archive.getTextIfEmpty(fileName, gcodeText), stretchRepository ) + +def getCraftedTextFromText( gcodeText, stretchRepository = None ): + "Stretch a gcode linear move text." + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'stretch'): + return gcodeText + if stretchRepository == None: + stretchRepository = settings.getReadRepository( StretchRepository() ) + if not stretchRepository.activateStretch.value: + return gcodeText + return StretchSkein().getCraftedGcode( gcodeText, stretchRepository ) + +def getNewRepository(): + 'Get new repository.' + return StretchRepository() + +def writeOutput(fileName, shouldAnalyze=True): + "Stretch a gcode linear move file. Chain stretch the gcode if it is not already stretched." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'stretch', shouldAnalyze) + + +class LineIteratorBackward: + "Backward line iterator class." + def __init__( self, isLoop, lineIndex, lines ): + self.firstLineIndex = None + self.isLoop = isLoop + self.lineIndex = lineIndex + self.lines = lines + + def getIndexBeforeNextDeactivate(self): + "Get index two lines before the deactivate command." + for lineIndex in xrange( self.lineIndex + 1, len(self.lines) ): + line = self.lines[lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'M103': + return lineIndex - 2 + print('This should never happen in stretch, no deactivate command was found for this thread.') + raise StopIteration, "You've reached the end of the line." + + def getNext(self): + "Get next line going backward or raise exception." + while self.lineIndex > 3: + if self.lineIndex == self.firstLineIndex: + raise StopIteration, "You've reached the end of the line." + if self.firstLineIndex == None: + self.firstLineIndex = self.lineIndex + nextLineIndex = self.lineIndex - 1 + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'M103': + if self.isLoop: + nextLineIndex = self.getIndexBeforeNextDeactivate() + else: + raise StopIteration, "You've reached the end of the line." + if firstWord == 'G1': + if self.isBeforeExtrusion(): + if self.isLoop: + nextLineIndex = self.getIndexBeforeNextDeactivate() + else: + raise StopIteration, "You've reached the end of the line." + else: + self.lineIndex = nextLineIndex + return line + self.lineIndex = nextLineIndex + raise StopIteration, "You've reached the end of the line." + + def isBeforeExtrusion(self): + "Determine if index is two or more before activate command." + linearMoves = 0 + for lineIndex in xrange( self.lineIndex + 1, len(self.lines) ): + line = self.lines[lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'G1': + linearMoves += 1 + if firstWord == 'M101': + return linearMoves > 0 + if firstWord == 'M103': + return False + print('This should never happen in isBeforeExtrusion in stretch, no activate command was found for this thread.') + return False + + +class LineIteratorForward: + "Forward line iterator class." + def __init__( self, isLoop, lineIndex, lines ): + self.firstLineIndex = None + self.isLoop = isLoop + self.lineIndex = lineIndex + self.lines = lines + + def getIndexJustAfterActivate(self): + "Get index just after the activate command." + for lineIndex in xrange( self.lineIndex - 1, 3, - 1 ): + line = self.lines[lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'M101': + return lineIndex + 1 + print('This should never happen in stretch, no activate command was found for this thread.') + raise StopIteration, "You've reached the end of the line." + + def getNext(self): + "Get next line or raise exception." + while self.lineIndex < len(self.lines): + if self.lineIndex == self.firstLineIndex: + raise StopIteration, "You've reached the end of the line." + if self.firstLineIndex == None: + self.firstLineIndex = self.lineIndex + nextLineIndex = self.lineIndex + 1 + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'M103': + if self.isLoop: + nextLineIndex = self.getIndexJustAfterActivate() + else: + raise StopIteration, "You've reached the end of the line." + self.lineIndex = nextLineIndex + if firstWord == 'G1': + return line + raise StopIteration, "You've reached the end of the line." + + +class StretchRepository: + "A class to handle the stretch settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.stretch.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Stretch', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Stretch') + self.activateStretch = settings.BooleanSetting().getFromValue('Activate Stretch', self, False ) + self.crossLimitDistanceOverEdgeWidth = settings.FloatSpin().getFromValue( 3.0, 'Cross Limit Distance Over Perimeter Width (ratio):', self, 10.0, 5.0 ) + self.loopStretchOverEdgeWidth = settings.FloatSpin().getFromValue( 0.05, 'Loop Stretch Over Perimeter Width (ratio):', self, 0.25, 0.11 ) + self.pathStretchOverEdgeWidth = settings.FloatSpin().getFromValue( 0.0, 'Path Stretch Over Perimeter Width (ratio):', self, 0.2, 0.0 ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Perimeter -', self ) + self.edgeInsideStretchOverEdgeWidth = settings.FloatSpin().getFromValue( 0.12, 'Perimeter Inside Stretch Over Perimeter Width (ratio):', self, 0.52, 0.32 ) + self.edgeOutsideStretchOverEdgeWidth = settings.FloatSpin().getFromValue( 0.05, 'Perimeter Outside Stretch Over Perimeter Width (ratio):', self, 0.25, 0.1 ) + settings.LabelSeparator().getFromRepository(self) + self.stretchFromDistanceOverEdgeWidth = settings.FloatSpin().getFromValue( 1.0, 'Stretch From Distance Over Perimeter Width (ratio):', self, 3.0, 2.0 ) + self.executeTitle = 'Stretch' + + def execute(self): + "Stretch button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class StretchSkein: + "A class to stretch a skein of extrusions." + def __init__(self): + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.edgeWidth = 0.4 + self.extruderActive = False + self.feedRateMinute = 959.0 + self.isLoop = False + self.layerCount = settings.LayerCount() + self.lineIndex = 0 + self.lines = None + self.oldLocation = None + + def getCraftedGcode( self, gcodeText, stretchRepository ): + "Parse gcode text and store the stretch gcode." + self.lines = archive.getTextLines(gcodeText) + self.stretchRepository = stretchRepository + self.parseInitialization() + for self.lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[self.lineIndex] + self.parseStretch(line) + return self.distanceFeedRate.output.getvalue() + + def getCrossLimitedStretch( self, crossLimitedStretch, crossLineIterator, locationComplex ): + "Get cross limited relative stretch for a location." + try: + line = crossLineIterator.getNext() + except StopIteration: + return crossLimitedStretch + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + pointComplex = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine).dropAxis() + pointMinusLocation = locationComplex - pointComplex + pointMinusLocationLength = abs( pointMinusLocation ) + if pointMinusLocationLength <= self.crossLimitDistanceFraction: + return crossLimitedStretch + parallelNormal = pointMinusLocation / pointMinusLocationLength + parallelStretch = euclidean.getDotProduct( parallelNormal, crossLimitedStretch ) * parallelNormal + if pointMinusLocationLength > self.crossLimitDistance: + return parallelStretch + crossNormal = complex( parallelNormal.imag, - parallelNormal.real ) + crossStretch = euclidean.getDotProduct( crossNormal, crossLimitedStretch ) * crossNormal + crossPortion = ( self.crossLimitDistance - pointMinusLocationLength ) / self.crossLimitDistanceRemainder + return parallelStretch + crossStretch * crossPortion + + def getRelativeStretch( self, locationComplex, lineIterator ): + "Get relative stretch for a location." + lastLocationComplex = locationComplex + oldTotalLength = 0.0 + pointComplex = locationComplex + totalLength = 0.0 + while 1: + try: + line = lineIterator.getNext() + except StopIteration: + locationMinusPoint = locationComplex - pointComplex + locationMinusPointLength = abs( locationMinusPoint ) + if locationMinusPointLength > 0.0: + return locationMinusPoint / locationMinusPointLength + return complex() + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = splitLine[0] + pointComplex = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine).dropAxis() + locationMinusPoint = lastLocationComplex - pointComplex + locationMinusPointLength = abs( locationMinusPoint ) + totalLength += locationMinusPointLength + if totalLength >= self.stretchFromDistance: + distanceFromRatio = ( self.stretchFromDistance - oldTotalLength ) / locationMinusPointLength + totalPoint = distanceFromRatio * pointComplex + ( 1.0 - distanceFromRatio ) * lastLocationComplex + locationMinusTotalPoint = locationComplex - totalPoint + return locationMinusTotalPoint / self.stretchFromDistance + lastLocationComplex = pointComplex + oldTotalLength = totalLength + + def getStretchedLine( self, splitLine ): + "Get stretched gcode line." + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + self.feedRateMinute = gcodec.getFeedRateMinute( self.feedRateMinute, splitLine ) + self.oldLocation = location + if self.extruderActive and self.threadMaximumAbsoluteStretch > 0.0: + return self.getStretchedLineFromIndexLocation( self.lineIndex - 1, self.lineIndex + 1, location ) + if self.isJustBeforeExtrusion() and self.threadMaximumAbsoluteStretch > 0.0: + return self.getStretchedLineFromIndexLocation( self.lineIndex - 1, self.lineIndex + 1, location ) + return self.lines[self.lineIndex] + + def getStretchedLineFromIndexLocation( self, indexPreviousStart, indexNextStart, location ): + "Get stretched gcode line from line index and location." + crossIteratorForward = LineIteratorForward( self.isLoop, indexNextStart, self.lines ) + crossIteratorBackward = LineIteratorBackward( self.isLoop, indexPreviousStart, self.lines ) + iteratorForward = LineIteratorForward( self.isLoop, indexNextStart, self.lines ) + iteratorBackward = LineIteratorBackward( self.isLoop, indexPreviousStart, self.lines ) + locationComplex = location.dropAxis() + relativeStretch = self.getRelativeStretch( locationComplex, iteratorForward ) + self.getRelativeStretch( locationComplex, iteratorBackward ) + relativeStretch *= 0.8 + relativeStretch = self.getCrossLimitedStretch( relativeStretch, crossIteratorForward, locationComplex ) + relativeStretch = self.getCrossLimitedStretch( relativeStretch, crossIteratorBackward, locationComplex ) + relativeStretchLength = abs( relativeStretch ) + if relativeStretchLength > 1.0: + relativeStretch /= relativeStretchLength + absoluteStretch = relativeStretch * self.threadMaximumAbsoluteStretch + stretchedPoint = location.dropAxis() + absoluteStretch + return self.distanceFeedRate.getLinearGcodeMovementWithFeedRate( self.feedRateMinute, stretchedPoint, location.z ) + + def isJustBeforeExtrusion(self): + "Determine if activate command is before linear move command." + for lineIndex in xrange( self.lineIndex + 1, len(self.lines) ): + line = self.lines[lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'G1' or firstWord == 'M103': + return False + if firstWord == 'M101': + return True +# print('This should never happen in isJustBeforeExtrusion in stretch, no activate or deactivate command was found for this thread.') + return False + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('stretch') + return + elif firstWord == '(': + edgeWidth = float(splitLine[1]) + self.crossLimitDistance = self.edgeWidth * self.stretchRepository.crossLimitDistanceOverEdgeWidth.value + self.loopMaximumAbsoluteStretch = self.edgeWidth * self.stretchRepository.loopStretchOverEdgeWidth.value + self.pathAbsoluteStretch = self.edgeWidth * self.stretchRepository.pathStretchOverEdgeWidth.value + self.edgeInsideAbsoluteStretch = self.edgeWidth * self.stretchRepository.edgeInsideStretchOverEdgeWidth.value + self.edgeOutsideAbsoluteStretch = self.edgeWidth * self.stretchRepository.edgeOutsideStretchOverEdgeWidth.value + self.stretchFromDistance = self.stretchRepository.stretchFromDistanceOverEdgeWidth.value * edgeWidth + self.threadMaximumAbsoluteStretch = self.pathAbsoluteStretch + self.crossLimitDistanceFraction = 0.333333333 * self.crossLimitDistance + self.crossLimitDistanceRemainder = self.crossLimitDistance - self.crossLimitDistanceFraction + self.distanceFeedRate.addLine(line) + + def parseStretch(self, line): + "Parse a gcode line and add it to the stretch skein." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + line = self.getStretchedLine(splitLine) + elif firstWord == 'M101': + self.extruderActive = True + elif firstWord == 'M103': + self.extruderActive = False + self.setStretchToPath() + elif firstWord == '(': + self.layerCount.printProgressIncrement('stretch') + elif firstWord == '(': + self.isLoop = True + self.threadMaximumAbsoluteStretch = self.loopMaximumAbsoluteStretch + elif firstWord == '()': + self.setStretchToPath() + elif firstWord == '(': + self.isLoop = True + self.threadMaximumAbsoluteStretch = self.edgeInsideAbsoluteStretch + if splitLine[1] == 'outer': + self.threadMaximumAbsoluteStretch = self.edgeOutsideAbsoluteStretch + elif firstWord == '()': + self.setStretchToPath() + self.distanceFeedRate.addLine(line) + + def setStretchToPath(self): + "Set the thread stretch to path stretch and is loop false." + self.isLoop = False + self.threadMaximumAbsoluteStretch = self.pathAbsoluteStretch + + +def main(): + "Display the stretch dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/temperature.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/temperature.py new file mode 100644 index 0000000..0a1a8b9 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/temperature.py @@ -0,0 +1,204 @@ +""" +This page is in the table of contents. +Temperature is a plugin to set the temperature for the entire extrusion. + +The temperature manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Temperature + +==Operation== +The default 'Activate Temperature' checkbox is on. When it is on, the functions described below will work, when it is off, nothing will be done. + +==Settings== +===Rate=== +The default cooling rate and heating rate for the extruder were both been derived from bothacker's graph at: +http://bothacker.com/wp-content/uploads/2009/09/18h5m53s9.29.2009.png + +====Cooling Rate==== +Default is three degrees Celcius per second. + +Defines the cooling rate of the extruder. + +====Heating Rate==== +Default is ten degrees Celcius per second. + +Defines the heating rate of the extruder. + +===Temperature=== +====Base Temperature==== +Default for ABS is two hundred degrees Celcius. + +Defines the raft base temperature. + +====Interface Temperature==== +Default for ABS is two hundred degrees Celcius. + +Defines the raft interface temperature. + +====Object First Layer Infill Temperature==== +Default for ABS is 195 degrees Celcius. + +Defines the infill temperature of the first layer of the object. + +====Object First Layer Perimeter Temperature==== +Default for ABS is two hundred and twenty degrees Celcius. + +Defines the edge temperature of the first layer of the object. + +====Object Next Layers Temperature==== +Default for ABS is two hundred and thirty degrees Celcius. + +Defines the temperature of the next layers of the object. + +====Support Layers Temperature==== +Default for ABS is two hundred degrees Celcius. + +Defines the support layers temperature. + +====Supported Layers Temperature==== +Default for ABS is two hundred and thirty degrees Celcius. + +Defines the temperature of the supported layers of the object, those layers which are right above a support layer. + +==Examples== +The following examples add temperature information to the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and temperature.py. + +> python temperature.py +This brings up the temperature dialog. + +> python temperature.py Screw Holder Bottom.stl +The temperature tool is parsing the file: +Screw Holder Bottom.stl +.. +The temperature tool has created the file: +.. Screw Holder Bottom_temperature.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import sys + + +__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 getCraftedText( fileName, text='', repository=None): + "Temperature the file or text." + return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository) + +def getCraftedTextFromText(gcodeText, repository=None): + "Temperature a gcode linear move text." + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'temperature'): + return gcodeText + if repository == None: + repository = settings.getReadRepository( TemperatureRepository() ) + if not repository.activateTemperature.value: + return gcodeText + return TemperatureSkein().getCraftedGcode(gcodeText, repository) + +def getNewRepository(): + 'Get new repository.' + return TemperatureRepository() + +def writeOutput(fileName, shouldAnalyze=True): + "Temperature a gcode linear move file." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'temperature', shouldAnalyze) + + +class TemperatureRepository: + "A class to handle the temperature settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.temperature.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Temperature', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Temperature') + self.activateTemperature = settings.BooleanSetting().getFromValue('Activate Temperature', self, False ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Rate -', self ) + self.coolingRate = settings.FloatSpin().getFromValue( 1.0, 'Cooling Rate (Celcius/second):', self, 20.0, 3.0 ) + self.heatingRate = settings.FloatSpin().getFromValue( 1.0, 'Heating Rate (Celcius/second):', self, 20.0, 10.0 ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Temperature -', self ) + self.baseTemperature = settings.FloatSpin().getFromValue( 140.0, 'Base Temperature (Celcius):', self, 260.0, 200.0 ) + self.interfaceTemperature = settings.FloatSpin().getFromValue( 140.0, 'Interface Temperature (Celcius):', self, 260.0, 200.0 ) + self.objectFirstLayerInfillTemperature = settings.FloatSpin().getFromValue( 140.0, 'Object First Layer Infill Temperature (Celcius):', self, 260.0, 195.0 ) + self.objectFirstLayerPerimeterTemperature = settings.FloatSpin().getFromValue( 140.0, 'Object First Layer Perimeter Temperature (Celcius):', self, 260.0, 220.0 ) + self.objectNextLayersTemperature = settings.FloatSpin().getFromValue( 140.0, 'Object Next Layers Temperature (Celcius):', self, 260.0, 230.0 ) + self.supportLayersTemperature = settings.FloatSpin().getFromValue( 140.0, 'Support Layers Temperature (Celcius):', self, 260.0, 200.0 ) + self.supportedLayersTemperature = settings.FloatSpin().getFromValue( 140.0, 'Supported Layers Temperature (Celcius):', self, 260.0, 230.0 ) + self.executeTitle = 'Temperature' + + def execute(self): + "Temperature button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class TemperatureSkein: + "A class to temperature a skein of extrusions." + def __init__(self): + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.lineIndex = 0 + self.lines = None + + def getCraftedGcode(self, gcodeText, repository): + "Parse gcode text and store the temperature gcode." + self.repository = repository + self.lines = archive.getTextLines(gcodeText) + if self.repository.coolingRate.value < 0.1: + print('The cooling rate should be more than 0.1, any cooling rate less than 0.1 will be treated as 0.1.') + self.repository.coolingRate.value = 0.1 + if self.repository.heatingRate.value < 0.1: + print('The heating rate should be more than 0.1, any heating rate less than 0.1 will be treated as 0.1.') + self.repository.heatingRate.value = 0.1 + self.parseInitialization() + self.distanceFeedRate.addLines( self.lines[self.lineIndex :] ) + return self.distanceFeedRate.output.getvalue() + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('temperature') + return + elif firstWord == '(': + self.distanceFeedRate.addTagBracketedLine('coolingRate', self.repository.coolingRate.value ) + self.distanceFeedRate.addTagBracketedLine('heatingRate', self.repository.heatingRate.value ) + self.distanceFeedRate.addTagBracketedLine('baseTemperature', self.repository.baseTemperature.value ) + self.distanceFeedRate.addTagBracketedLine('interfaceTemperature', self.repository.interfaceTemperature.value ) + self.distanceFeedRate.addTagBracketedLine('objectFirstLayerInfillTemperature', self.repository.objectFirstLayerInfillTemperature.value ) + self.distanceFeedRate.addTagBracketedLine('objectFirstLayerPerimeterTemperature', self.repository.objectFirstLayerPerimeterTemperature.value ) + self.distanceFeedRate.addTagBracketedLine('objectNextLayersTemperature', self.repository.objectNextLayersTemperature.value ) + self.distanceFeedRate.addTagBracketedLine('supportLayersTemperature', self.repository.supportLayersTemperature.value ) + self.distanceFeedRate.addTagBracketedLine('supportedLayersTemperature', self.repository.supportedLayersTemperature.value ) + self.distanceFeedRate.addLine(line) + + +def main(): + "Display the temperature dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/tower.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/tower.py new file mode 100644 index 0000000..500ad86 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/tower.py @@ -0,0 +1,380 @@ +""" +This page is in the table of contents. +Tower commands the fabricator to extrude a disconnected region for a few layers, then go to another disconnected region and extrude there. Its purpose is to reduce the number of stringers between a shape and reduce extruder travel. + +The tower manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Tower + +==Operation== +The default 'Activate Tower' checkbox is off. The default is off because tower could result in the extruder colliding with an already extruded part of the shape and because extruding in one region for more than one layer could result in the shape melting. When it is on, the functions described below will work, when it is off, nothing will be done. + +==Settings== +===Maximum Tower Height=== +Default: 5 + +Defines the maximum number of layers that the extruder will extrude in one region before going to another. This is the most important value for tower. + +===Extruder Possible Collision Cone Angle=== +Default: 60 degrees + +Tower works by looking for islands in each layer and if it finds another island in the layer above, it goes to the next layer above instead of going across to other regions on the original layer. It checks for collision with shapes already extruded within a cone from the nozzle tip. The 'Extruder Possible Collision Cone Angle' setting is the angle of that cone. Realistic values for the cone angle range between zero and ninety. The higher the angle, the less likely a collision with the rest of the shape is, generally the extruder will stay in the region for only a few layers before a collision is detected with the wide cone. + +===Tower Start Layer=== +Default: 1 + +Defines the layer index which the script starts extruding towers, after the last raft layer which does not have support material. It is best to not tower at least the first layer because the temperature of the first layer is sometimes different than that of the other layers. + +==Examples== +The following examples tower the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and tower.py. + +> python tower.py +This brings up the tower dialog. + +> python tower.py Screw Holder Bottom.stl +The tower tool is parsing the file: +Screw Holder Bottom.stl +.. +The tower tool has created the file: +.. Screw Holder Bottom_tower.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import sys + + +__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 getCraftedText( fileName, text, towerRepository = None ): + "Tower a gcode linear move file or text." + return getCraftedTextFromText( archive.getTextIfEmpty(fileName, text), towerRepository ) + +def getCraftedTextFromText( gcodeText, towerRepository = None ): + "Tower a gcode linear move text." + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'tower'): + return gcodeText + if towerRepository == None: + towerRepository = settings.getReadRepository( TowerRepository() ) + if not towerRepository.activateTower.value: + return gcodeText + return TowerSkein().getCraftedGcode( gcodeText, towerRepository ) + +def getNewRepository(): + 'Get new repository.' + return TowerRepository() + +def writeOutput(fileName, shouldAnalyze=True): + "Tower a gcode linear move file." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'tower', shouldAnalyze) + + +class Island: + "A class to hold the boundary and lines." + def __init__(self): + self.boundary = [] + self.boundingLoop = None + self.lines = [] + + def addToBoundary( self, splitLine ): + "Add to the boundary if it is not complete." + if self.boundingLoop == None: + location = gcodec.getLocationFromSplitLine(None, splitLine) + self.boundary.append(location.dropAxis()) + self.z = location.z + + def createBoundingLoop(self): + "Create the bounding loop if it is not already created." + if self.boundingLoop == None: + self.boundingLoop = intercircle.BoundingLoop().getFromLoop( self.boundary ) + + +class ThreadLayer: + "A layer of loops and paths." + def __init__(self): + "Thread layer constructor." + self.afterExtrusionLines = [] + self.beforeExtrusionLines = [] + self.islands = [] + + def __repr__(self): + "Get the string representation of this thread layer." + return '%s' % self.islands + + +class TowerRepository: + "A class to handle the tower settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.tower.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Tower', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Tower') + self.activateTower = settings.BooleanSetting().getFromValue('Activate Tower', self, False ) + self.extruderPossibleCollisionConeAngle = settings.FloatSpin().getFromValue( 40.0, 'Extruder Possible Collision Cone Angle (degrees):', self, 80.0, 60.0 ) + self.maximumTowerHeight = settings.IntSpin().getFromValue( 2, 'Maximum Tower Height (layers):', self, 10, 5 ) + self.towerStartLayer = settings.IntSpin().getFromValue( 1, 'Tower Start Layer (integer):', self, 5, 1 ) + self.executeTitle = 'Tower' + + def execute(self): + "Tower button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class TowerSkein: + "A class to tower a skein of extrusions." + def __init__(self): + self.afterExtrusionLines = [] + self.beforeExtrusionLines = [] + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.edgeWidth = 0.6 + self.highestZ = - 987654321.0 + self.island = None + self.layerIndex = 0 + self.lineIndex = 0 + self.lines = None + self.minimumBelow = 0.1 + self.oldLayerIndex = None + self.oldLocation = None + self.oldOrderedLocation = Vector3() + self.shutdownLineIndex = sys.maxint + self.nestedRingCount = 0 + self.threadLayer = None + self.threadLayers = [] + self.travelFeedRateMinute = None + + def addEntireLayer( self, threadLayer ): + "Add entire thread layer." + self.distanceFeedRate.addLines( threadLayer.beforeExtrusionLines ) + for island in threadLayer.islands: + self.distanceFeedRate.addLines( island.lines ) + self.distanceFeedRate.addLines( threadLayer.afterExtrusionLines ) + + def addHighThread(self, location): + "Add thread with a high move if necessary to clear the previous extrusion." + if self.oldLocation != None: + if self.oldLocation.z + self.minimumBelow < self.highestZ: + self.distanceFeedRate.addGcodeMovementZWithFeedRate( self.travelFeedRateMinute, self.oldLocation.dropAxis(), self.highestZ ) + if location.z + self.minimumBelow < self.highestZ: + self.distanceFeedRate.addGcodeMovementZWithFeedRate( self.travelFeedRateMinute, location.dropAxis(), self.highestZ ) + + def addThreadLayerIfNone(self): + "Add a thread layer if it is none." + if self.threadLayer != None: + return + self.threadLayer = ThreadLayer() + self.threadLayers.append( self.threadLayer ) + self.threadLayer.beforeExtrusionLines = self.beforeExtrusionLines + self.beforeExtrusionLines = [] + + def addTowers(self): + "Add towers." + bottomLayerIndex = self.getBottomLayerIndex() + if bottomLayerIndex == None: + return + removedIsland = self.getRemovedIslandAddLayerLinesIfDifferent( self.threadLayers[ bottomLayerIndex ].islands, bottomLayerIndex ) + while 1: + self.climbTower( removedIsland ) + bottomLayerIndex = self.getBottomLayerIndex() + if bottomLayerIndex == None: + return + removedIsland = self.getRemovedIslandAddLayerLinesIfDifferent( self.threadLayers[ bottomLayerIndex ].islands, bottomLayerIndex ) + + def climbTower( self, removedIsland ): + "Climb up the island to any islands directly above." + outsetDistance = 1.5 * self.edgeWidth + for step in xrange( self.towerRepository.maximumTowerHeight.value ): + aboveIndex = self.oldLayerIndex + 1 + if aboveIndex >= len( self.threadLayers ): + return + outsetRemovedLoop = removedIsland.boundingLoop.getOutsetBoundingLoop( outsetDistance ) + islandsWithin = [] + for island in self.threadLayers[ aboveIndex ].islands: + if self.isInsideRemovedOutsideCone( island, outsetRemovedLoop, aboveIndex ): + islandsWithin.append( island ) + if len( islandsWithin ) < 1: + return + removedIsland = self.getRemovedIslandAddLayerLinesIfDifferent( islandsWithin, aboveIndex ) + self.threadLayers[ aboveIndex ].islands.remove( removedIsland ) + + def getBottomLayerIndex(self): + "Get the index of the first island layer which has islands." + for islandLayerIndex in xrange( len( self.threadLayers ) ): + if len( self.threadLayers[ islandLayerIndex ].islands ) > 0: + return islandLayerIndex + return None + + def getCraftedGcode( self, gcodeText, towerRepository ): + "Parse gcode text and store the tower gcode." + self.lines = archive.getTextLines(gcodeText) + self.towerRepository = towerRepository + self.parseInitialization() + self.parseIfWordUntilWord('(') + self.parseIfWordUntilWord('()') + for lineIndex in xrange(self.lineIndex, len(self.lines)): + self.parseLine( lineIndex ) + concatenateEndIndex = min( len( self.threadLayers ), towerRepository.towerStartLayer.value ) + for threadLayer in self.threadLayers[ : concatenateEndIndex ]: + self.addEntireLayer( threadLayer ) + self.threadLayers = self.threadLayers[ concatenateEndIndex : ] + self.addTowers() + self.distanceFeedRate.addLines( self.lines[ self.shutdownLineIndex : ] ) + return self.distanceFeedRate.output.getvalue() + + def getRemovedIslandAddLayerLinesIfDifferent( self, islands, layerIndex ): + "Add gcode lines for the layer if it is different than the old bottom layer index." + threadLayer = None + if layerIndex != self.oldLayerIndex: + self.oldLayerIndex = layerIndex + threadLayer = self.threadLayers[layerIndex] + self.distanceFeedRate.addLines( threadLayer.beforeExtrusionLines ) + removedIsland = self.getTransferClosestNestedRingLines( self.oldOrderedLocation, islands ) + if threadLayer != None: + self.distanceFeedRate.addLines( threadLayer.afterExtrusionLines ) + return removedIsland + + def getTransferClosestNestedRingLines( self, oldOrderedLocation, remainingNestedRings ): + "Get and transfer the closest remaining nested ring." + if len( remainingNestedRings ) > 0: + oldOrderedLocation.z = remainingNestedRings[0].z + closestDistance = 999999999987654321.0 + closestNestedRing = None + for remainingNestedRing in remainingNestedRings: + distance = euclidean.getClosestDistanceIndexToLine(oldOrderedLocation.dropAxis(), remainingNestedRing.boundary).distance + if distance < closestDistance: + closestDistance = distance + closestNestedRing = remainingNestedRing + remainingNestedRings.remove(closestNestedRing) + hasTravelledHighRoad = False + for line in closestNestedRing.lines: + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + if firstWord == 'G1': + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + if not hasTravelledHighRoad: + hasTravelledHighRoad = True + self.addHighThread(location) + if location.z > self.highestZ: + self.highestZ = location.z + self.oldLocation = location + self.distanceFeedRate.addLine(line) + return closestNestedRing + + def isInsideRemovedOutsideCone( self, island, removedBoundingLoop, untilLayerIndex ): + "Determine if the island is entirely inside the removed bounding loop and outside the collision cone of the remaining islands." + if not island.boundingLoop.isEntirelyInsideAnother( removedBoundingLoop ): + return False + bottomLayerIndex = self.getBottomLayerIndex() + coneAngleTangent = math.tan( math.radians( self.towerRepository.extruderPossibleCollisionConeAngle.value ) ) + for layerIndex in xrange( bottomLayerIndex, untilLayerIndex ): + islands = self.threadLayers[layerIndex].islands + outsetDistance = self.edgeWidth * ( untilLayerIndex - layerIndex ) * coneAngleTangent + 0.5 * self.edgeWidth + for belowIsland in self.threadLayers[layerIndex].islands: + outsetIslandLoop = belowIsland.boundingLoop.getOutsetBoundingLoop( outsetDistance ) + if island.boundingLoop.isOverlappingAnother( outsetIslandLoop ): + return False + return True + + def parseIfWordUntilWord(self, word): + "Parse gcode if there is a word until the word is reached." + for self.lineIndex in xrange(self.lineIndex, gcodec.getFirstWordIndexReverse(word, self.lines, self.lineIndex)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.addLine(line) + if firstWord == 'G1': + self.oldLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + if self.oldLocation.z > self.highestZ: + self.highestZ = self.oldLocation.z + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('tower') + elif firstWord == '(': + return + elif firstWord == '(': + self.minimumBelow = 0.1 * float(splitLine[1]) + elif firstWord == '(': + self.edgeWidth = float(splitLine[1]) + elif firstWord == '(': + self.travelFeedRateMinute = 60.0 * float(splitLine[1]) + self.distanceFeedRate.addLine(line) + + def parseLine( self, lineIndex ): + "Parse a gcode line." + line = self.lines[lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + self.afterExtrusionLines.append(line) + if firstWord == 'M103': + self.afterExtrusionLines = [] + elif firstWord == '()': + self.island.createBoundingLoop() + elif firstWord == '(': + self.island.addToBoundary(splitLine) + elif firstWord == '()': + self.shutdownLineIndex = lineIndex + elif firstWord == '(': + self.beforeExtrusionLines = [ line ] + self.island = None + self.nestedRingCount = 0 + self.threadLayer = None + return + elif firstWord == '()': + if self.threadLayer != None: + self.threadLayer.afterExtrusionLines = self.afterExtrusionLines + self.afterExtrusionLines = [] + elif firstWord == '()': + self.afterExtrusionLines = [] + elif firstWord == '()': + self.nestedRingCount += 1 + if self.island == None: + self.island = Island() + self.addThreadLayerIfNone() + self.threadLayer.islands.append( self.island ) + elif firstWord == '()': + self.afterExtrusionLines = [] + if self.island != None: + self.island.lines.append(line) + if firstWord == '()': + self.afterExtrusionLines = [] + self.nestedRingCount -= 1 + if self.nestedRingCount == 0: + self.island = None + if len( self.beforeExtrusionLines ) > 0: + self.beforeExtrusionLines.append(line) + + +def main(): + "Display the tower dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/unpause.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/unpause.py new file mode 100644 index 0000000..1827bd4 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/unpause.py @@ -0,0 +1,193 @@ +""" +This page is in the table of contents. +The unpause plugin is based on the Shane Hathaway's patch to speed up a line segment to compensate for the delay of the microprocessor. The description is at: +http://shane.willowrise.com/archives/delay-compensation-in-firmware/ + +The unpause manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Unpause + +==Operation== +The default 'Activate Unpause' checkbox is off. When it is on, the functions described below will work, when it is off, nothing will be done. + +==Settings== +===Delay=== +Default is 28 milliseconds, which Shane found for the Arduino. + +Defines the delay on the microprocessor that will be at least partially compensated for. + +===Maximum Speed=== +Default is 1.3. + +Defines the maximum amount that the feed rate will be sped up to, compared to the original feed rate. + +==Examples== +The following examples unpause the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and unpause.py. + +> python unpause.py +This brings up the unpause dialog. + +> python unpause.py Screw Holder Bottom.stl +The unpause tool is parsing the file: +Screw Holder Bottom.stl +.. +The unpause tool has created the file: +.. Screw Holder Bottom_unpause.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import sys + + +__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 getCraftedText( fileName, gcodeText, repository=None): + "Unpause a gcode linear move file or text." + return getCraftedTextFromText( archive.getTextIfEmpty( fileName, gcodeText ), repository ) + +def getCraftedTextFromText(gcodeText, repository=None): + "Unpause a gcode linear move text." + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'unpause'): + return gcodeText + if repository == None: + repository = settings.getReadRepository( UnpauseRepository() ) + if not repository.activateUnpause.value: + return gcodeText + return UnpauseSkein().getCraftedGcode(gcodeText, repository) + +def getNewRepository(): + 'Get new repository.' + return UnpauseRepository() + +def getSelectedPlugin(repository): + "Get the selected plugin." + for plugin in repository.unpausePlugins: + if plugin.value: + return plugin + return None + +def writeOutput(fileName, shouldAnalyze=True): + "Unpause a gcode linear move file." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'unpause', shouldAnalyze) + + +class UnpauseRepository: + "A class to handle the unpause settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.unpause.html', self) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Unpause', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Unpause') + self.activateUnpause = settings.BooleanSetting().getFromValue('Activate Unpause', self, False ) + self.delay = settings.FloatSpin().getFromValue( 2.0, 'Delay (milliseconds):', self, 42.0, 28.0 ) + self.maximumSpeed = settings.FloatSpin().getFromValue( 1.1, 'Maximum Speed (ratio):', self, 1.9, 1.3 ) + self.executeTitle = 'Unpause' + + def execute(self): + "Unpause button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class UnpauseSkein: + "A class to unpause a skein of extrusions." + def __init__(self): + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.feedRateMinute = 959.0 + self.lineIndex = 0 + self.lines = None + self.oldLocation = None + + def getCraftedGcode(self, gcodeText, repository): + "Parse gcode text and store the unpause gcode." + self.delaySecond = repository.delay.value * 0.001 + self.maximumSpeed = repository.maximumSpeed.value + self.minimumSpeedUpReciprocal = 1.0 / self.maximumSpeed + self.repository = repository + self.lines = archive.getTextLines(gcodeText) + self.parseInitialization() + for self.lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[self.lineIndex] + self.parseLine(line) + return self.distanceFeedRate.output.getvalue() + + def getUnpausedArcMovement( self, line, splitLine ): + "Get an unpaused arc movement." + self.feedRateMinute = gcodec.getFeedRateMinute( self.feedRateMinute, splitLine ) + if self.oldLocation == None: + return line + relativeLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + self.oldLocation += relativeLocation + distance = gcodec.getArcDistance(relativeLocation, splitLine) + return self.getUnpausedMovement(distance, line, splitLine) + + def getUnpausedLinearMovement( self, line, splitLine ): + "Get an unpaused linear movement." + self.feedRateMinute = gcodec.getFeedRateMinute( self.feedRateMinute, splitLine ) + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + if self.oldLocation == None: + self.oldLocation = location + return line + distance = abs(self.oldLocation - location) + self.oldLocation = location + return self.getUnpausedMovement(distance, line, splitLine) + + def getUnpausedMovement(self, distance, line, splitLine): + "Get an unpaused movement." + if distance <= 0.0: + return line + resultantReciprocal = 1.0 - self.delaySecond / distance * self.feedRateMinute / 60.0 + resultantReciprocal = max(self.minimumSpeedUpReciprocal, resultantReciprocal) + return self.distanceFeedRate.getLineWithFeedRate(self.feedRateMinute / resultantReciprocal, line, splitLine) + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('unpause') + return + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + "Parse a gcode line." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + line = self.getUnpausedLinearMovement( line, splitLine ) + if firstWord == 'G2' or firstWord == 'G3': + line = self.getUnpausedArcMovement( line, splitLine ) + self.distanceFeedRate.addLine(line) + + +def main(): + "Display the unpause dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/whittle.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/whittle.py new file mode 100644 index 0000000..38130dc --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/whittle.py @@ -0,0 +1,172 @@ +""" +This page is in the table of contents. +Whittle will convert each polygon of a gcode file into a helix which has a vertical step down on each rotation. + +==Operation== +The default 'Activate Whittle' checkbox is on. When it is on, the functions described below will work, when it is off, the functions will not be called. If the cutting tool can cut the slab in one cut, the 'Activate Whittle' checkbox should be off, the default is off. + +==Settings== +===Maximum Vertical Step'=== +Default is 0.1 mm. + +Defines the maximum distance that the helix will step down on each rotation. The number of steps in the helix will be the layer height divided by the 'Maximum Vertical Step', rounded up. The amount the helix will step down is the layer height divided by the number of steps. The thinner the 'Maximum Vertical Step', the more times the cutting tool will circle around on its way to the bottom of the slab. + +==Examples== +The following examples whittle the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and whittle.py. + +> python whittle.py +This brings up the whittle dialog. + +> python whittle.py Screw Holder Bottom.stl +The whittle tool is parsing the file: +Screw Holder Bottom.stl +.. +The whittle tool has created the file: +.. Screw Holder Bottom_whittle.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import archive +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import sys + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/02/05 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCraftedText( fileName, text='', whittleRepository = None ): + "Whittle the preface file or text." + return getCraftedTextFromText( archive.getTextIfEmpty(fileName, text), whittleRepository ) + +def getCraftedTextFromText( gcodeText, whittleRepository = None ): + "Whittle the preface gcode text." + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'whittle'): + return gcodeText + if whittleRepository == None: + whittleRepository = settings.getReadRepository( WhittleRepository() ) + if not whittleRepository.activateWhittle.value: + return gcodeText + return WhittleSkein().getCraftedGcode( whittleRepository, gcodeText ) + +def getNewRepository(): + 'Get new repository.' + return WhittleRepository() + +def writeOutput(fileName, shouldAnalyze=True): + "Whittle the carving of a gcode file." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'whittle', shouldAnalyze) + + +class WhittleRepository: + "A class to handle the whittle settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.whittle.html', self ) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File to be Whittled', self, '') + self.activateWhittle = settings.BooleanSetting().getFromValue('Activate Whittle', self, False ) + self.maximumVerticalStep = settings.FloatSpin().getFromValue( 0.02, 'Maximum Vertical Step (mm):', self, 0.42, 0.1 ) + self.executeTitle = 'Whittle' + + def execute(self): + "Whittle button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class WhittleSkein: + "A class to whittle a skein of extrusions." + def __init__(self): + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.layerHeight = 0.3333333333 + self.lineIndex = 0 + self.movementLines = [] + self.oldLocation = None + + def getCraftedGcode( self, whittleRepository, gcodeText ): + "Parse gcode text and store the whittle gcode." + self.whittleRepository = whittleRepository + self.lines = archive.getTextLines(gcodeText) + self.parseInitialization() + for line in self.lines[self.lineIndex :]: + self.parseLine(line) + return self.distanceFeedRate.output.getvalue() + + def getLinearMove( self, line, splitLine ): + "Get the linear move." + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + self.movementLines.append(line) + z = location.z + self.layerDeltas[0] + self.oldLocation = location + return self.distanceFeedRate.getLineWithZ( line, splitLine, z ) + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex].lstrip() + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('whittle') + return + elif firstWord == '(': + self.setLayerThinknessVerticalDeltas(splitLine) + self.distanceFeedRate.addTagBracketedLine('layerStep', self.layerStep ) + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + "Parse a gcode line and add it to the whittle skein." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + line = self.getLinearMove( line, splitLine ) + elif firstWord == 'M103': + self.repeatLines() + self.distanceFeedRate.addLine(line) + + def repeatLines(self): + "Repeat the lines at decreasing altitude." + for layerDelta in self.layerDeltas[1 :]: + for movementLine in self.movementLines: + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(movementLine) + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + z = location.z + layerDelta + self.distanceFeedRate.addLine( self.distanceFeedRate.getLineWithZ( movementLine, splitLine, z ) ) + self.movementLines = [] + + def setLayerThinknessVerticalDeltas( self, splitLine ): + "Set the layer height and the vertical deltas." + self.layerHeight = float(splitLine[1]) + numberOfSteps = int( math.ceil( self.layerHeight / self.whittleRepository.maximumVerticalStep.value ) ) + self.layerStep = self.layerHeight / float( numberOfSteps ) + self.layerDeltas = [] + halfDeltaMinusHalfTop = 0.5 * self.layerStep * ( 1.0 - numberOfSteps ) + for layerDeltaIndex in xrange( numberOfSteps - 1, - 1, - 1 ): + layerDelta = layerDeltaIndex * self.layerStep + halfDeltaMinusHalfTop + self.layerDeltas.append( layerDelta ) + + +def main(): + "Display the whittle dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/widen.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/widen.py new file mode 100644 index 0000000..432b34c --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/widen.py @@ -0,0 +1,225 @@ +#! /usr/bin/env python +""" +This page is in the table of contents. +Widen will widen the outside edges away from the inside edges, so that the outsides will be at least two edge widths away from the insides and therefore the outside filaments will not overlap the inside filaments. + +For example, if a mug has a very thin wall, widen would widen the outside of the mug so that the wall of the mug would be two edge widths wide, and the outside wall filament would not overlap the inside filament. + +For another example, if the outside of the object runs right next to a hole, widen would widen the wall around the hole so that the wall would bulge out around the hole, and the outside filament would not overlap the hole filament. + +The widen manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Widen + +==Operation== +The default 'Activate Widen' checkbox is off. When it is on, widen will work, when it is off, nothing will be done. + +==Examples== +The following examples widen the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and widen.py. + +> python widen.py +This brings up the widen dialog. + +> python widen.py Screw Holder Bottom.stl +The widen tool is parsing the file: +Screw Holder Bottom.stl +.. +The widen tool has created the file: +.. Screw Holder Bottom_widen.gcode + +""" + +from __future__ import absolute_import +try: + import psyco + psyco.full() +except: + pass +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities.geometry.geometry_utilities import boolean_solid +from fabmetheus_utilities.geometry.solids import triangle_mesh +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import intercircle +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import os +import sys + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/28/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getCraftedText(fileName, text='', repository=None): + 'Widen the preface file or text.' + return getCraftedTextFromText(archive.getTextIfEmpty(fileName, text), repository) + +def getCraftedTextFromText(gcodeText, repository=None): + 'Widen the preface gcode text.' + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'widen'): + return gcodeText + if repository == None: + repository = settings.getReadRepository( WidenRepository() ) + if not repository.activateWiden.value: + return gcodeText + return WidenSkein().getCraftedGcode(gcodeText, repository) + +def getIntersectingWithinLoops(loop, loopList, outsetLoop): + 'Get the loops which are intersecting or which it is within.' + intersectingWithinLoops = [] + for otherLoop in loopList: + if getIsIntersectingWithinLoop(loop, otherLoop, outsetLoop): + intersectingWithinLoops.append(otherLoop) + return intersectingWithinLoops + +def getIsIntersectingWithinLoop(loop, otherLoop, outsetLoop): + 'Determine if the loop is intersecting or is within the other loop.' + if euclidean.isLoopIntersectingLoop(loop, otherLoop): + return True + return euclidean.isPathInsideLoop(otherLoop, loop) != euclidean.isPathInsideLoop(otherLoop, outsetLoop) + +def getIsPointInsideALoop(loops, point): + 'Determine if a point is inside a loop of a loop list.' + for loop in loops: + if euclidean.isPointInsideLoop(loop, point): + return True + return False + +def getNewRepository(): + 'Get new repository.' + return WidenRepository() + +def getWidenedLoop(loop, loopList, outsetLoop, radius): + 'Get the widened loop.' + intersectingWithinLoops = getIntersectingWithinLoops(loop, loopList, outsetLoop) + if len(intersectingWithinLoops) < 1: + return loop + loopsUnified = boolean_solid.getLoopsUnion(radius, [[loop], intersectingWithinLoops]) + if len(loopsUnified) < 1: + return loop + return euclidean.getLargestLoop(loopsUnified) + +def writeOutput(fileName, shouldAnalyze=True): + 'Widen the carving of a gcode file.' + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'widen', shouldAnalyze) + + +class WidenRepository: + 'A class to handle the widen settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.widen.html', self) + self.fileNameInput = settings.FileNameInput().getFromFileName( + fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Widen', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute( + 'http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Widen') + self.activateWiden = settings.BooleanSetting().getFromValue('Activate Widen', self, False) + self.executeTitle = 'Widen' + + def execute(self): + 'Widen button has been clicked.' + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode( + self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class WidenSkein: + 'A class to widen a skein of extrusions.' + def __init__(self): + self.boundary = None + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.layerCount = settings.LayerCount() + self.lineIndex = 0 + self.loopLayer = None + + def addWiden(self, loopLayer): + 'Add widen to the layer.' + triangle_mesh.sortLoopsInOrderOfArea(False, loopLayer.loops) + widdershinsLoops = [] + clockwiseInsetLoops = [] + for loopIndex in xrange(len(loopLayer.loops)): + loop = loopLayer.loops[loopIndex] + if euclidean.isWiddershins(loop): + otherLoops = loopLayer.loops[: loopIndex] + loopLayer.loops[loopIndex + 1 :] + leftPoint = euclidean.getLeftPoint(loop) + if getIsPointInsideALoop(otherLoops, leftPoint): + self.distanceFeedRate.addGcodeFromLoop(loop, loopLayer.z) + else: + widdershinsLoops.append(loop) + else: +# clockwiseInsetLoop = intercircle.getLargestInsetLoopFromLoop(loop, self.doubleEdgeWidth) +# clockwiseInsetLoop.reverse() +# clockwiseInsetLoops.append(clockwiseInsetLoop) + clockwiseInsetLoops += intercircle.getInsetLoopsFromLoop(loop, self.doubleEdgeWidth) + self.distanceFeedRate.addGcodeFromLoop(loop, loopLayer.z) + for widdershinsLoop in widdershinsLoops: + outsetLoop = intercircle.getLargestInsetLoopFromLoop(widdershinsLoop, -self.doubleEdgeWidth) + widenedLoop = getWidenedLoop(widdershinsLoop, clockwiseInsetLoops, outsetLoop, self.edgeWidth) + self.distanceFeedRate.addGcodeFromLoop(widenedLoop, loopLayer.z) + + def getCraftedGcode(self, gcodeText, repository): + 'Parse gcode text and store the widen gcode.' + self.repository = repository + self.lines = archive.getTextLines(gcodeText) + self.parseInitialization() + for line in self.lines[self.lineIndex :]: + self.parseLine(line) + return self.distanceFeedRate.output.getvalue() + + def parseInitialization(self): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('widen') + elif firstWord == '()': + self.distanceFeedRate.addLine(line) + return + elif firstWord == '(': + self.edgeWidth = float(splitLine[1]) + self.doubleEdgeWidth = 2.0 * self.edgeWidth + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + 'Parse a gcode line and add it to the widen skein.' + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == '(': + location = gcodec.getLocationFromSplitLine(None, splitLine) + self.boundary.append(location.dropAxis()) + elif firstWord == '(': + self.layerCount.printProgressIncrement('widen') + self.loopLayer = euclidean.LoopLayer(float(splitLine[1])) + self.distanceFeedRate.addLine(line) + elif firstWord == '()': + self.addWiden( self.loopLayer ) + self.loopLayer = None + elif firstWord == '()': + self.boundary = [] + self.loopLayer.loops.append( self.boundary ) + if self.loopLayer == None: + self.distanceFeedRate.addLine(line) + + +def main(): + 'Display the widen dialog.' + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/wipe.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/wipe.py new file mode 100644 index 0000000..ac662ec --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/craft_plugins/wipe.py @@ -0,0 +1,267 @@ +""" +This page is in the table of contents. +At the beginning of a layer, depending on the settings, wipe will move the nozzle with the extruder off to the arrival point, then to the wipe point, then to the departure point, then back to the layer. + +The wipe path is machine specific, so you'll probably have to change all the default locations. + +The wipe manual page is at: +http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Wipe + +==Operation== +The default 'Activate Wipe' checkbox is off. When it is on, the functions described below will work, when it is off, nothing will be done. + +==Settings== +===Arrival Location=== +====Arrival X==== +Default is minus seventy millimeters. + +Defines the x coordinate of the arrival location. + +====Arrival Y==== +Default is minus fifty millimeters. + +Defines the y coordinate of the arrival location. + +====Arrival Z==== +Default is fifty millimeters. + +Defines the z coordinate of the arrival location. + +===Departure Location=== +====Departure X==== +Default is minus seventy millimeters. + +Defines the x coordinate of the departure location. + +====Departure Y==== +Default is minus forty millimeters. + +Defines the y coordinate of the departure location. + +====Departure Z==== +Default is fifty millimeters. + +Defines the z coordinate of the departure location. + +===Wipe Location=== +====Wipe X==== +Default is minus seventy millimeters. + +Defines the x coordinate of the wipe location. + +====Wipe Y==== +Default is minus seventy millimeters. + +Defines the y coordinate of the wipe location. + +====Wipe Z==== +Default is fifty millimeters. + +Defines the z coordinate of the wipe location. + +===Wipe Period=== +Default is three. + +Defines the number of layers between wipes. Wipe will always wipe just before layer zero, afterwards it will wipe every "Wipe Period" layers. With the default of three, wipe will wipe just before layer zero, layer three, layer six and so on. + +==Examples== +The following examples wipe the file Screw Holder Bottom.stl. The examples are run in a terminal in the folder which contains Screw Holder Bottom.stl and wipe.py. + +> python wipe.py +This brings up the wipe dialog. + +> python wipe.py Screw Holder Bottom.stl +The wipe tool is parsing the file: +Screw Holder Bottom.stl +.. +The wipe tool has created the file: +.. Screw Holder Bottom_wipe.gcode + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities.vector3 import Vector3 +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_craft +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import math +import sys + + +__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 getCraftedText( fileName, text, wipeRepository = None ): + "Wipe a gcode linear move text." + return getCraftedTextFromText( archive.getTextIfEmpty(fileName, text), wipeRepository ) + +def getCraftedTextFromText( gcodeText, wipeRepository = None ): + "Wipe a gcode linear move text." + if gcodec.isProcedureDoneOrFileIsEmpty( gcodeText, 'wipe'): + return gcodeText + if wipeRepository == None: + wipeRepository = settings.getReadRepository( WipeRepository() ) + if not wipeRepository.activateWipe.value: + return gcodeText + return WipeSkein().getCraftedGcode( gcodeText, wipeRepository ) + +def getNewRepository(): + 'Get new repository.' + return WipeRepository() + +def writeOutput(fileName, shouldAnalyze=True): + "Wipe a gcode linear move file." + skeinforge_craft.writeChainTextWithNounMessage(fileName, 'wipe', shouldAnalyze) + + +class WipeRepository: + "A class to handle the wipe settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.wipe.html', self) + self.fileNameInput = settings.FileNameInput().getFromFileName(fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Wipe', self, '') + self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Wipe') + self.activateWipe = settings.BooleanSetting().getFromValue('Activate Wipe', self, False) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Arrival Location -', self) + self.locationArrivalX = settings.FloatSpin().getFromValue(-100.0, 'Arrival X (mm):', self, 100.0, -70.0) + self.locationArrivalY = settings.FloatSpin().getFromValue(-100.0, 'Arrival Y (mm):', self, 100.0, -50.0) + self.locationArrivalZ = settings.FloatSpin().getFromValue(-100.0, 'Arrival Z (mm):', self, 100.0, 50.0) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Departure Location -', self) + self.locationDepartureX = settings.FloatSpin().getFromValue(-100.0, 'Departure X (mm):', self, 100.0, -70.0) + self.locationDepartureY = settings.FloatSpin().getFromValue(-100.0, 'Departure Y (mm):', self, 100.0, -40.0) + self.locationDepartureZ = settings.FloatSpin().getFromValue(-100.0, 'Departure Z (mm):', self, 100.0, 50.0) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Wipe Location -', self) + self.locationWipeX = settings.FloatSpin().getFromValue(-100.0, 'Wipe X (mm):', self, 100.0, -70.0) + self.locationWipeY = settings.FloatSpin().getFromValue(-100.0, 'Wipe Y (mm):', self, 100.0, -70.0) + self.locationWipeZ = settings.FloatSpin().getFromValue(-100.0, 'Wipe Z (mm):', self, 100.0, 50.0) + settings.LabelSeparator().getFromRepository(self) + self.wipePeriod = settings.IntSpin().getFromValue(1, 'Wipe Period (layers):', self, 5, 3) + self.executeTitle = 'Wipe' + + def execute(self): + "Wipe button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) + for fileName in fileNames: + writeOutput(fileName) + + +class WipeSkein: + "A class to wipe a skein of extrusions." + def __init__(self): + self.distanceFeedRate = gcodec.DistanceFeedRate() + self.extruderActive = False + self.highestZ = None + self.layerIndex = -1 + self.lineIndex = 0 + self.lines = None + self.oldLocation = None + self.shouldWipe = False + self.travelFeedRateMinute = 957.0 + + def addHop( self, begin, end ): + "Add hop to highest point." + beginEndDistance = begin.distance(end) + if beginEndDistance < 3.0 * self.absoluteEdgeWidth: + return + alongWay = self.absoluteEdgeWidth / beginEndDistance + closeToOldLocation = euclidean.getIntermediateLocation( alongWay, begin, end ) + closeToOldLocation.z = self.highestZ + self.distanceFeedRate.addLine( self.getLinearMoveWithFeedRate( self.travelFeedRateMinute, closeToOldLocation ) ) + closeToOldArrival = euclidean.getIntermediateLocation( alongWay, end, begin ) + closeToOldArrival.z = self.highestZ + self.distanceFeedRate.addLine( self.getLinearMoveWithFeedRate( self.travelFeedRateMinute, closeToOldArrival ) ) + + def addWipeTravel( self, splitLine ): + "Add the wipe travel gcode." + location = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + self.highestZ = max( self.highestZ, location.z ) + if not self.shouldWipe: + return + self.shouldWipe = False + if self.extruderActive: + self.distanceFeedRate.addLine('M103') + if self.oldLocation != None: + self.addHop( self.oldLocation, self.locationArrival ) + self.distanceFeedRate.addLine( self.getLinearMoveWithFeedRate( self.travelFeedRateMinute, self.locationArrival ) ) + self.distanceFeedRate.addLine( self.getLinearMoveWithFeedRate( self.travelFeedRateMinute, self.locationWipe ) ) + self.distanceFeedRate.addLine( self.getLinearMoveWithFeedRate( self.travelFeedRateMinute, self.locationDeparture ) ) + self.addHop( self.locationDeparture, location ) + if self.extruderActive: + self.distanceFeedRate.addLine('M101') + + def getCraftedGcode( self, gcodeText, wipeRepository ): + "Parse gcode text and store the wipe gcode." + self.lines = archive.getTextLines(gcodeText) + self.wipePeriod = wipeRepository.wipePeriod.value + self.parseInitialization( wipeRepository ) + self.locationArrival = Vector3( wipeRepository.locationArrivalX.value, wipeRepository.locationArrivalY.value, wipeRepository.locationArrivalZ.value ) + self.locationDeparture = Vector3( wipeRepository.locationDepartureX.value, wipeRepository.locationDepartureY.value, wipeRepository.locationDepartureZ.value ) + self.locationWipe = Vector3( wipeRepository.locationWipeX.value, wipeRepository.locationWipeY.value, wipeRepository.locationWipeZ.value ) + for self.lineIndex in xrange(self.lineIndex, len(self.lines)): + line = self.lines[self.lineIndex] + self.parseLine(line) + return self.distanceFeedRate.output.getvalue() + + def getLinearMoveWithFeedRate( self, feedRate, location ): + "Get a linear move line with the feedRate." + return self.distanceFeedRate.getLinearGcodeMovementWithFeedRate( feedRate, location.dropAxis(), location.z ) + + def parseInitialization( self, wipeRepository ): + 'Parse gcode initialization and store the parameters.' + for self.lineIndex in xrange(len(self.lines)): + line = self.lines[self.lineIndex] + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + firstWord = gcodec.getFirstWord(splitLine) + self.distanceFeedRate.parseSplitLine(firstWord, splitLine) + if firstWord == '()': + self.distanceFeedRate.addTagBracketedProcedure('wipe') + return + elif firstWord == '(': + self.absoluteEdgeWidth = abs(float(splitLine[1])) + elif firstWord == '(': + self.travelFeedRateMinute = 60.0 * float(splitLine[1]) + self.distanceFeedRate.addLine(line) + + def parseLine(self, line): + "Parse a gcode line and add it to the bevel gcode." + splitLine = gcodec.getSplitLineBeforeBracketSemicolon(line) + if len(splitLine) < 1: + return + firstWord = splitLine[0] + if firstWord == 'G1': + self.addWipeTravel(splitLine) + self.oldLocation = gcodec.getLocationFromSplitLine(self.oldLocation, splitLine) + elif firstWord == '(': + self.layerIndex += 1 + settings.printProgress(self.layerIndex, 'wipe') + if self.layerIndex % self.wipePeriod == 0: + self.shouldWipe = True + elif firstWord == 'M101': + self.extruderActive = True + elif firstWord == 'M103': + self.extruderActive = False + self.distanceFeedRate.addLine(line) + + +def main(): + "Display the wipe dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/help.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/help.py new file mode 100644 index 0000000..8725328 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/help.py @@ -0,0 +1,82 @@ +""" +This page is in the table of contents. +Help has buttons and menu items to open help, blog and forum pages in your primary browser. + + +==Link Buttons== +===Announcements=== +====Fabmetheus Blog==== +The skeinforge announcements blog and the place to post questions, bugs and skeinforge requests. + +===Documentation=== +====Index of Local Documentation==== +The list of the pages in the documentation folder. + +====Wiki Manual==== +The skeinforge wiki with pictures and charts. It is the best and most readable source of skeinforge information and you are welcome to contribute. + +====Skeinforge Overview==== +A general description of skeinforge, has answers to frequently asked questions and has many links to skeinforge, fabrication and python pages. It is also the help page of the skeinforge tool. + +===Forums=== +====Bits from Bytes Printing Board==== +Board about printing questions, problems and solutions. Most of the people on that forum use the rapman, but many of the solutions apply to any reprap. + +====Bits from Bytes Software Board==== +Board about software, and has some skeinforge threads. + +====Skeinforge Contributions Thread==== +Forum thread about how to contribute to skeinforge development. + +====Skeinforge Settings Thread==== +Forum thread for people to post, download and discuss skeinforge settings. + +==Settings== +===Wiki Manual Primary=== +Default is on. + +The help menu has an item for each button on the help page. Also, at the very top, it has a link to the local documentation and if there is a separate page for that tool in the wiki manual, a link to that page on the manual. If the 'Wiki Manual Primary' checkbutton is selected and there is a separate wiki manual page, the wiki page will be the primary document page, otherwise the local page will be primary. The help button (? symbol button) on the tool page will open the primary page, as will pressing . For example, if you click the the help button from the chamber tool, which has a separate page in the wiki, and 'Wiki Manual Primary' is selected, the wiki manual chamber page will be opened. Clicking F1 will also open the wiki manual chamber page. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_help +import os + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def addToMenu( master, menu, repository, window ): + "Add a tool plugin menu." + path = settings.getPathInFabmetheusFromFileNameHelp( repository.fileNameHelp ) + capitalizedBasename = os.path.basename(path).capitalize() + helpRepository = settings.getReadRepository( skeinforge_help.HelpRepository() ) + if repository.openWikiManualHelpPage != None and helpRepository.wikiManualPrimary.value: + menu.add_command( label = 'Local ' + capitalizedBasename, command = repository.openLocalHelpPage ) + else: + settings.addAcceleratorCommand('', repository.openLocalHelpPage, master, menu, 'Local ' + capitalizedBasename ) + if repository.openWikiManualHelpPage != None: + if helpRepository.wikiManualPrimary.value: + settings.addAcceleratorCommand('', repository.openWikiManualHelpPage, master, menu, 'Wiki Manual ' + capitalizedBasename ) + else: + menu.add_command( label = 'Wiki Manual ' + capitalizedBasename, command = repository.openWikiManualHelpPage ) + menu.add_separator() + settings.addMenuEntitiesToMenu( menu, helpRepository.menuEntities ) + +def getNewRepository(): + 'Get new repository.' + return skeinforge_help.HelpRepository() + +def main(): + "Display the help dialog." + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/meta.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/meta.py new file mode 100644 index 0000000..8b9ec49 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/meta.py @@ -0,0 +1,36 @@ +""" +This page is in the table of contents. +Meta is a script to access the plugins which handle meta information. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_meta + + +__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 addToMenu( master, menu, repository, window ): + "Add a tool plugin menu." + metaFilePath = archive.getSkeinforgePluginsPath('meta.py') + settings.addPluginsParentToMenu(skeinforge_meta.getPluginsDirectoryPath(), menu, metaFilePath, skeinforge_meta.getPluginFileNames()) + +def getNewRepository(): + 'Get new repository.' + return skeinforge_meta.MetaRepository() + + +def main(): + "Display the meta dialog." + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/meta_plugins/__init__.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/meta_plugins/__init__.py new file mode 100644 index 0000000..1121e8a --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/meta_plugins/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 3 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/meta_plugins/description.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/meta_plugins/description.py new file mode 100644 index 0000000..8ce2a72 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/meta_plugins/description.py @@ -0,0 +1,51 @@ +""" +This page is in the table of contents. +Description is a script to store a description of the profile. + +==Settings== +===Description Text=== +Default is 'Write your profile description here.' + +The suggested format is a description, followed by a link to a profile post or web page. + +==Example== +Example of using description follows below. + +> python description.py +This brings up the description dialog. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_profile + + +__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 getNewRepository(): + 'Get new repository.' + return DescriptionRepository() + + +class DescriptionRepository: + "A class to handle the description settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.meta_plugins.description.html', self) + description = 'Write your description of the profile here.\n\nSuggested format is a description, followed by a link to the profile post or web page.' + self.descriptionText = settings.TextSetting().getFromValue('Description Text:', self, description) + + +def main(): + "Display the file or directory dialog." + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/meta_plugins/polyfile.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/meta_plugins/polyfile.py new file mode 100644 index 0000000..6d377b7 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/meta_plugins/polyfile.py @@ -0,0 +1,45 @@ +""" +This page is in the table of contents. +Polyfile is a script to choose whether the skeinforge toolchain will operate on one file or all the files in a directory. + +==Settings== +===Polyfile Choice=== +Default is 'Execute File', + +====Execute File==== +When selected, the toolchain will operate on only the chosen file. + +====Execute All Unmodified Files in a Directory'==== +When selected, the toolchain will operate on all the unmodifed files in the directory that the chosen file is in. + +==Example== +Example of using polyfile follows below. + +> python polyfile.py +This brings up the polyfile dialog. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile + + +__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 getNewRepository(): + 'Get new repository.' + return skeinforge_polyfile.PolyfileRepository() + + +def main(): + "Display the file or directory dialog." + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/profile.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/profile.py new file mode 100644 index 0000000..808d482 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/profile.py @@ -0,0 +1,116 @@ +""" +This page is in the table of contents. +Profile is a script to set the craft types setting for the skeinforge chain. + +Profile presents the user with a choice of the craft types in the profile_plugins folder. The chosen craft type is used to determine the craft type profile for the skeinforge chain. The default craft type is extrusion. + +The setting is the selection. If you hit 'Save and Close' the selection will be saved, if you hit 'Cancel' the selection will not be saved. + +To change the profile setting, in a shell in the profile folder type: +> python profile.py + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import os + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def addSubmenus( craftTypeName, menu, pluginFileName, pluginPath, profileRadioVar ): + "Add a tool plugin menu." + submenu = settings.Tkinter.Menu( menu, tearoff = 0 ) + menu.add_cascade( label = pluginFileName.capitalize(), menu = submenu ) + settings.ToolDialog().addPluginToMenu( submenu, pluginPath ) + submenu.add_separator() + pluginModule = skeinforge_profile.getCraftTypePluginModule( pluginFileName ) + profilePluginSettings = settings.getReadRepository( pluginModule.getNewRepository() ) + isSelected = ( craftTypeName == pluginFileName ) + for profileName in profilePluginSettings.profileList.value: + value = isSelected and profileName == profilePluginSettings.profileListbox.value + ProfileMenuRadio( pluginFileName, submenu, profileName, profileRadioVar, value ) + +def addToMenu( master, menu, repository, window ): + "Add a tool plugin menu." + ProfileMenuSaveListener( menu, window ) + +def addToProfileMenu( menu ): + "Add a profile menu." + settings.ToolDialog().addPluginToMenu(menu, archive.getUntilDot(archive.getSkeinforgePluginsPath('profile.py'))) + menu.add_separator() + directoryPath = skeinforge_profile.getPluginsDirectoryPath() + pluginFileNames = skeinforge_profile.getPluginFileNames() + craftTypeName = skeinforge_profile.getCraftTypeName() + profileRadioVar = settings.Tkinter.StringVar() + for pluginFileName in pluginFileNames: + addSubmenus( craftTypeName, menu, pluginFileName, os.path.join( directoryPath, pluginFileName ), profileRadioVar ) + +def getNewRepository(): + 'Get new repository.' + return skeinforge_profile.ProfileRepository() + + +class ProfileMenuRadio: + "A class to display a profile menu radio button." + def __init__( self, profilePluginFileName, menu, name, radioVar, value ): + "Create a profile menu radio." + self.activate = False + self.menu = menu + self.name = name + self.profileJoinName = profilePluginFileName + '.& /' + name + self.profilePluginFileName = profilePluginFileName + self.radioVar = radioVar + menu.add_radiobutton( label = name.replace('_', ' '), command = self.clickRadio, value = self.profileJoinName, variable = self.radioVar ) + self.menuLength = menu.index( settings.Tkinter.END ) + if value: + self.radioVar.set( self.profileJoinName ) + self.menu.invoke( self.menuLength ) + self.activate = True + + def clickRadio(self): + "Workaround for Tkinter bug, invoke and set the value when clicked." + if not self.activate: + return + self.radioVar.set( self.profileJoinName ) + pluginModule = skeinforge_profile.getCraftTypePluginModule( self.profilePluginFileName ) + profilePluginSettings = settings.getReadRepository( pluginModule.getNewRepository() ) + profilePluginSettings.profileListbox.value = self.name + settings.writeSettings( profilePluginSettings ) + profileSettings = skeinforge_profile.getReadProfileRepository() + plugins = profileSettings.craftRadios + for plugin in plugins: + plugin.value = ( plugin.name == self.profilePluginFileName ) + settings.writeSettings( profileSettings ) + skeinforge_profile.updateProfileSaveListeners() + + +class ProfileMenuSaveListener: + "A class to update a profile menu." + def __init__( self, menu, window ): + "Set the menu." + self.menu = menu + addToProfileMenu( menu ) + euclidean.addElementToListDictionaryIfNotThere( self, window, settings.globalProfileSaveListenerListTable ) + + def save(self): + "Profile has been saved and profile menu should be updated." + settings.deleteMenuItems( self.menu ) + addToProfileMenu( self.menu ) + + +def main(): + "Display the profile dialog." + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/profile_plugins/__init__.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/profile_plugins/__init__.py new file mode 100644 index 0000000..1121e8a --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/profile_plugins/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 3 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/profile_plugins/cutting.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/profile_plugins/cutting.py new file mode 100644 index 0000000..0cc3c51 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/profile_plugins/cutting.py @@ -0,0 +1,53 @@ +""" +This page is in the table of contents. +Cutting is a script to set the cutting profile for the skeinforge chain. + +The displayed craft sequence is the sequence in which the tools craft the model and export the output. + +On the cutting dialog, clicking the 'Add Profile' button will duplicate the selected profile and give it the name in the input field. For example, if laser is selected and the name laser_10mm is in the input field, clicking the 'Add Profile' button will duplicate laser and save it as laser_10mm. The 'Delete Profile' button deletes the selected profile. + +The profile selection is the setting. If you hit 'Save and Close' the selection will be saved, if you hit 'Cancel' the selection will not be saved. However; adding and deleting a profile is a permanent action, for example 'Cancel' will not bring back any deleted profiles. + +To change the cutting profile, in a shell in the profile_plugins folder type: +> python cutting.py + +""" + + +from __future__ import absolute_import +import __init__ +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import sys + + +__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 getCraftSequence(): + "Get the cutting craft sequence." + return 'chop preface outset multiply whittle drill lift flow feed home lash fillet limit unpause alteration export'.split() + +def getNewRepository(): + 'Get new repository.' + return CuttingRepository() + + +class CuttingRepository: + "A class to handle the cutting settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsSetCraftProfile( getCraftSequence(), 'end_mill', self, 'skeinforge_application.skeinforge_plugins.profile_plugins.cutting.html') + + +def main(): + "Display the export dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/profile_plugins/extrusion.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/profile_plugins/extrusion.py new file mode 100644 index 0000000..a6f09a9 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/profile_plugins/extrusion.py @@ -0,0 +1,53 @@ +""" +This page is in the table of contents. +Extrusion is a script to set the extrusion profile for the skeinforge chain. + +The displayed craft sequence is the sequence in which the tools craft the model and export the output. + +On the extrusion dialog, clicking the 'Add Profile' button will duplicate the selected profile and give it the name in the input field. For example, if ABS is selected and the name ABS_black is in the input field, clicking the 'Add Profile' button will duplicate ABS and save it as ABS_black. The 'Delete Profile' button deletes the selected profile. + +The profile selection is the setting. If you hit 'Save and Close' the selection will be saved, if you hit 'Cancel' the selection will not be saved. However; adding and deleting a profile is a permanent action, for example 'Cancel' will not bring back any deleted profiles. + +To change the extrusion profile, in a shell in the profile_plugins folder type: +> python extrusion.py + +""" + + +from __future__ import absolute_import +import __init__ +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import sys + + +__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 getCraftSequence(): + 'Get the extrusion craft sequence.' + return 'carve scale bottom preface widen inset fill multiply speed temperature raft skirt chamber tower jitter clip smooth stretch skin comb cool hop wipe oozebane splodge home lash fillet limit unpause dimension alteration export'.split() + +def getNewRepository(): + 'Get new repository.' + return ExtrusionRepository() + + +class ExtrusionRepository: + 'A class to handle the export settings.' + def __init__(self): + 'Set the default settings, execute title & settings fileName.' + skeinforge_profile.addListsSetCraftProfile( getCraftSequence(), 'ABS', self, 'skeinforge_application.skeinforge_plugins.profile_plugins.extrusion.html') + + +def main(): + 'Display the export dialog.' + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == '__main__': + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/profile_plugins/milling.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/profile_plugins/milling.py new file mode 100644 index 0000000..fd682b7 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/profile_plugins/milling.py @@ -0,0 +1,53 @@ +""" +This page is in the table of contents. +Milling is a script to set the milling profile for the skeinforge chain. + +The displayed craft sequence is the sequence in which the tools craft the model and export the output. + +On the milling dialog, clicking the 'Add Profile' button will duplicate the selected profile and give it the name in the input field. For example, if laser is selected and the name laser_10mm is in the input field, clicking the 'Add Profile' button will duplicate laser and save it as laser_10mm. The 'Delete Profile' button deletes the selected profile. + +The profile selection is the setting. If you hit 'Save and Close' the selection will be saved, if you hit 'Cancel' the selection will not be saved. However; adding and deleting a profile is a permanent action, for example 'Cancel' will not bring back any deleted profiles. + +To change the milling profile, in a shell in the profile_plugins folder type: +> python milling.py + +""" + + +from __future__ import absolute_import +import __init__ +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import sys + + +__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 getCraftSequence(): + "Get the milling craft sequence." + return 'chop preface outset mill multiply drill lift flow feed home lash fillet limit unpause alteration export'.split() + +def getNewRepository(): + 'Get new repository.' + return MillingRepository() + + +class MillingRepository: + "A class to handle the milling settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsSetCraftProfile( getCraftSequence(), 'end_mill', self, 'skeinforge_application.skeinforge_plugins.profile_plugins.milling.html') + + +def main(): + "Display the export dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_plugins/profile_plugins/winding.py b/SkeinPyPy/skeinforge_application/skeinforge_plugins/profile_plugins/winding.py new file mode 100644 index 0000000..4cbd165 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_plugins/profile_plugins/winding.py @@ -0,0 +1,53 @@ +""" +This page is in the table of contents. +Winding is a script to set the winding profile for the skeinforge chain. + +The displayed craft sequence is the sequence in which the tools craft the model and export the output. + +On the winding dialog, clicking the 'Add Profile' button will duplicate the selected profile and give it the name in the input field. For example, if laser is selected and the name laser_10mm is in the input field, clicking the 'Add Profile' button will duplicate laser and save it as laser_10mm. The 'Delete Profile' button deletes the selected profile. + +The profile selection is the setting. If you hit 'Save and Close' the selection will be saved, if you hit 'Cancel' the selection will not be saved. However; adding and deleting a profile is a permanent action, for example 'Cancel' will not bring back any deleted profiles. + +To change the winding profile, in a shell in the profile_plugins folder type: +> python winding.py + +""" + + +from __future__ import absolute_import +import __init__ +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import sys + + +__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 getCraftSequence(): + "Get the winding craft sequence." + return 'cleave preface coil flow feed home lash fillet limit unpause alteration export'.split() + +def getNewRepository(): + 'Get new repository.' + return WindingRepository() + + +class WindingRepository: + "A class to handle the winding settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsSetCraftProfile( getCraftSequence(), 'free_wire', self, 'skeinforge_application.skeinforge_plugins.profile_plugins.winding.html') + + +def main(): + "Display the export dialog." + if len(sys.argv) > 1: + writeOutput(' '.join(sys.argv[1 :])) + else: + settings.startMainLoopFromConstructor(getNewRepository()) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_utilities/__init__.py b/SkeinPyPy/skeinforge_application/skeinforge_utilities/__init__.py new file mode 100644 index 0000000..2dc8ddc --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_utilities/__init__.py @@ -0,0 +1,9 @@ +#This is required to workaround the python import bug where relative imports don't work if the module is imported as a main module. +import os +import sys +numberOfLevelsDeepInPackageHierarchy = 2 +packageFilePath = os.path.abspath(__file__) +for level in range( numberOfLevelsDeepInPackageHierarchy + 1 ): + packageFilePath = os.path.dirname( packageFilePath ) +if packageFilePath not in sys.path: + sys.path.insert( 0, packageFilePath ) diff --git a/SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_analyze.py b/SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_analyze.py new file mode 100644 index 0000000..26e30ae --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_analyze.py @@ -0,0 +1,82 @@ +""" +Analyze is a script to access the plugins which analyze a gcode file. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import os +import sys +import traceback + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getNewRepository(): + 'Get new repository.' + return AnalyzeRepository() + +def getPluginFileNames(): + "Get analyze plugin fileNames." + return archive.getPluginFileNamesFromDirectoryPath( getPluginsDirectoryPath() ) + +def getPluginsDirectoryPath(): + "Get the plugins directory path." + return archive.getAnalyzePluginsDirectoryPath() + +def writeOutput(fileName, fileNamePenultimate, fileNameSuffix, filePenultimateWritten, gcodeText=''): + "Analyze a gcode file." + gcodeText = archive.getTextIfEmpty(fileName, gcodeText) + pluginFileNames = getPluginFileNames() + window = None + for pluginFileName in pluginFileNames: + analyzePluginsDirectoryPath = getPluginsDirectoryPath() + pluginModule = archive.getModuleWithDirectoryPath( analyzePluginsDirectoryPath, pluginFileName ) + if pluginModule != None: + try: + newWindow = pluginModule.writeOutput(fileName, fileNamePenultimate, fileNameSuffix, + filePenultimateWritten, gcodeText ) + if newWindow != None: + window = newWindow + except: + print('Warning, the tool %s could not analyze the output.' % pluginFileName ) + print('Exception traceback in writeOutput in skeinforge_analyze:') + traceback.print_exc(file=sys.stdout) + return window + + +class AnalyzeRepository: + "A class to handle the analyze settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_utilities.skeinforge_analyze.html', self) + self.fileNameInput = settings.FileNameInput().getFromFileName( [ ('Gcode text files', '*.gcode') ], 'Open File for Analyze', self, '') + importantFileNames = ['skeiniso', 'skeinlayer', 'statistic'] + settings.getRadioPluginsAddPluginFrame( getPluginsDirectoryPath(), importantFileNames, getPluginFileNames(), self ) + self.executeTitle = 'Analyze' + + def execute(self): + "Analyze button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode( self.fileNameInput.value, [], self.fileNameInput.wasCancelled ) + for fileName in fileNames: + writeOutput( fileName, fileName ) + + +def main(): + "Write analyze output." + fileName = ' '.join(sys.argv[1 :]) + settings.startMainLoopFromWindow(writeOutput(fileName, fileName)) + + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_craft.py b/SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_craft.py new file mode 100644 index 0000000..4a872db --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_craft.py @@ -0,0 +1,237 @@ +""" +Craft is a script to access the plugins which craft a gcode file. + +The plugin buttons which are commonly used are bolded and the ones which are rarely used have normal font weight. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities.fabmetheus_tools import fabmetheus_interpret +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_analyze +from skeinforge_application.skeinforge_utilities import skeinforge_polyfile +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import os +import sys +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 getChainText( fileName, procedure ): + "Get a crafted shape file." + text='' + if fileName.endswith('.gcode') or fileName.endswith('.svg'): + text = archive.getFileText(fileName) + procedures = getProcedures( procedure, text ) + return getChainTextFromProcedures( fileName, procedures, text ) + +def getChainTextFromProcedures(fileName, procedures, text): + 'Get a crafted shape file from a list of procedures.' + lastProcedureTime = time.time() + for procedure in procedures: + craftModule = getCraftModule(procedure) + if craftModule != None: + text = craftModule.getCraftedText(fileName, text) + if text == '': + print('Warning, the text was not recognized in getChainTextFromProcedures in skeinforge_craft for') + print(fileName) + return '' + if gcodec.isProcedureDone( text, procedure ): + print('%s procedure took %s.' % (procedure.capitalize(), euclidean.getDurationString(time.time() - lastProcedureTime))) + lastProcedureTime = time.time() + return text + +def getCraftModule(pluginName): + 'Get craft module.' + return archive.getModuleWithDirectoryPath(getPluginsDirectoryPath(), pluginName) + +def getCraftPreferences(pluginName): + 'Get craft preferences.' + return settings.getReadRepository(getCraftModule(pluginName).getNewRepository()).preferences + +def getCraftValue(preferenceName, preferences): + "Get craft preferences value." + for preference in preferences: + if preference.name.startswith(preferenceName): + return preference.value + return None + +def getLastModule(): + "Get the last tool." + craftSequence = getReadCraftSequence() + if len( craftSequence ) < 1: + return None + return getCraftModule( craftSequence[-1] ) + +def getNewRepository(): + 'Get new repository.' + return CraftRepository() + +def getPluginFileNames(): + "Get craft plugin fileNames." + craftSequence = getReadCraftSequence() + craftSequence.sort() + return craftSequence + +def getPluginsDirectoryPath(): + "Get the plugins directory path." + return archive.getCraftPluginsDirectoryPath() + +def getProcedures( procedure, text ): + "Get the procedures up to and including the given procedure." + craftSequence = getReadCraftSequence() + sequenceIndexPlusOneFromText = getSequenceIndexPlusOneFromText(text) + sequenceIndexFromProcedure = getSequenceIndexFromProcedure(procedure) + return craftSequence[ sequenceIndexPlusOneFromText : sequenceIndexFromProcedure + 1 ] + +def getReadCraftSequence(): + "Get profile sequence." + return skeinforge_profile.getCraftTypePluginModule().getCraftSequence() + +def getSequenceIndexFromProcedure(procedure): + "Get the profile sequence index of the procedure. Return None if the procedure is not in the sequence" + craftSequence = getReadCraftSequence() + if procedure not in craftSequence: + return 0 + return craftSequence.index(procedure) + +def getSequenceIndexPlusOneFromText(fileText): + "Get the profile sequence index of the file plus one. Return zero if the procedure is not in the file" + craftSequence = getReadCraftSequence() + for craftSequenceIndex in xrange( len( craftSequence ) - 1, - 1, - 1 ): + procedure = craftSequence[ craftSequenceIndex ] + if gcodec.isProcedureDone( fileText, procedure ): + return craftSequenceIndex + 1 + return 0 + +def writeChainTextWithNounMessage(fileName, procedure, shouldAnalyze=True): + 'Get and write a crafted shape file.' + print('') + print('The %s tool is parsing the file:' % procedure) + print(os.path.basename(fileName)) + print('') + startTime = time.time() + fileNameSuffix = fileName[: fileName.rfind('.')] + '_' + procedure + '.gcode' + craftText = getChainText(fileName, procedure) + if craftText == '': + print('Warning, there was no text output in writeChainTextWithNounMessage in skeinforge_craft for:') + print(fileName) + return + archive.writeFileText(fileNameSuffix, craftText) + window = None + if shouldAnalyze: + window = skeinforge_analyze.writeOutput(fileName, fileNameSuffix, fileNameSuffix, True, craftText) + print('') + print('The %s tool has created the file:' % procedure) + print(fileNameSuffix) + print('') + print('It took %s to craft the file.' % euclidean.getDurationString(time.time() - startTime)) + return window + +def writeOutput(fileName, shouldAnalyze=True): + "Craft a gcode file with the last module." + pluginModule = getLastModule() + if pluginModule != None: + return pluginModule.writeOutput(fileName, shouldAnalyze) + +def writeSVGTextWithNounMessage(fileName, repository, shouldAnalyze=True): + 'Get and write an svg text and print messages.' + print('') + print('The %s tool is parsing the file:' % repository.lowerName) + print(os.path.basename(fileName)) + print('') + startTime = time.time() + fileNameSuffix = fileName[: fileName.rfind('.')] + '_' + repository.lowerName + '.svg' + craftText = getChainText(fileName, repository.lowerName) + if craftText == '': + return + archive.writeFileText(fileNameSuffix, craftText) + print('') + print('The %s tool has created the file:' % repository.lowerName) + print(fileNameSuffix) + print('') + print('It took %s to craft the file.' % euclidean.getDurationString(time.time() - startTime)) + if shouldAnalyze: + settings.getReadRepository(repository) + settings.openSVGPage(fileNameSuffix, repository.svgViewer.value) + + +class CraftRadioButtonsSaveListener: + "A class to update the craft radio buttons." + def addToDialog( self, gridPosition ): + "Add this to the dialog." + euclidean.addElementToListDictionaryIfNotThere( self, self.repository.repositoryDialog, settings.globalProfileSaveListenerListTable ) + self.gridPosition = gridPosition.getCopy() + self.gridPosition.row = gridPosition.rowStart + self.gridPosition.increment() + self.setRadioButtons() + + def getFromRadioPlugins( self, radioPlugins, repository ): + "Initialize." + self.name = 'CraftRadioButtonsSaveListener' + self.radioPlugins = radioPlugins + self.repository = repository + repository.displayEntities.append(self) + return self + + def save(self): + "Profile has been saved and craft radio plugins should be updated." + self.setRadioButtons() + + def setRadioButtons(self): + "Profile has been saved and craft radio plugins should be updated." + activeRadioPlugins = [] + craftSequence = skeinforge_profile.getCraftTypePluginModule().getCraftSequence() + gridPosition = self.gridPosition.getCopy() + isRadioPluginSelected = False + settings.getReadRepository(self.repository) + for radioPlugin in self.radioPlugins: + if radioPlugin.name in craftSequence: + activeRadioPlugins.append(radioPlugin) + radioPlugin.incrementGridPosition(gridPosition) + if radioPlugin.value: + radioPlugin.setSelect() + isRadioPluginSelected = True + else: + radioPlugin.radiobutton.grid_remove() + if not isRadioPluginSelected: + radioPluginNames = self.repository.importantFileNames + [activeRadioPlugins[0].name] + settings.getSelectedRadioPlugin(radioPluginNames , activeRadioPlugins).setSelect() + self.repository.pluginFrame.update() + + +class CraftRepository: + "A class to handle the craft settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_utilities.skeinforge_craft.html', self) + self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Craft', self, '') + self.importantFileNames = ['carve', 'chop', 'feed', 'flow', 'lift', 'raft', 'speed'] + allCraftNames = archive.getPluginFileNamesFromDirectoryPath(getPluginsDirectoryPath()) + self.radioPlugins = settings.getRadioPluginsAddPluginFrame(getPluginsDirectoryPath(), self.importantFileNames, allCraftNames, self) + CraftRadioButtonsSaveListener().getFromRadioPlugins(self.radioPlugins, self) + self.executeTitle = 'Craft' + + def execute(self): + "Craft button has been clicked." + fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode( self.fileNameInput.value, [], self.fileNameInput.wasCancelled ) + for fileName in fileNames: + writeOutput(fileName) + + +def main(): + "Write craft output." + writeOutput(' '.join(sys.argv[1 :]), False) + +if __name__ == "__main__": + main() diff --git a/SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_help.py b/SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_help.py new file mode 100644 index 0000000..6dad5c7 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_help.py @@ -0,0 +1,62 @@ +""" +Help has buttons and menu items to open help, blog and forum pages in your primary browser. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_profile + + +__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 getNewRepository(): + 'Get new repository.' + return HelpRepository() + + +class HelpRepository: + "A class to handle the help settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_utilities.skeinforge_help.html', self) + announcementsText = '- Announcements - ' + announcementsLabel = settings.LabelDisplay().getFromName(announcementsText, self ) + announcementsLabel.columnspan = 6 + settings.LabelDisplay().getFromName('Fabmetheus Blog, Announcements & Questions:', self ) + settings.HelpPage().getFromNameAfterHTTP('fabmetheus.blogspot.com/', 'Fabmetheus Blog', self ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Documentation -', self ) + settings.LabelDisplay().getFromName('Local Documentation Table of Contents: ', self ) + settings.HelpPage().getFromNameSubName('Contents', self, 'contents.html') + settings.LabelDisplay().getFromName('Wiki Manual with Pictures & Charts: ', self ) + settings.HelpPage().getFromNameAfterHTTP('fabmetheus.crsndoo.com/wiki/index.php/Skeinforge', 'Wiki Manual', self ) + settings.LabelDisplay().getFromName('Skeinforge Overview: ', self ) + settings.HelpPage().getFromNameSubName('Skeinforge Overview', self, 'skeinforge_application.skeinforge.html') + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Search -', self ) + settings.LabelDisplay().getFromName('Reprap Search:', self ) + settings.HelpPage().getFromNameAfterHTTP('members.axion.net/~enrique/search_reprap.html', 'Reprap Search', self ) + settings.LabelDisplay().getFromName('Skeinforge Search:', self ) + settings.HelpPage().getFromNameAfterHTTP('members.axion.net/~enrique/search_skeinforge.html', 'Skeinforge Search', self ) + settings.LabelDisplay().getFromName('Web Search:', self ) + settings.HelpPage().getFromNameAfterHTTP('members.axion.net/~enrique/search_web.html', 'Web Search', self ) + settings.LabelSeparator().getFromRepository(self) + settings.LabelDisplay().getFromName('- Troubleshooting -', self ) + settings.LabelDisplay().getFromName('Skeinforge Forum:', self) + settings.HelpPage().getFromNameAfterHTTP('forums.reprap.org/list.php?154', ' Skeinforge Forum ', self ) + settings.LabelSeparator().getFromRepository(self) + self.version = settings.LabelDisplay().getFromName('Version: ' + archive.getFileText(archive.getVersionFileName()), self) + self.wikiManualPrimary = settings.BooleanSetting().getFromValue('Wiki Manual Primary', self, True ) + self.wikiManualPrimary.setUpdateFunction( self.save ) + + def save(self): + "Write the entities." + settings.writeSettingsPrintMessage(self) diff --git a/SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_meta.py b/SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_meta.py new file mode 100644 index 0000000..b03b9f1 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_meta.py @@ -0,0 +1,41 @@ +""" +Meta is a script to access the plugins which handle meta information. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_profile +import os + + +__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' +__date__ = '$Date: 2008/21/04 $' +__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' + + +def getNewRepository(): + 'Get new repository.' + return MetaRepository() + +def getPluginFileNames(): + "Get meta plugin file names." + return archive.getPluginFileNamesFromDirectoryPath( getPluginsDirectoryPath() ) + +def getPluginsDirectoryPath(): + "Get the plugins directory path." + return archive.getSkeinforgePluginsPath('meta_plugins') + + +class MetaRepository: + "A class to handle the meta settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_utilities.skeinforge_meta.html', self) + importantFileNames = ['polyfile'] + settings.getRadioPluginsAddPluginFrame( getPluginsDirectoryPath(), importantFileNames, getPluginFileNames(), self ) diff --git a/SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_polyfile.py b/SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_polyfile.py new file mode 100644 index 0000000..03f4c41 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_polyfile.py @@ -0,0 +1,70 @@ +""" +Polyfile is a script to choose whether the skeinforge toolchain will operate on one file or all the files in a directory. + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities import settings +from skeinforge_application.skeinforge_utilities import skeinforge_profile + + +__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 getFileOrDirectoryTypes( fileName, fileTypes, wasCancelled ): + "Get the gcode files in the directory the file is in if directory setting is true. Otherwise, return the file in a list." + if isEmptyOrCancelled( fileName, wasCancelled ): + return [] + if isDirectorySetting(): + return archive.getFilesWithFileTypesWithoutWords( fileTypes, [], fileName ) + return [ fileName ] + +def getFileOrDirectoryTypesUnmodifiedGcode(fileName, fileTypes, wasCancelled): + "Get the gcode files in the directory the file is in if directory setting is true. Otherwise, return the file in a list." + if isEmptyOrCancelled(fileName, wasCancelled): + return [] + if isDirectorySetting(): + return archive.getFilesWithFileTypesWithoutWords(fileTypes, [], fileName) + return [fileName] + +def getFileOrGcodeDirectory( fileName, wasCancelled, words = [] ): + "Get the gcode files in the directory the file is in if directory setting is true. Otherwise, return the file in a list." + if isEmptyOrCancelled( fileName, wasCancelled ): + return [] + if isDirectorySetting(): + dotIndex = fileName.rfind('.') + if dotIndex < 0: + print('The file name should have a suffix, like myfile.xml.') + print('Since the file name does not have a suffix, nothing will be done') + suffix = fileName[ dotIndex + 1 : ] + return archive.getFilesWithFileTypeWithoutWords( suffix, words, fileName ) + return [ fileName ] + +def getNewRepository(): + 'Get new repository.' + return PolyfileRepository() + +def isDirectorySetting(): + "Determine if the directory setting is true." + return settings.getReadRepository( PolyfileRepository() ).directorySetting.value + +def isEmptyOrCancelled( fileName, wasCancelled ): + "Determine if the fileName is empty or the dialog was cancelled." + return str(fileName) == '' or str(fileName) == '()' or wasCancelled + + +class PolyfileRepository: + "A class to handle the polyfile settings." + def __init__(self): + "Set the default settings, execute title & settings fileName." + skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_utilities.skeinforge_polyfile.html', self) + self.directoryOrFileChoiceLabel = settings.LabelDisplay().getFromName('Directory or File Choice: ', self ) + directoryLatentStringVar = settings.LatentStringVar() + self.directorySetting = settings.Radio().getFromRadio( directoryLatentStringVar, 'Execute All Unmodified Files in a Directory', self, False ) + self.fileSetting = settings.Radio().getFromRadio( directoryLatentStringVar, 'Execute File', self, True ) diff --git a/SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_profile.py b/SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_profile.py new file mode 100644 index 0000000..6223e13 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/skeinforge_utilities/skeinforge_profile.py @@ -0,0 +1,415 @@ +""" +Profile is a script to set the craft types setting for the skeinforge chain. + +Profile presents the user with a choice of the craft types in the profile_plugins folder. The chosen craft type is used to determine the craft type profile for the skeinforge chain. The default craft type is extrusion. + +The setting is the selection. If you hit 'Save and Close' the selection will be saved, if you hit 'Cancel' the selection will not be saved. + +To change the profile setting, in a shell in the profile folder type: +> python profile.py + +""" + +from __future__ import absolute_import +#Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. +import __init__ + +from fabmetheus_utilities import archive +from fabmetheus_utilities import euclidean +from fabmetheus_utilities import gcodec +from fabmetheus_utilities import settings +import os +import shutil + + +__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 addListsSetCraftProfile( craftSequence, defaultProfile, repository, fileNameHelp ): + "Set the craft profile repository." + settings.addListsToRepository(fileNameHelp, repository) + repository.craftSequenceLabel = settings.LabelDisplay().getFromName('Craft Sequence: ', repository ) + craftToolStrings = [] + for craftTool in craftSequence[ : - 1 ]: + craftToolStrings.append( settings.getEachWordCapitalized( craftTool ) + '->') + craftToolStrings.append( settings.getEachWordCapitalized( craftSequence[-1] ) ) + for craftToolStringIndex in xrange( 0, len( craftToolStrings ), 5 ): + craftLine = ' '.join( craftToolStrings[ craftToolStringIndex : craftToolStringIndex + 5 ] ) + settings.LabelDisplay().getFromName( craftLine, repository ) + settings.LabelDisplay().getFromName('', repository ) + repository.profileList = ProfileList().getFromName('Profile List:', repository ) + repository.profileListbox = ProfileListboxSetting().getFromListSetting( repository.profileList, 'Profile Selection:', repository, defaultProfile ) + repository.addListboxSelection = AddProfile().getFromProfileListboxSettingRepository( repository.profileListbox, repository ) + repository.deleteListboxSelection = DeleteProfile().getFromProfileListboxSettingRepository( repository.profileListbox, repository ) + directoryName = archive.getProfilesPath() + archive.makeDirectory(directoryName) + repository.windowPosition.value = '0+400' + +def addListsToCraftTypeRepository(fileNameHelp, repository): + "Add the value to the lists." + settings.addListsToRepositoryByFunction(fileNameHelp, getProfileDirectory, repository) + dotsMinusOne = fileNameHelp.count('.') - 1 + x = 0 + xAddition = 400 + for step in xrange(dotsMinusOne): + x += xAddition + xAddition /= 2 + repository.windowPosition.value = '%s+0' % x + +def cancelAll(): + "Cancel all the dialogs." + for globalRepositoryDialogValue in settings.getGlobalRepositoryDialogValues(): + globalRepositoryDialogValue.cancel() + +def getCraftTypeName(subName=''): + "Get the craft type from the profile." + profileSettings = getReadProfileRepository() + craftTypeName = settings.getSelectedPluginName( profileSettings.craftRadios ) + if subName == '': + return craftTypeName + return os.path.join( craftTypeName, subName ) + +def getCraftTypePluginModule( craftTypeName = ''): + "Get the craft type plugin module." + if craftTypeName == '': + craftTypeName = getCraftTypeName() + profilePluginsDirectoryPath = getPluginsDirectoryPath() + return archive.getModuleWithDirectoryPath( profilePluginsDirectoryPath, craftTypeName ) + +def getNewRepository(): + 'Get new repository.' + return ProfileRepository() + +def getPluginFileNames(): + "Get analyze plugin fileNames." + return archive.getPluginFileNamesFromDirectoryPath( getPluginsDirectoryPath() ) + +def getPluginsDirectoryPath(): + "Get the plugins directory path." + return archive.getSkeinforgePluginsPath('profile_plugins') + +def getProfileDirectory(): + "Get the profile directory." + craftTypeName = getCraftTypeName() + return os.path.join( craftTypeName, getProfileName(craftTypeName) ) + +def getProfileName(craftTypeName): + "Get the profile name from the craft type name." + craftTypeSettings = getCraftTypePluginModule(craftTypeName).getNewRepository() + settings.getReadRepository(craftTypeSettings) + return craftTypeSettings.profileListbox.value + +def getReadProfileRepository(): + "Get the read profile repository." + return settings.getReadRepository( ProfileRepository() ) + +def updateProfileSaveListeners(): + "Call the save function of all the update profile save listeners." + for globalProfileSaveListener in euclidean.getListTableElements( settings.globalProfileSaveListenerListTable ): + globalProfileSaveListener.save() + cancelAll() + + +class AddProfile: + "A class to add a profile." + def addSelection(self): + "Add the selection of a listbox setting." + entryText = self.entry.get() + if entryText == '': + print('To add to the profiles, enter the material name.') + return + self.profileListboxSetting.listSetting.setValueToFolders() + if entryText in self.profileListboxSetting.listSetting.value: + print('There is already a profile by the name of %s, so no profile will be added.' % entryText ) + return + self.entry.delete( 0, settings.Tkinter.END ) + craftTypeProfileDirectory = archive.getProfilesPath( self.profileListboxSetting.listSetting.craftTypeName ) + destinationDirectory = os.path.join( craftTypeProfileDirectory, entryText ) + shutil.copytree( self.profileListboxSetting.getSelectedFolder(), destinationDirectory ) + self.profileListboxSetting.listSetting.setValueToFolders() + self.profileListboxSetting.value = entryText + self.profileListboxSetting.setStateToValue() + + def addSelectionWithEvent(self, event): + "Add the selection of a listbox setting, given an event." + self.addSelection() + + def addToDialog( self, gridPosition ): + "Add this to the dialog." + gridPosition.increment() + self.entry = settings.Tkinter.Entry( gridPosition.master ) + self.entry.bind('', self.addSelectionWithEvent ) + self.entry.grid( row = gridPosition.row, column = 1, columnspan = 3, sticky = settings.Tkinter.W ) + self.addButton = settings.Tkinter.Button( gridPosition.master, activebackground = 'black', activeforeground = 'white', text = 'Add Profile', command = self.addSelection ) + self.addButton.grid( row = gridPosition.row, column = 0 ) + + def getFromProfileListboxSettingRepository( self, profileListboxSetting, repository ): + "Initialize." + self.profileListboxSetting = profileListboxSetting + self.repository = repository + repository.displayEntities.append(self) + return self + + +class DeleteProfile( AddProfile ): + "A class to delete the selection of a listbox profile." + def addToDialog( self, gridPosition ): + "Add this to the dialog." + gridPosition.increment() + self.deleteButton = settings.Tkinter.Button( gridPosition.master, activebackground = 'black', activeforeground = 'white', text = "Delete Profile", command = self.deleteSelection ) + self.deleteButton.grid( row = gridPosition.row, column = 0 ) + + def deleteSelection(self): + "Delete the selection of a listbox setting." + DeleteProfileDialog( self.profileListboxSetting, settings.Tkinter.Tk() ) + + +class DeleteProfileDialog: + "A dialog to delete a profile." + def __init__(self, profileListboxSetting, root): + "Display a delete dialog." + self.profileListboxSetting = profileListboxSetting + self.root = root + root.title('Delete Warning') + rowIndex = 0 + self.label = settings.Tkinter.Label(self.root, text = 'Do you want to delete the profile?') + self.label.grid(row = rowIndex, column = 0, columnspan = 3, sticky = settings.Tkinter.W) + rowIndex += 1 + columnIndex = 1 + deleteButton = settings.Tkinter.Button(root, activebackground = 'black', activeforeground = 'red', command = self.delete, fg = 'red', text = 'Delete') + deleteButton.grid(row = rowIndex, column = columnIndex) + columnIndex += 1 + noButton = settings.Tkinter.Button(root, activebackground = 'black', activeforeground = 'darkgreen', command = self.no, fg = 'darkgreen', text = 'Do Nothing') + noButton.grid(row = rowIndex, column = columnIndex) + + def delete(self): + "Delete the selection of a listbox setting." + self.profileListboxSetting.setToDisplay() + self.profileListboxSetting.listSetting.setValueToFolders() + if self.profileListboxSetting.value not in self.profileListboxSetting.listSetting.value: + return + lastSelectionIndex = 0 + currentSelectionTuple = self.profileListboxSetting.listbox.curselection() + if len(currentSelectionTuple) > 0: + lastSelectionIndex = int(currentSelectionTuple[0]) + else: + print('No profile is selected, so no profile will be deleted.') + return + craftTypeName = self.profileListboxSetting.listSetting.craftTypeName + settings.deleteDirectory(archive.getProfilesPath(craftTypeName), self.profileListboxSetting.value) + settings.deleteDirectory(settings.getProfilesDirectoryInAboveDirectory(craftTypeName), self.profileListboxSetting.value) + self.profileListboxSetting.listSetting.setValueToFolders() + if len(self.profileListboxSetting.listSetting.value) < 1: + defaultSettingsDirectory = archive.getProfilesPath(os.path.join(craftTypeName, self.profileListboxSetting.defaultValue)) + archive.makeDirectory(defaultSettingsDirectory) + self.profileListboxSetting.listSetting.setValueToFolders() + lastSelectionIndex = min(lastSelectionIndex, len(self.profileListboxSetting.listSetting.value) - 1) + self.profileListboxSetting.value = self.profileListboxSetting.listSetting.value[lastSelectionIndex] + self.profileListboxSetting.setStateToValue() + self.no() + + def no(self): + "The dialog was closed." + self.root.destroy() + + +class ProfileList: + "A class to list the profiles." + def getFromName( self, name, repository ): + "Initialize." + self.craftTypeName = repository.lowerName + self.name = name + self.repository = repository + self.setValueToFolders() + return self + + def setValueToFolders(self): + "Set the value to the folders in the profiles directories." + self.value = settings.getFolders( archive.getProfilesPath( self.craftTypeName ) ) + defaultFolders = settings.getFolders( settings.getProfilesDirectoryInAboveDirectory( self.craftTypeName ) ) + for defaultFolder in defaultFolders: + if defaultFolder not in self.value: + self.value.append( defaultFolder ) + self.value.sort() + + +class ProfileListboxSetting( settings.StringSetting ): + "A class to handle the profile listbox." + def addToDialog( self, gridPosition ): + "Add this to the dialog." +#http://www.pythonware.com/library/tkinter/introduction/x5453-patterns.htm + self.root = gridPosition.master + gridPosition.increment() + from fabmetheus_utilities.hidden_scrollbar import HiddenScrollbar + scrollbar = HiddenScrollbar( gridPosition.master ) + self.listbox = settings.Tkinter.Listbox( gridPosition.master, selectmode = settings.Tkinter.SINGLE, yscrollcommand = scrollbar.set ) + self.listbox.bind('', self.buttonReleaseOne ) + gridPosition.master.bind('', self.focusIn ) + scrollbar.config( command = self.listbox.yview ) + self.listbox.grid( row = gridPosition.row, column = 0, sticky = settings.Tkinter.N + settings.Tkinter.S ) + scrollbar.grid( row = gridPosition.row, column = 1, sticky = settings.Tkinter.N + settings.Tkinter.S ) + self.setStateToValue() + self.repository.saveListenerTable['updateProfileSaveListeners'] = updateProfileSaveListeners + + def buttonReleaseOne(self, event): + "Button one released." + self.setValueToIndex( self.listbox.nearest(event.y) ) + + def focusIn(self, event): + "The root has gained focus." + settings.getReadRepository(self.repository) + self.setStateToValue() + + def getFromListSetting( self, listSetting, name, repository, value ): + "Initialize." + self.getFromValueOnly( name, repository, value ) + self.listSetting = listSetting + repository.displayEntities.append(self) + repository.preferences.append(self) + return self + + def getSelectedFolder(self): + "Get the selected folder." + settingProfileSubfolder = settings.getSubfolderWithBasename( self.value, archive.getProfilesPath( self.listSetting.craftTypeName ) ) + if settingProfileSubfolder != None: + return settingProfileSubfolder + toolProfileSubfolder = settings.getSubfolderWithBasename( self.value, settings.getProfilesDirectoryInAboveDirectory( self.listSetting.craftTypeName ) ) + return toolProfileSubfolder + + def setStateToValue(self): + "Set the listbox items to the list setting." + self.listbox.delete( 0, settings.Tkinter.END ) + for item in self.listSetting.value: + self.listbox.insert( settings.Tkinter.END, item ) + if self.value == item: + self.listbox.select_set( settings.Tkinter.END ) + + def setToDisplay(self): + "Set the selection value to the listbox selection." + currentSelectionTuple = self.listbox.curselection() + if len( currentSelectionTuple ) > 0: + self.setValueToIndex( int( currentSelectionTuple[0] ) ) + + def setValueToIndex( self, index ): + "Set the selection value to the index." + valueString = self.listbox.get( index ) + self.setValueToString( valueString ) + + def setValueToString( self, valueString ): + "Set the value to the value string." + self.value = valueString + if self.getSelectedFolder() == None: + self.value = self.defaultValue + if self.getSelectedFolder() == None: + if len( self.listSetting.value ) > 0: + self.value = self.listSetting.value[0] + + +class ProfilePluginRadioButtonsSaveListener: + "A class to update the profile radio buttons." + def addToDialog( self, gridPosition ): + "Add this to the dialog." + euclidean.addElementToListDictionaryIfNotThere( self, self.repository.repositoryDialog, settings.globalProfileSaveListenerListTable ) + + def getFromRadioPlugins( self, radioPlugins, repository ): + "Initialize." + self.name = 'ProfilePluginRadioButtonsSaveListener' + self.radioPlugins = radioPlugins + self.repository = repository + repository.displayEntities.append(self) + return self + + def save(self): + "Profile has been saved and profile radio plugins should be updated." + craftTypeName = getCraftTypeName() + for radioPlugin in self.radioPlugins: + if radioPlugin.name == craftTypeName: + if radioPlugin.setSelect(): + self.repository.pluginFrame.update() + return + + +class ProfileRepository: + "A class to handle the profile entities." + def __init__(self): + "Set the default entities, execute title & repository fileName." + settings.addListsToRepository('skeinforge_application.skeinforge_utilities.skeinforge_profile.html', self) + importantFileNames = ['extrusion'] + self.craftRadios = settings.getRadioPluginsAddPluginFrame( getPluginsDirectoryPath(), importantFileNames, getPluginFileNames(), self ) + ProfilePluginRadioButtonsSaveListener().getFromRadioPlugins( self.craftRadios, self ) + for craftRadio in self.craftRadios: + craftRadio.updateFunction = self.updateRelay + directoryName = archive.getProfilesPath() + archive.makeDirectory(directoryName) + self.windowPosition.value = '0+200' + + def updateRelay(self): + "Update the plugin frame then the ProfileSaveListeners." + self.pluginFrame.update() + updateProfileSaveListeners() + + +class ProfileSelectionMenuRadio: + "A class to display a profile selection menu radio button." + def addToDialog( self, gridPosition ): + "Add this to the dialog." + self.activate = False + self.menuButtonDisplay.setToNameAddToDialog( self.valueName, gridPosition ) + self.menuButtonDisplay.menu.add_radiobutton( label = self.valueName, command = self.clickRadio, value = self.valueName, variable = self.menuButtonDisplay.radioVar ) + self.menuLength = self.menuButtonDisplay.menu.index( settings.Tkinter.END ) + if self.value: + self.menuButtonDisplay.radioVar.set( self.valueName ) + self.menuButtonDisplay.menu.invoke( self.menuLength ) + euclidean.addElementToListDictionaryIfNotThere( self.repository, self.repository.repositoryDialog, settings.globalProfileSaveListenerListTable ) + self.activate = True + + def clickRadio(self): + "Workaround for Tkinter bug, invoke and set the value when clicked." + if not self.activate: + return + settings.saveAll() + self.menuButtonDisplay.radioVar.set( self.valueName ) + pluginModule = getCraftTypePluginModule() + profilePluginSettings = settings.getReadRepository( pluginModule.getNewRepository() ) + profilePluginSettings.profileListbox.value = self.name + settings.writeSettings( profilePluginSettings ) + updateProfileSaveListeners() + + def getFromMenuButtonDisplay( self, menuButtonDisplay, name, repository, value ): + "Initialize." + self.setToMenuButtonDisplay( menuButtonDisplay, name, repository, value ) + self.valueName = name.replace('_', ' ') + return self + + def setToMenuButtonDisplay( self, menuButtonDisplay, name, repository, value ): + "Initialize." + self.menuButtonDisplay = menuButtonDisplay + self.menuButtonDisplay.menuRadios.append(self) + self.name = name + self.repository = repository + self.value = value + repository.displayEntities.append(self) + + +class ProfileTypeMenuRadio( ProfileSelectionMenuRadio ): + "A class to display a profile type menu radio button." + def clickRadio(self): + "Workaround for Tkinter bug, invoke and set the value when clicked." + if not self.activate: + return + settings.saveAll() + self.menuButtonDisplay.radioVar.set( self.valueName ) + profileSettings = getReadProfileRepository() + plugins = profileSettings.craftRadios + for plugin in plugins: + plugin.value = ( plugin.name == self.name ) + settings.writeSettings( profileSettings ) + updateProfileSaveListeners() + + def getFromMenuButtonDisplay( self, menuButtonDisplay, name, repository, value ): + "Initialize." + self.setToMenuButtonDisplay( menuButtonDisplay, name, repository, value ) + self.valueName = settings.getEachWordCapitalized( name ) + return self diff --git a/SkeinPyPy/skeinforge_application/terminal.sh b/SkeinPyPy/skeinforge_application/terminal.sh new file mode 100755 index 0000000..3826bf6 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/terminal.sh @@ -0,0 +1,24 @@ +#!/bin/sh +# +# Script to open the bash terminal in this directory. +# +# Usage: set the executable property to true if it isn't already. Then double click the file. +# +echo 'Directory listing:' +echo '' +ls +echo '' +echo 'To run a python script (.py) listed above, try typing something like:' +echo 'python filename' +echo '' +echo 'For example, in the skeinforge_application directory you could run skeinforge by typing:' +echo 'python skeinforge.py' +echo '' +echo 'To skeinforge the test.stl file from the command line, in the skeinforge_application directory you could type:' +echo 'python skeinforge.py test.stl' +echo '' +echo 'To run a script in a subdirectory, append the directory path. For example, to run skeinforge from the top directory you could type:' +echo 'python skeinforge_application/skeinforge.py' +echo '' +bash + diff --git a/SkeinPyPy/skeinforge_application/test.stl b/SkeinPyPy/skeinforge_application/test.stl new file mode 100644 index 0000000..f372a18 --- /dev/null +++ b/SkeinPyPy/skeinforge_application/test.stl @@ -0,0 +1,2886 @@ +solid "Screw_Holder_Bottom"; Produced by Art of Illusion 2.4, Fri Oct 16 16:42:04 PDT 2009 +facet normal 0 -0.831469612304 0.555570233017 + outer loop + vertex 54.002309837942 24.804173569059 0.110046151702 + vertex 48.011309837942 24.804173569059 0.110046151702 + vertex 54.002309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 24.730643081307 0 + vertex 14.883210818105 25.830303993361 0 + vertex 15.262563132924 25.262563132924 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 60 23 0 + vertex 54.002309837942 24.730643081307 0 + vertex 60 40 0 + endloop +endfacet +facet normal 0 0.831469612301 0.555570233022 + outer loop + vertex 54.002309837942 15.269356918694 0 + vertex 48.011309837942 15.269356918694 0 + vertex 54.002309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.464705848856 10 0 + vertex 15.830303993361 11.883210818105 0 + vertex 16.5 11.75 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 52 30 0 + vertex 43.737436867076 27.737436867076 0 + vertex 43.169696006639 28.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 43.169696006639 15.116789181895 0 + vertex 42.5 15.25 0 + vertex 41.422110817733 17 0 + endloop +endfacet +facet normal 0 -0.831469612304 0.555570233017 + outer loop + vertex 48.011309837942 24.730643081307 0 + vertex 54.002309837942 24.730643081307 0 + vertex 48.011309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 52 30 0 + vertex 43.169696006639 28.116789181895 0 + vertex 42.5 28.25 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 60 40 0 + vertex 52 30 0 + vertex 52 40 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.883210818105 14.169696006639 0 + vertex 14.75 13.5 0 + vertex 12.002309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.883210818105 27.169696006639 0 + vertex 14.042553191489 30 0 + vertex 15.262563132924 27.737436867076 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.464705848856 10 0 + vertex 16.5 11.75 0 + vertex 17.169696006639 11.883210818105 0 + endloop +endfacet +facet normal 0 0.831469612301 0.555570233022 + outer loop + vertex 48.011309837942 15.195826430941 0.110046151702 + vertex 54.002309837942 15.195826430941 0.110046151702 + vertex 48.011309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.042553191489 30 0 + vertex 17.169696006639 28.116789181895 0 + vertex 16.5 28.25 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 15.269356918694 0 + vertex 14.75 13.5 0 + vertex 14.883210818105 12.830303993361 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 54.002309837942 17 0 + vertex 60 17 0 + vertex 54.002309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 52 10 0 + vertex 42.5 11.75 0 + vertex 43.169696006639 11.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 15.269356918694 0 + vertex 15.262563132924 14.737436867076 0 + vertex 14.883210818105 14.169696006639 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 52 10 0 + vertex 14.464705848856 10 0 + vertex 42.5 11.75 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.042553191489 30 0 + vertex 15.830303993361 28.116789181895 0 + vertex 15.262563132924 27.737436867076 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.464705848856 10 0 + vertex 15.262563132924 12.262563132924 0 + vertex 15.830303993361 11.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.042553191489 30 0 + vertex 16.5 28.25 0 + vertex 15.830303993361 28.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 54.002309837942 24.730643081307 0 + vertex 60 23 0 + vertex 54.002309837942 23 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 23 0 + vertex 16.5 24.75 0 + vertex 17.169696006639 24.883210818105 0 + endloop +endfacet +facet normal 0 0.831469612301 0.555570233022 + outer loop + vertex 6.011309837942 15.269356918694 0 + vertex 6.011309837942 15.195826430941 0.110046151702 + vertex 12.002309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.75 26.5 0 + vertex 12.002309837942 24.730643081307 0 + vertex 14.883210818105 27.169696006639 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 52 30 0 + vertex 60 40 0 + vertex 54.002309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 41.422110817733 17 0 + vertex 48.011309837942 17 0 + vertex 43.169696006639 15.116789181895 0 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 14.75 13.5 0 + vertex 14.883210818105 12.830303993361 0.77775 + vertex 14.883210818105 12.830303993361 0 + endloop +endfacet +facet normal -0 -0 -1 + outer loop + vertex 6.011309837942 17 0 + vertex 6.011309837942 15.269356918694 0 + vertex 0 17 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 41.422110817733 17 0 + vertex 41.830303993361 15.116789181895 0 + vertex 41.262563132924 14.737436867076 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.169696006639 11.883210818105 0 + vertex 17.737436867076 12.262563132924 0 + vertex 41.830303993361 11.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 6.011309837942 15.269356918694 0 + vertex 0 10 0 + vertex 0 17 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.883210818105 25.830303993361 0 + vertex 12.002309837942 24.730643081307 0 + vertex 14.75 26.5 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 48.011309837942 24.730643081307 0 + vertex 52 30 0 + vertex 54.002309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 48.011309837942 17 0 + vertex 43.737436867076 14.737436867076 0 + vertex 43.169696006639 15.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 42.5 15.25 0 + vertex 41.830303993361 15.116789181895 0 + vertex 41.422110817733 17 0 + endloop +endfacet +facet normal -0 -0 -1 + outer loop + vertex 12.002309837942 15.269356918694 0 + vertex 14.883210818105 12.830303993361 0 + vertex 6.011309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 0 23 0 + vertex 0 30 0 + vertex 6.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0.831469612301 0.555570233022 + outer loop + vertex 12.002309837942 15.269356918694 0 + vertex 6.011309837942 15.269356918694 0 + vertex 12.002309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 54.002309837942 15.269356918694 0 + vertex 60 17 0 + vertex 52 10 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 6.011309837942 23 0 + vertex 0 23 0 + vertex 6.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.116789181895 14.169696006639 0 + vertex 48.011309837942 15.269356918694 0 + vertex 44.25 13.5 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 18.116789181895 27.169696006639 0 + vertex 41.262563132924 25.262563132924 0 + vertex 18.25 26.5 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 18.116789181895 25.830303993361 0 + vertex 18.25 26.5 0 + vertex 41.830303993361 24.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.737436867076 14.737436867076 0 + vertex 41.262563132924 14.737436867076 0 + vertex 40.883210818105 14.169696006639 0 + endloop +endfacet +facet normal -0 -0 -1 + outer loop + vertex 41.262563132924 25.262563132924 0 + vertex 41.830303993361 24.883210818105 0 + vertex 18.25 26.5 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.737436867076 25.262563132924 0 + vertex 18.116789181895 25.830303993361 0 + vertex 41.830303993361 24.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.737436867076 12.262563132924 0 + vertex 18.116789181895 12.830303993361 0 + vertex 40.883210818105 12.830303993361 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.737436867076 12.262563132924 0 + vertex 41.262563132924 12.262563132924 0 + vertex 41.830303993361 11.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 41.262563132924 27.737436867076 0 + vertex 17.169696006639 28.116789181895 0 + vertex 41.830303993361 28.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 42.5 11.75 0 + vertex 14.464705848856 10 0 + vertex 17.169696006639 11.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.25 26.5 0 + vertex 44.116789181895 27.169696006639 0 + vertex 48.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 40.883210818105 12.830303993361 0 + vertex 41.262563132924 12.262563132924 0 + vertex 17.737436867076 12.262563132924 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.169696006639 24.883210818105 0 + vertex 17.737436867076 25.262563132924 0 + vertex 41.830303993361 24.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.116789181895 12.830303993361 0 + vertex 44.25 13.5 0 + vertex 48.011309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 18.116789181895 12.830303993361 0 + vertex 40.75 13.5 0 + vertex 40.883210818105 12.830303993361 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 18.25 13.5 0 + vertex 18.116789181895 14.169696006639 0 + vertex 40.883210818105 14.169696006639 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 41.262563132924 14.737436867076 0 + vertex 17.737436867076 14.737436867076 0 + vertex 17.169696006639 15.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 18.116789181895 14.169696006639 0 + vertex 17.737436867076 14.737436867076 0 + vertex 40.883210818105 14.169696006639 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.116789181895 25.830303993361 0 + vertex 44.25 26.5 0 + vertex 48.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 14.883210818105 14.169696006639 0.77775 + vertex 14.75 13.5 0.77775 + vertex 14.883210818105 14.169696006639 0 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 14.75 13.5 0.77775 + vertex 14.75 13.5 0 + vertex 14.883210818105 14.169696006639 0 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 41.830303993361 28.116789181895 0 + vertex 42.5 28.25 0.77775 + vertex 41.830303993361 28.116789181895 0.77775 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 15.830303993361 24.883210818105 0.77775 + vertex 15.830303993361 24.883210818105 0 + vertex 15.262563132924 25.262563132924 0 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 0 + outer loop + vertex 16.5 28.25 0 + vertex 17.169696006639 28.116789181895 0.77775 + vertex 16.5 28.25 0.77775 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 43.169696006639 24.883210818105 0 + vertex 42.5 24.75 0.77775 + vertex 43.169696006639 24.883210818105 0.77775 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 16.5 28.25 0.77775 + vertex 15.830303993361 28.116789181895 0.77775 + vertex 16.5 28.25 0 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 15.830303993361 28.116789181895 0.77775 + vertex 15.830303993361 28.116789181895 0 + vertex 16.5 28.25 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 0 23 0 + vertex 6.011309837942 23 0.77775 + vertex 0 23 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 60 0 0.77775 + vertex 52 10 0.77775 + vertex 52 0 0.77775 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 40.883210818105 27.169696006639 0 + vertex 40.883210818105 27.169696006639 0.77775 + vertex 40.75 26.5 0 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 17.169696006639 24.883210818105 0.77775 + vertex 17.169696006639 24.883210818105 0 + vertex 16.5 24.75 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 52 0 0 + vertex 52 0 0.77775 + vertex 52 10 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 34.416922535211 17 0.77775 + vertex 40.883210818105 12.830303993361 0.77775 + vertex 40.75 13.5 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 12.002309837942 23 0.77775 + vertex 12.002309837942 24.936988122232 0.77775 + vertex 12.002309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 42.5 24.75 0 + vertex 41.830303993361 24.883210818105 0.77775 + vertex 42.5 24.75 0.77775 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 60 0 0 + vertex 60 0 0.77775 + vertex 52 0 0 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 15.830303993361 28.116789181895 0.77775 + vertex 15.262563132924 27.737436867076 0.77775 + vertex 15.830303993361 28.116789181895 0 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 15.262563132924 27.737436867076 0.77775 + vertex 15.262563132924 27.737436867076 0 + vertex 15.830303993361 28.116789181895 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 6.011309837942 17 0.77775 + vertex 6.011309837942 17 0 + vertex 0 17 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 48.011309837942 17 0 + vertex 48.011309837942 17 0.77775 + vertex 48.011309837942 15.063011877768 0.77775 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 0 + outer loop + vertex 18.25 26.5 0 + vertex 18.25 26.5 0.77775 + vertex 18.116789181895 27.169696006639 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 15.262563132924 14.737436867076 0.77775 + vertex 15.830303993361 15.116789181895 0.77775 + vertex 15.455907969142 17 0.77775 + endloop +endfacet +facet normal 0 0.980785280403 0.195090322015 + outer loop + vertex 54.002309837942 15.063011877768 0.77775 + vertex 54.002309837942 15.195826430941 0.110046151702 + vertex 48.011309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 0 + outer loop + vertex 44.25 26.5 0 + vertex 44.25 26.5 0.77775 + vertex 44.116789181895 27.169696006639 0.77775 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 52 10 0 + vertex 15.306548743796 10 0.77775 + vertex 14.464705848856 10 0 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 40.75 26.5 0.77775 + vertex 40.883210818105 25.830303993361 0 + vertex 40.75 26.5 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 48.011309837942 15.063011877768 0.77775 + vertex 48.011309837942 15.195826430941 0.110046151702 + vertex 48.011309837942 15.269356918694 0 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 -0 + outer loop + vertex 44.116789181895 27.169696006639 0.77775 + vertex 43.737436867076 27.737436867076 0.77775 + vertex 44.116789181895 27.169696006639 0 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 -0 + outer loop + vertex 43.737436867076 27.737436867076 0.77775 + vertex 43.737436867076 27.737436867076 0 + vertex 44.116789181895 27.169696006639 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 52 10 0 + vertex 52 10 0.77775 + vertex 15.306548743796 10 0.77775 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 6.011309837942 23 0.77775 + vertex 6.011309837942 23 0 + vertex 6.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 34.416922535211 17 0.77775 + vertex 40.883210818105 14.169696006639 0.77775 + vertex 41.262563132924 14.737436867076 0.77775 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 14.75 26.5 0 + vertex 14.883210818105 25.830303993361 0.77775 + vertex 14.883210818105 25.830303993361 0 + endloop +endfacet +facet normal 0 -0.980785280403 0.195090322015 + outer loop + vertex 6.011309837942 24.936988122232 0.77775 + vertex 6.011309837942 24.804173569059 0.110046151702 + vertex 12.002309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 -0 + outer loop + vertex 18.116789181895 14.169696006639 0.77775 + vertex 17.737436867076 14.737436867076 0.77775 + vertex 18.116789181895 14.169696006639 0 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 -0 + outer loop + vertex 17.737436867076 14.737436867076 0.77775 + vertex 17.737436867076 14.737436867076 0 + vertex 18.116789181895 14.169696006639 0 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 42.5 15.25 0.77775 + vertex 41.830303993361 15.116789181895 0.77775 + vertex 42.5 15.25 0 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 41.830303993361 15.116789181895 0.77775 + vertex 41.830303993361 15.116789181895 0 + vertex 42.5 15.25 0 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 15.262563132924 14.737436867076 0.77775 + vertex 14.883210818105 14.169696006639 0.77775 + vertex 15.262563132924 14.737436867076 0 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 14.883210818105 14.169696006639 0.77775 + vertex 14.883210818105 14.169696006639 0 + vertex 15.262563132924 14.737436867076 0 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 0 + outer loop + vertex 43.169696006639 15.116789181895 0 + vertex 43.737436867076 14.737436867076 0.77775 + vertex 43.169696006639 15.116789181895 0.77775 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 14.75 13.5 0.77775 + vertex 14.883210818105 12.830303993361 0.77775 + vertex 14.75 13.5 0 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 41.262563132924 14.737436867076 0.77775 + vertex 40.883210818105 14.169696006639 0.77775 + vertex 40.883210818105 14.169696006639 0 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 42.5 11.75 0.77775 + vertex 43.169696006639 11.883210818105 0.77775 + vertex 43.169696006639 11.883210818105 0 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 -0 + outer loop + vertex 17.169696006639 15.116789181895 0.77775 + vertex 17.169696006639 15.116789181895 0 + vertex 17.737436867076 14.737436867076 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 12.002309837942 23 0.77775 + vertex 12.002309837942 24.730643081307 0 + vertex 12.002309837942 23 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 6.011309837942 23 0.77775 + vertex 0 23 0 + vertex 6.011309837942 23 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.116789181895 25.830303993361 0 + vertex 48.011309837942 23 0 + vertex 43.737436867076 25.262563132924 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 12.002309837942 15.195826430941 0.110046151702 + vertex 12.002309837942 17 0.77775 + vertex 12.002309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 0 10 0 + vertex 6.011309837942 15.269356918694 0 + vertex 14.883210818105 12.830303993361 0 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 -0 + outer loop + vertex 17.169696006639 28.116789181895 0.77775 + vertex 16.5 28.25 0 + vertex 17.169696006639 28.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 48.011309837942 24.730643081307 0 + vertex 44.116789181895 27.169696006639 0 + vertex 43.737436867076 27.737436867076 0 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 44.25 26.5 0.77775 + vertex 44.25 26.5 0 + vertex 44.116789181895 25.830303993361 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 40.75 26.5 0.77775 + vertex 37.474084379442 23 0.77775 + vertex 40.883210818105 25.830303993361 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 34.416922535211 17 0.77775 + vertex 40.75 13.5 0.77775 + vertex 40.883210818105 14.169696006639 0.77775 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 17.169696006639 11.883210818105 0.77775 + vertex 17.169696006639 11.883210818105 0 + vertex 16.5 11.75 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 43.737436867076 27.737436867076 0.77775 + vertex 52 30 0.77775 + vertex 41.866228156844 30 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 12.002309837942 15.063011877768 0.77775 + vertex 14.883210818105 12.830303993361 0.77775 + vertex 14.75 13.5 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 15.063011877768 0.77775 + vertex 48.011309837942 15.063011877768 0.77775 + vertex 52 10 0.77775 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 41.830303993361 24.883210818105 0.77775 + vertex 42.5 24.75 0 + vertex 41.830303993361 24.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 6.011309837942 24.730643081307 0 + vertex 14.883210818105 27.169696006639 0 + vertex 12.002309837942 24.730643081307 0 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 -0 + outer loop + vertex 43.737436867076 14.737436867076 0.77775 + vertex 43.169696006639 15.116789181895 0 + vertex 43.737436867076 14.737436867076 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 43.737436867076 27.737436867076 0.77775 + vertex 41.866228156844 30 0.77775 + vertex 43.169696006639 28.116789181895 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 34.416922535211 17 0.77775 + vertex 41.830303993361 15.116789181895 0.77775 + vertex 42.5 15.25 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 16.5 11.75 0.77775 + vertex 15.830303993361 11.883210818105 0.77775 + vertex 15.306548743796 10 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 0 23 0.77775 + vertex 0 30 0 + vertex 0 23 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 0 30 0 + vertex 0 23 0.77775 + vertex 0 30 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 52 10 0 + vertex 52 0 0.77775 + vertex 52 10 0.77775 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 17.169696006639 11.883210818105 0 + vertex 17.169696006639 11.883210818105 0.77775 + vertex 17.737436867076 12.262563132924 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 43.169696006639 28.116789181895 0.77775 + vertex 41.866228156844 30 0.77775 + vertex 42.5 28.25 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 14.042553191489 30 0 + vertex 41.866228156844 30 0.77775 + vertex 52 30 0 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 41.830303993361 11.883210818105 0.77775 + vertex 42.5 11.75 0 + vertex 41.830303993361 11.883210818105 0 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 15.262563132924 12.262563132924 0.77775 + vertex 15.830303993361 11.883210818105 0 + vertex 15.262563132924 12.262563132924 0 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 40.883210818105 25.830303993361 0 + vertex 40.75 26.5 0.77775 + vertex 40.883210818105 25.830303993361 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 18.116789181895 27.169696006639 0 + vertex 40.883210818105 25.830303993361 0 + vertex 41.262563132924 25.262563132924 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 42.5 28.25 0 + vertex 17.169696006639 28.116789181895 0 + vertex 14.042553191489 30 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.607403236621 30 0.77775 + vertex 15.262563132924 27.737436867076 0.77775 + vertex 15.830303993361 28.116789181895 0.77775 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 14.883210818105 25.830303993361 0.77775 + vertex 15.262563132924 25.262563132924 0.77775 + vertex 14.883210818105 25.830303993361 0 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 15.262563132924 25.262563132924 0.77775 + vertex 15.262563132924 25.262563132924 0 + vertex 14.883210818105 25.830303993361 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 48.011309837942 24.804173569059 0.110046151702 + vertex 48.011309837942 23 0.77775 + vertex 48.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 48.011309837942 24.730643081307 0 + vertex 43.737436867076 27.737436867076 0 + vertex 52 30 0 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 18.116789181895 25.830303993361 0 + vertex 18.116789181895 25.830303993361 0.77775 + vertex 18.25 26.5 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 54.002309837942 15.195826430941 0.110046151702 + vertex 54.002309837942 15.063011877768 0.77775 + vertex 54.002309837942 17 0.77775 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 41.830303993361 15.116789181895 0 + vertex 41.830303993361 15.116789181895 0.77775 + vertex 41.262563132924 14.737436867076 0.77775 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 -0 + outer loop + vertex 43.737436867076 27.737436867076 0.77775 + vertex 43.169696006639 28.116789181895 0.77775 + vertex 43.737436867076 27.737436867076 0 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 -0 + outer loop + vertex 43.169696006639 28.116789181895 0.77775 + vertex 43.169696006639 28.116789181895 0 + vertex 43.737436867076 27.737436867076 0 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 41.830303993361 11.883210818105 0.77775 + vertex 42.5 11.75 0.77775 + vertex 42.5 11.75 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 12.002309837942 23 0.77775 + vertex 15.830303993361 24.883210818105 0.77775 + vertex 15.262563132924 25.262563132924 0.77775 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 41.262563132924 14.737436867076 0.77775 + vertex 41.262563132924 14.737436867076 0 + vertex 41.830303993361 15.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 60 17 0 + vertex 60 0 0 + vertex 52 0 0 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 15.262563132924 25.262563132924 0 + vertex 15.262563132924 25.262563132924 0.77775 + vertex 15.830303993361 24.883210818105 0.77775 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 12.002309837942 23 0.77775 + vertex 12.002309837942 23 0 + vertex 17.379510917074 23 0.77775 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 6.011309837942 24.936988122232 0.77775 + vertex 6.011309837942 23 0.77775 + vertex 6.011309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.169696006639 11.883210818105 0 + vertex 41.830303993361 11.883210818105 0 + vertex 42.5 11.75 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 42.5 28.25 0 + vertex 14.042553191489 30 0 + vertex 52 30 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 17 0.77775 + vertex 43.169696006639 15.116789181895 0.77775 + vertex 43.737436867076 14.737436867076 0.77775 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 6.011309837942 15.269356918694 0 + vertex 6.011309837942 17 0.77775 + vertex 6.011309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 -0 + outer loop + vertex 18.116789181895 27.169696006639 0.77775 + vertex 18.116789181895 27.169696006639 0 + vertex 18.25 26.5 0 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 40.75 26.5 0 + vertex 40.883210818105 27.169696006639 0.77775 + vertex 40.75 26.5 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.607403236621 30 0.77775 + vertex 0 30 0.77775 + vertex 14.883210818105 27.169696006639 0.77775 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 42.5 11.75 0.77775 + vertex 43.169696006639 11.883210818105 0 + vertex 42.5 11.75 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.607403236621 30 0.77775 + vertex 15.830303993361 28.116789181895 0.77775 + vertex 16.5 28.25 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 52 30 0.77775 + vertex 52 30 0 + vertex 41.866228156844 30 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 60 17 0 + vertex 52 0 0 + vertex 52 10 0 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 0 + outer loop + vertex 44.116789181895 14.169696006639 0.77775 + vertex 43.737436867076 14.737436867076 0.77775 + vertex 43.737436867076 14.737436867076 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 14.607403236621 30 0.77775 + vertex 41.866228156844 30 0.77775 + vertex 14.042553191489 30 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.169696006639 11.883210818105 0.77775 + vertex 15.306548743796 10 0.77775 + vertex 17.737436867076 12.262563132924 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 12.002309837942 15.269356918694 0 + vertex 12.002309837942 17 0.77775 + vertex 12.002309837942 17 0 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 17.169696006639 24.883210818105 0.77775 + vertex 16.5 24.75 0 + vertex 16.5 24.75 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 23 0.77775 + vertex 43.169696006639 24.883210818105 0.77775 + vertex 42.5 24.75 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 6.011309837942 17 0.77775 + vertex 0 17 0 + vertex 0 17 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.607403236621 30 0.77775 + vertex 16.5 28.25 0.77775 + vertex 17.169696006639 28.116789181895 0.77775 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 18.116789181895 25.830303993361 0.77775 + vertex 17.737436867076 25.262563132924 0 + vertex 17.737436867076 25.262563132924 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 16.5 24.75 0.77775 + vertex 17.379510917074 23 0.77775 + vertex 17.169696006639 24.883210818105 0.77775 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 -0 + outer loop + vertex 44.116789181895 27.169696006639 0.77775 + vertex 44.116789181895 27.169696006639 0 + vertex 44.25 26.5 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 40.75 13.5 0 + vertex 18.25 13.5 0 + vertex 40.883210818105 14.169696006639 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.116789181895 25.830303993361 0 + vertex 48.011309837942 24.730643081307 0 + vertex 48.011309837942 23 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 0 10 0.77775 + vertex 0 10 0 + vertex 14.464705848856 10 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 15.306548743796 10 0.77775 + vertex 15.830303993361 11.883210818105 0.77775 + vertex 15.262563132924 12.262563132924 0.77775 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 0 + outer loop + vertex 43.169696006639 28.116789181895 0 + vertex 43.169696006639 28.116789181895 0.77775 + vertex 42.5 28.25 0.77775 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 17.169696006639 11.883210818105 0.77775 + vertex 16.5 11.75 0 + vertex 16.5 11.75 0.77775 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 0 + outer loop + vertex 18.25 13.5 0.77775 + vertex 18.116789181895 14.169696006639 0.77775 + vertex 18.116789181895 14.169696006639 0 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 17.169696006639 24.883210818105 0.77775 + vertex 17.737436867076 25.262563132924 0 + vertex 17.169696006639 24.883210818105 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 0 10 0.77775 + vertex 14.464705848856 10 0 + vertex 15.306548743796 10 0.77775 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 0 + outer loop + vertex 17.169696006639 15.116789181895 0.77775 + vertex 17.737436867076 14.737436867076 0 + vertex 17.737436867076 14.737436867076 0.77775 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 48.011309837942 23 0.77775 + vertex 48.011309837942 24.804173569059 0.110046151702 + vertex 48.011309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.830303993361 28.116789181895 0.77775 + vertex 41.866228156844 30 0.77775 + vertex 41.262563132924 27.737436867076 0.77775 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 16.5 11.75 0.77775 + vertex 15.830303993361 11.883210818105 0 + vertex 15.830303993361 11.883210818105 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.75 26.5 0.77775 + vertex 12.002309837942 24.936988122232 0.77775 + vertex 14.883210818105 25.830303993361 0.77775 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 -0 + outer loop + vertex 44.116789181895 14.169696006639 0.77775 + vertex 43.737436867076 14.737436867076 0 + vertex 44.116789181895 14.169696006639 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 12.002309837942 23 0.77775 + vertex 12.002309837942 24.804173569059 0.110046151702 + vertex 12.002309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 15.063011877768 0.77775 + vertex 60 17 0.77775 + vertex 54.002309837942 17 0.77775 + endloop +endfacet +facet normal 0 0.980785280403 0.195090322015 + outer loop + vertex 6.011309837942 15.063011877768 0.77775 + vertex 12.002309837942 15.195826430941 0.110046151702 + vertex 6.011309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 12.002309837942 23 0.77775 + vertex 17.379510917074 23 0.77775 + vertex 15.830303993361 24.883210818105 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 15.830303993361 24.883210818105 0.77775 + vertex 17.379510917074 23 0.77775 + vertex 16.5 24.75 0.77775 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 18.25 26.5 0.77775 + vertex 18.25 26.5 0 + vertex 18.116789181895 25.830303993361 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.262563132924 25.262563132924 0.77775 + vertex 37.474084379442 23 0.77775 + vertex 41.830303993361 24.883210818105 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.607403236621 30 0.77775 + vertex 14.883210818105 27.169696006639 0.77775 + vertex 15.262563132924 27.737436867076 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.737436867076 14.737436867076 0.77775 + vertex 15.455907969142 17 0.77775 + vertex 17.169696006639 15.116789181895 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.737436867076 14.737436867076 0.77775 + vertex 34.416922535211 17 0.77775 + vertex 15.455907969142 17 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 15.306548743796 10 0.77775 + vertex 15.262563132924 12.262563132924 0.77775 + vertex 14.883210818105 12.830303993361 0.77775 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 17.169696006639 24.883210818105 0.77775 + vertex 17.737436867076 25.262563132924 0.77775 + vertex 17.737436867076 25.262563132924 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 16.5 15.25 0.77775 + vertex 15.455907969142 17 0.77775 + vertex 15.830303993361 15.116789181895 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.830303993361 28.116789181895 0.77775 + vertex 42.5 28.25 0.77775 + vertex 41.866228156844 30 0.77775 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 18.116789181895 12.830303993361 0.77775 + vertex 18.25 13.5 0.77775 + vertex 18.25 13.5 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 60 17 0.77775 + vertex 60 17 0 + vertex 54.002309837942 17 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 15.455907969142 17 0.77775 + vertex 12.002309837942 17 0 + vertex 12.002309837942 17 0.77775 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 43.169696006639 11.883210818105 0 + vertex 43.737436867076 12.262563132924 0.77775 + vertex 43.737436867076 12.262563132924 0 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 43.169696006639 11.883210818105 0 + vertex 43.169696006639 11.883210818105 0.77775 + vertex 43.737436867076 12.262563132924 0.77775 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 -0 + outer loop + vertex 18.25 13.5 0.77775 + vertex 18.116789181895 14.169696006639 0 + vertex 18.25 13.5 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 42.5 28.25 0 + vertex 41.830303993361 28.116789181895 0 + vertex 17.169696006639 28.116789181895 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 48.011309837942 23 0.77775 + vertex 48.011309837942 23 0 + vertex 48.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.169696006639 15.116789181895 0.77775 + vertex 15.455907969142 17 0.77775 + vertex 16.5 15.25 0.77775 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 17.737436867076 12.262563132924 0.77775 + vertex 17.737436867076 12.262563132924 0 + vertex 17.169696006639 11.883210818105 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 52 40 0.77775 + vertex 60 23 0.77775 + vertex 60 40 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 40.75 13.5 0 + vertex 18.116789181895 12.830303993361 0 + vertex 18.25 13.5 0 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 0 + outer loop + vertex 43.169696006639 28.116789181895 0 + vertex 42.5 28.25 0.77775 + vertex 42.5 28.25 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 48.011309837942 23 0 + vertex 12.002309837942 23 0 + vertex 42.5 24.75 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 23 0.77775 + vertex 60 23 0.77775 + vertex 54.002309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 6.011309837942 23 0.77775 + vertex 6.011309837942 24.730643081307 0 + vertex 6.011309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 0 + outer loop + vertex 17.169696006639 15.116789181895 0 + vertex 16.5 15.25 0.77775 + vertex 16.5 15.25 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 6.011309837942 17 0.77775 + vertex 6.011309837942 15.063011877768 0.77775 + vertex 6.011309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 60 17 0.77775 + vertex 60 0 0 + vertex 60 17 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 60 0 0.77775 + vertex 60 0 0 + vertex 60 17 0.77775 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 15.262563132924 12.262563132924 0.77775 + vertex 15.830303993361 11.883210818105 0.77775 + vertex 15.830303993361 11.883210818105 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 52 10 0.77775 + vertex 44.116789181895 12.830303993361 0.77775 + vertex 43.737436867076 12.262563132924 0.77775 + endloop +endfacet +facet normal 0 0.980785280403 0.195090322015 + outer loop + vertex 6.011309837942 15.063011877768 0.77775 + vertex 12.002309837942 15.063011877768 0.77775 + vertex 12.002309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.169696006639 24.883210818105 0.77775 + vertex 17.379510917074 23 0.77775 + vertex 17.737436867076 25.262563132924 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 52 10 0.77775 + vertex 43.737436867076 12.262563132924 0.77775 + vertex 43.169696006639 11.883210818105 0.77775 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 42.5 28.25 0.77775 + vertex 41.830303993361 28.116789181895 0 + vertex 42.5 28.25 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 6.011309837942 15.269356918694 0 + vertex 6.011309837942 17 0 + vertex 6.011309837942 17 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 40.883210818105 25.830303993361 0.77775 + vertex 37.474084379442 23 0.77775 + vertex 41.262563132924 25.262563132924 0.77775 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 18.116789181895 12.830303993361 0.77775 + vertex 18.25 13.5 0 + vertex 18.116789181895 12.830303993361 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.737436867076 25.262563132924 0.77775 + vertex 17.379510917074 23 0.77775 + vertex 18.116789181895 25.830303993361 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 34.416922535211 17 0.77775 + vertex 41.262563132924 14.737436867076 0.77775 + vertex 41.830303993361 15.116789181895 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 0 30 0.77775 + vertex 6.011309837942 24.936988122232 0.77775 + vertex 12.002309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.116789181895 25.830303993361 0.77775 + vertex 17.379510917074 23 0.77775 + vertex 37.474084379442 23 0.77775 + endloop +endfacet +facet normal 0 -0.980785280403 0.195090322015 + outer loop + vertex 6.011309837942 24.936988122232 0.77775 + vertex 12.002309837942 24.804173569059 0.110046151702 + vertex 12.002309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 52 10 0.77775 + vertex 43.169696006639 11.883210818105 0.77775 + vertex 42.5 11.75 0.77775 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 41.262563132924 14.737436867076 0.77775 + vertex 40.883210818105 14.169696006639 0 + vertex 41.262563132924 14.737436867076 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 48.011309837942 23 0 + vertex 43.169696006639 24.883210818105 0 + vertex 43.737436867076 25.262563132924 0 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 44.25 26.5 0.77775 + vertex 44.116789181895 25.830303993361 0 + vertex 44.116789181895 25.830303993361 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 60 17 0.77775 + vertex 54.002309837942 17 0 + vertex 54.002309837942 17 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 48.011309837942 23 0 + vertex 42.5 24.75 0 + vertex 43.169696006639 24.883210818105 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 43.737436867076 27.737436867076 0.77775 + vertex 44.116789181895 27.169696006639 0.77775 + vertex 52 30 0.77775 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 18.116789181895 25.830303993361 0.77775 + vertex 18.116789181895 25.830303993361 0 + vertex 17.737436867076 25.262563132924 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 54.002309837942 15.195826430941 0.110046151702 + vertex 54.002309837942 17 0.77775 + vertex 54.002309837942 15.269356918694 0 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 14.883210818105 27.169696006639 0.77775 + vertex 14.75 26.5 0 + vertex 14.883210818105 27.169696006639 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 52 0 0.77775 + vertex 52 0 0 + vertex 60 0 0.77775 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 14.883210818105 27.169696006639 0.77775 + vertex 14.75 26.5 0.77775 + vertex 14.75 26.5 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 54.002309837942 15.269356918694 0 + vertex 54.002309837942 17 0.77775 + vertex 54.002309837942 17 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 16.5 11.75 0.77775 + vertex 15.306548743796 10 0.77775 + vertex 17.169696006639 11.883210818105 0.77775 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 0 + outer loop + vertex 17.169696006639 15.116789181895 0 + vertex 17.169696006639 15.116789181895 0.77775 + vertex 16.5 15.25 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 12.002309837942 15.195826430941 0.110046151702 + vertex 12.002309837942 15.063011877768 0.77775 + vertex 12.002309837942 17 0.77775 + endloop +endfacet +facet normal 0 0.980785280403 0.195090322015 + outer loop + vertex 54.002309837942 15.063011877768 0.77775 + vertex 48.011309837942 15.195826430941 0.110046151702 + vertex 48.011309837942 15.063011877768 0.77775 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 16.5 11.75 0.77775 + vertex 16.5 11.75 0 + vertex 15.830303993361 11.883210818105 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 48.011309837942 15.063011877768 0.77775 + vertex 48.011309837942 15.269356918694 0 + vertex 48.011309837942 17 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 24.936988122232 0.77775 + vertex 52 30 0.77775 + vertex 48.011309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 42.5 24.75 0.77775 + vertex 43.169696006639 24.883210818105 0 + vertex 42.5 24.75 0 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 14.75 26.5 0 + vertex 14.75 26.5 0.77775 + vertex 14.883210818105 25.830303993361 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.116789181895 14.169696006639 0 + vertex 43.737436867076 14.737436867076 0 + vertex 48.011309837942 17 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.116789181895 14.169696006639 0 + vertex 48.011309837942 17 0 + vertex 48.011309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.169696006639 24.883210818105 0 + vertex 41.830303993361 24.883210818105 0 + vertex 42.5 24.75 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.169696006639 24.883210818105 0 + vertex 42.5 24.75 0 + vertex 12.002309837942 23 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 52 40 0 + vertex 52 40 0.77775 + vertex 60 40 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 52 40 0 + vertex 60 40 0.77775 + vertex 60 40 0 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 0 + outer loop + vertex 18.116789181895 27.169696006639 0.77775 + vertex 17.737436867076 27.737436867076 0.77775 + vertex 17.737436867076 27.737436867076 0 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 -0 + outer loop + vertex 18.116789181895 27.169696006639 0.77775 + vertex 17.737436867076 27.737436867076 0 + vertex 18.116789181895 27.169696006639 0 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 15.830303993361 24.883210818105 0 + vertex 15.830303993361 24.883210818105 0.77775 + vertex 16.5 24.75 0.77775 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 15.830303993361 24.883210818105 0 + vertex 16.5 24.75 0.77775 + vertex 16.5 24.75 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 12.002309837942 24.936988122232 0.77775 + vertex 12.002309837942 23 0.77775 + vertex 15.262563132924 25.262563132924 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 12.002309837942 24.936988122232 0.77775 + vertex 15.262563132924 25.262563132924 0.77775 + vertex 14.883210818105 25.830303993361 0.77775 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 60 40 0.77775 + vertex 60 23 0.77775 + vertex 60 23 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 60 40 0.77775 + vertex 60 23 0 + vertex 60 40 0 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 15.262563132924 27.737436867076 0 + vertex 15.262563132924 27.737436867076 0.77775 + vertex 14.883210818105 27.169696006639 0.77775 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 15.262563132924 27.737436867076 0 + vertex 14.883210818105 27.169696006639 0.77775 + vertex 14.883210818105 27.169696006639 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 37.474084379442 23 0.77775 + vertex 48.011309837942 23 0.77775 + vertex 42.5 24.75 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 37.474084379442 23 0.77775 + vertex 42.5 24.75 0.77775 + vertex 41.830303993361 24.883210818105 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 41.262563132924 14.737436867076 0 + vertex 12.002309837942 17 0 + vertex 41.422110817733 17 0 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 0 + outer loop + vertex 43.169696006639 15.116789181895 0.77775 + vertex 42.5 15.25 0.77775 + vertex 42.5 15.25 0 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 -0 + outer loop + vertex 43.169696006639 15.116789181895 0.77775 + vertex 42.5 15.25 0 + vertex 43.169696006639 15.116789181895 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 15.063011877768 0.77775 + vertex 52 10 0.77775 + vertex 60 0 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 15.063011877768 0.77775 + vertex 60 0 0.77775 + vertex 60 17 0.77775 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 40.75 13.5 0.77775 + vertex 40.883210818105 12.830303993361 0.77775 + vertex 40.883210818105 12.830303993361 0 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 40.75 13.5 0.77775 + vertex 40.883210818105 12.830303993361 0 + vertex 40.75 13.5 0 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 0 + outer loop + vertex 17.737436867076 27.737436867076 0 + vertex 17.737436867076 27.737436867076 0.77775 + vertex 17.169696006639 28.116789181895 0.77775 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 0 + outer loop + vertex 17.737436867076 27.737436867076 0 + vertex 17.169696006639 28.116789181895 0.77775 + vertex 17.169696006639 28.116789181895 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.883210818105 27.169696006639 0.77775 + vertex 0 30 0.77775 + vertex 12.002309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.883210818105 27.169696006639 0.77775 + vertex 12.002309837942 24.936988122232 0.77775 + vertex 14.75 26.5 0.77775 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 43.737436867076 12.262563132924 0.77775 + vertex 44.116789181895 12.830303993361 0.77775 + vertex 44.116789181895 12.830303993361 0 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 43.737436867076 12.262563132924 0.77775 + vertex 44.116789181895 12.830303993361 0 + vertex 43.737436867076 12.262563132924 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 48.011309837942 23 0 + vertex 48.011309837942 23 0.77775 + vertex 37.474084379442 23 0.77775 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 16.5 15.25 0.77775 + vertex 15.830303993361 15.116789181895 0.77775 + vertex 15.830303993361 15.116789181895 0 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 16.5 15.25 0.77775 + vertex 15.830303993361 15.116789181895 0 + vertex 16.5 15.25 0 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 40.883210818105 12.830303993361 0.77775 + vertex 41.262563132924 12.262563132924 0.77775 + vertex 41.262563132924 12.262563132924 0 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 40.883210818105 12.830303993361 0.77775 + vertex 41.262563132924 12.262563132924 0 + vertex 40.883210818105 12.830303993361 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 17 0 + vertex 16.5 15.25 0 + vertex 15.830303993361 15.116789181895 0 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 43.737436867076 25.262563132924 0 + vertex 43.737436867076 25.262563132924 0.77775 + vertex 44.116789181895 25.830303993361 0.77775 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 43.737436867076 25.262563132924 0 + vertex 44.116789181895 25.830303993361 0.77775 + vertex 44.116789181895 25.830303993361 0 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 40.883210818105 14.169696006639 0.77775 + vertex 40.75 13.5 0.77775 + vertex 40.75 13.5 0 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 40.883210818105 14.169696006639 0.77775 + vertex 40.75 13.5 0 + vertex 40.883210818105 14.169696006639 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 60 23 0 + vertex 60 23 0.77775 + vertex 54.002309837942 23 0.77775 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 60 23 0 + vertex 54.002309837942 23 0.77775 + vertex 54.002309837942 23 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 0 30 0 + vertex 14.042553191489 30 0 + vertex 14.883210818105 27.169696006639 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 0 30 0 + vertex 14.883210818105 27.169696006639 0 + vertex 6.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 41.262563132924 25.262563132924 0 + vertex 41.262563132924 25.262563132924 0.77775 + vertex 41.830303993361 24.883210818105 0.77775 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 41.262563132924 25.262563132924 0 + vertex 41.830303993361 24.883210818105 0.77775 + vertex 41.830303993361 24.883210818105 0 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 41.830303993361 28.116789181895 0.77775 + vertex 41.262563132924 27.737436867076 0.77775 + vertex 41.262563132924 27.737436867076 0 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 41.830303993361 28.116789181895 0.77775 + vertex 41.262563132924 27.737436867076 0 + vertex 41.830303993361 28.116789181895 0 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 43.169696006639 24.883210818105 0.77775 + vertex 43.737436867076 25.262563132924 0.77775 + vertex 43.737436867076 25.262563132924 0 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 43.169696006639 24.883210818105 0.77775 + vertex 43.737436867076 25.262563132924 0 + vertex 43.169696006639 24.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 52 10 0 + vertex 43.169696006639 11.883210818105 0 + vertex 43.737436867076 12.262563132924 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 52 10 0 + vertex 43.737436867076 12.262563132924 0 + vertex 54.002309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 24.936988122232 0.77775 + vertex 60 23 0.77775 + vertex 52 40 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 24.936988122232 0.77775 + vertex 52 40 0.77775 + vertex 52 30 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 15.455907969142 17 0.77775 + vertex 34.416922535211 17 0.77775 + vertex 41.422110817733 17 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 15.455907969142 17 0.77775 + vertex 41.422110817733 17 0 + vertex 12.002309837942 17 0 + endloop +endfacet +facet normal 0 -0.831469612304 0.555570233017 + outer loop + vertex 12.002309837942 24.804173569059 0.110046151702 + vertex 6.011309837942 24.804173569059 0.110046151702 + vertex 6.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 -0.831469612304 0.555570233017 + outer loop + vertex 12.002309837942 24.804173569059 0.110046151702 + vertex 6.011309837942 24.730643081307 0 + vertex 12.002309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 15.262563132924 14.737436867076 0.77775 + vertex 15.455907969142 17 0.77775 + vertex 12.002309837942 17 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 15.262563132924 14.737436867076 0.77775 + vertex 12.002309837942 17 0.77775 + vertex 14.883210818105 14.169696006639 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 44.116789181895 25.830303993361 0.77775 + vertex 43.737436867076 25.262563132924 0.77775 + vertex 48.011309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 0 17 0.77775 + vertex 0 10 0.77775 + vertex 6.011309837942 15.063011877768 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 0 17 0.77775 + vertex 6.011309837942 15.063011877768 0.77775 + vertex 6.011309837942 17 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 17 0.77775 + vertex 34.416922535211 17 0.77775 + vertex 42.5 15.25 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 17 0.77775 + vertex 42.5 15.25 0.77775 + vertex 43.169696006639 15.116789181895 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.883210818105 14.169696006639 0.77775 + vertex 12.002309837942 17 0.77775 + vertex 12.002309837942 15.063011877768 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.883210818105 14.169696006639 0.77775 + vertex 12.002309837942 15.063011877768 0.77775 + vertex 14.75 13.5 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 6.011309837942 24.936988122232 0.77775 + vertex 0 30 0.77775 + vertex 0 23 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 6.011309837942 24.936988122232 0.77775 + vertex 0 23 0.77775 + vertex 6.011309837942 23 0.77775 + endloop +endfacet +facet normal -0 -0 -1 + outer loop + vertex 40.75 26.5 0 + vertex 40.883210818105 25.830303993361 0 + vertex 18.116789181895 27.169696006639 0 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 0 + outer loop + vertex 44.25 13.5 0.77775 + vertex 44.116789181895 14.169696006639 0.77775 + vertex 44.116789181895 14.169696006639 0 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 -0 + outer loop + vertex 44.25 13.5 0.77775 + vertex 44.116789181895 14.169696006639 0 + vertex 44.25 13.5 0 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 17.737436867076 12.262563132924 0 + vertex 17.737436867076 12.262563132924 0.77775 + vertex 18.116789181895 12.830303993361 0.77775 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 17.737436867076 12.262563132924 0 + vertex 18.116789181895 12.830303993361 0.77775 + vertex 18.116789181895 12.830303993361 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 0 10 0.77775 + vertex 15.306548743796 10 0.77775 + vertex 14.883210818105 12.830303993361 0.77775 + endloop +endfacet +facet normal 0 -0.980785280403 0.195090322015 + outer loop + vertex 54.002309837942 24.804173569059 0.110046151702 + vertex 54.002309837942 24.936988122232 0.77775 + vertex 48.011309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 -0.980785280403 0.195090322015 + outer loop + vertex 54.002309837942 24.804173569059 0.110046151702 + vertex 48.011309837942 24.936988122232 0.77775 + vertex 48.011309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 14.883210818105 12.830303993361 0 + vertex 14.883210818105 12.830303993361 0.77775 + vertex 15.262563132924 12.262563132924 0.77775 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 14.883210818105 12.830303993361 0 + vertex 15.262563132924 12.262563132924 0.77775 + vertex 15.262563132924 12.262563132924 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 54.002309837942 15.269356918694 0 + vertex 43.737436867076 12.262563132924 0 + vertex 44.116789181895 12.830303993361 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 54.002309837942 15.269356918694 0 + vertex 44.116789181895 12.830303993361 0 + vertex 48.011309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 0 30 0 + vertex 0 30 0.77775 + vertex 14.607403236621 30 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 0 30 0 + vertex 14.607403236621 30 0.77775 + vertex 14.042553191489 30 0 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 41.262563132924 12.262563132924 0 + vertex 41.262563132924 12.262563132924 0.77775 + vertex 41.830303993361 11.883210818105 0.77775 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 41.262563132924 12.262563132924 0 + vertex 41.830303993361 11.883210818105 0.77775 + vertex 41.830303993361 11.883210818105 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 0 10 0 + vertex 0 10 0.77775 + vertex 0 17 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 0 10 0 + vertex 0 17 0.77775 + vertex 0 17 0 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 44.116789181895 12.830303993361 0.77775 + vertex 44.25 13.5 0.77775 + vertex 44.25 13.5 0 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 44.116789181895 12.830303993361 0.77775 + vertex 44.25 13.5 0 + vertex 44.116789181895 12.830303993361 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 41.422110817733 17 0 + vertex 34.416922535211 17 0.77775 + vertex 48.011309837942 17 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 41.422110817733 17 0 + vertex 48.011309837942 17 0.77775 + vertex 48.011309837942 17 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 52 30 0.77775 + vertex 52 40 0.77775 + vertex 52 40 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 52 30 0.77775 + vertex 52 40 0 + vertex 52 30 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 15.830303993361 24.883210818105 0 + vertex 16.5 24.75 0 + vertex 12.002309837942 23 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 41.262563132924 27.737436867076 0 + vertex 40.883210818105 27.169696006639 0 + vertex 17.737436867076 27.737436867076 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 41.262563132924 27.737436867076 0 + vertex 17.737436867076 27.737436867076 0 + vertex 17.169696006639 28.116789181895 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 6.011309837942 15.063011877768 0.77775 + vertex 0 10 0.77775 + vertex 14.883210818105 12.830303993361 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 6.011309837942 15.063011877768 0.77775 + vertex 14.883210818105 12.830303993361 0.77775 + vertex 12.002309837942 15.063011877768 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 17 0.77775 + vertex 43.737436867076 14.737436867076 0.77775 + vertex 44.116789181895 14.169696006639 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 17 0.77775 + vertex 44.116789181895 14.169696006639 0.77775 + vertex 48.011309837942 15.063011877768 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 15.262563132924 25.262563132924 0 + vertex 15.830303993361 24.883210818105 0 + vertex 12.002309837942 23 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 15.262563132924 25.262563132924 0 + vertex 12.002309837942 23 0 + vertex 12.002309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 44.25 26.5 0.77775 + vertex 44.116789181895 25.830303993361 0.77775 + vertex 48.011309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 40.883210818105 27.169696006639 0 + vertex 40.75 26.5 0 + vertex 18.116789181895 27.169696006639 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 40.883210818105 27.169696006639 0 + vertex 18.116789181895 27.169696006639 0 + vertex 17.737436867076 27.737436867076 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 17 0 + vertex 41.262563132924 14.737436867076 0 + vertex 17.169696006639 15.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 17 0 + vertex 17.169696006639 15.116789181895 0 + vertex 16.5 15.25 0 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 15.830303993361 15.116789181895 0 + vertex 15.830303993361 15.116789181895 0.77775 + vertex 15.262563132924 14.737436867076 0.77775 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 15.830303993361 15.116789181895 0 + vertex 15.262563132924 14.737436867076 0.77775 + vertex 15.262563132924 14.737436867076 0 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 41.262563132924 27.737436867076 0.77775 + vertex 40.883210818105 27.169696006639 0.77775 + vertex 40.883210818105 27.169696006639 0 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 41.262563132924 27.737436867076 0.77775 + vertex 40.883210818105 27.169696006639 0 + vertex 41.262563132924 27.737436867076 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 48.011309837942 23 0 + vertex 37.474084379442 23 0.77775 + vertex 17.379510917074 23 0.77775 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 48.011309837942 23 0 + vertex 17.379510917074 23 0.77775 + vertex 12.002309837942 23 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 17 0 + vertex 15.830303993361 15.116789181895 0 + vertex 15.262563132924 14.737436867076 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 17 0 + vertex 15.262563132924 14.737436867076 0 + vertex 12.002309837942 15.269356918694 0 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 40.883210818105 25.830303993361 0 + vertex 40.883210818105 25.830303993361 0.77775 + vertex 41.262563132924 25.262563132924 0.77775 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 40.883210818105 25.830303993361 0 + vertex 41.262563132924 25.262563132924 0.77775 + vertex 41.262563132924 25.262563132924 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.883210818105 12.830303993361 0 + vertex 15.262563132924 12.262563132924 0 + vertex 14.464705848856 10 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.883210818105 12.830303993361 0 + vertex 14.464705848856 10 0 + vertex 0 10 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.737436867076 27.737436867076 0.77775 + vertex 40.883210818105 27.169696006639 0.77775 + vertex 41.262563132924 27.737436867076 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.262563132924 27.737436867076 0.77775 + vertex 17.169696006639 28.116789181895 0.77775 + vertex 17.737436867076 27.737436867076 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 23 0.77775 + vertex 48.011309837942 24.936988122232 0.77775 + vertex 43.737436867076 25.262563132924 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 23 0.77775 + vertex 43.737436867076 25.262563132924 0.77775 + vertex 43.169696006639 24.883210818105 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 15.063011877768 0.77775 + vertex 44.116789181895 14.169696006639 0.77775 + vertex 44.25 13.5 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 44.116789181895 27.169696006639 0.77775 + vertex 44.25 26.5 0.77775 + vertex 48.011309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 44.116789181895 27.169696006639 0.77775 + vertex 48.011309837942 24.936988122232 0.77775 + vertex 52 30 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.116789181895 27.169696006639 0.77775 + vertex 40.883210818105 27.169696006639 0.77775 + vertex 17.737436867076 27.737436867076 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 54.002309837942 23 0.77775 + vertex 54.002309837942 24.936988122232 0.77775 + vertex 54.002309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.262563132924 27.737436867076 0.77775 + vertex 41.866228156844 30 0.77775 + vertex 14.607403236621 30 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.262563132924 27.737436867076 0.77775 + vertex 14.607403236621 30 0.77775 + vertex 17.169696006639 28.116789181895 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 15.063011877768 0.77775 + vertex 44.25 13.5 0.77775 + vertex 44.116789181895 12.830303993361 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 15.063011877768 0.77775 + vertex 44.116789181895 12.830303993361 0.77775 + vertex 52 10 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.116789181895 14.169696006639 0.77775 + vertex 18.25 13.5 0.77775 + vertex 41.262563132924 12.262563132924 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.25 26.5 0.77775 + vertex 40.883210818105 27.169696006639 0.77775 + vertex 18.116789181895 27.169696006639 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 54.002309837942 24.730643081307 0 + vertex 54.002309837942 23 0 + vertex 54.002309837942 23 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 54.002309837942 24.730643081307 0 + vertex 54.002309837942 23 0.77775 + vertex 54.002309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.830303993361 11.883210818105 0.77775 + vertex 41.262563132924 12.262563132924 0.77775 + vertex 18.25 13.5 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.830303993361 11.883210818105 0.77775 + vertex 18.25 13.5 0.77775 + vertex 18.116789181895 12.830303993361 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.116789181895 12.830303993361 0.77775 + vertex 17.737436867076 12.262563132924 0.77775 + vertex 42.5 11.75 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.116789181895 12.830303993361 0.77775 + vertex 42.5 11.75 0.77775 + vertex 41.830303993361 11.883210818105 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.737436867076 12.262563132924 0.77775 + vertex 15.306548743796 10 0.77775 + vertex 52 10 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.737436867076 12.262563132924 0.77775 + vertex 52 10 0.77775 + vertex 42.5 11.75 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 40.883210818105 12.830303993361 0.77775 + vertex 18.116789181895 14.169696006639 0.77775 + vertex 41.262563132924 12.262563132924 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.25 26.5 0.77775 + vertex 40.75 26.5 0.77775 + vertex 40.883210818105 27.169696006639 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.116789181895 25.830303993361 0.77775 + vertex 37.474084379442 23 0.77775 + vertex 40.75 26.5 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.116789181895 25.830303993361 0.77775 + vertex 40.75 26.5 0.77775 + vertex 18.25 26.5 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 40.883210818105 12.830303993361 0.77775 + vertex 34.416922535211 17 0.77775 + vertex 17.737436867076 14.737436867076 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 40.883210818105 12.830303993361 0.77775 + vertex 17.737436867076 14.737436867076 0.77775 + vertex 18.116789181895 14.169696006639 0.77775 + endloop +endfacet +endsolid diff --git a/SkeinPyPy/terminal.sh b/SkeinPyPy/terminal.sh new file mode 100755 index 0000000..bc7d246 --- /dev/null +++ b/SkeinPyPy/terminal.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# +# Script to open the bash terminal in this directory. +# +# Usage: set the executable property to true if it isn't already. Then double click the file. +# +echo 'Directory listing:' +echo '' +ls +echo '' +echo 'To run a python script (.py) listed above, try typing something like:' +echo 'python filename' +echo '' +echo 'For example, in the skeinforge directory you could run skeinforge.py by typing:' +echo 'python skeinforge.py' +echo '' +echo 'To skeinforge the test.stl file from the command line, in the skeinforge directory you could type:' +echo 'python skeinforge.py test.stl' +echo '' +bash + diff --git a/SkeinPyPy/test.stl b/SkeinPyPy/test.stl new file mode 100644 index 0000000..f372a18 --- /dev/null +++ b/SkeinPyPy/test.stl @@ -0,0 +1,2886 @@ +solid "Screw_Holder_Bottom"; Produced by Art of Illusion 2.4, Fri Oct 16 16:42:04 PDT 2009 +facet normal 0 -0.831469612304 0.555570233017 + outer loop + vertex 54.002309837942 24.804173569059 0.110046151702 + vertex 48.011309837942 24.804173569059 0.110046151702 + vertex 54.002309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 24.730643081307 0 + vertex 14.883210818105 25.830303993361 0 + vertex 15.262563132924 25.262563132924 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 60 23 0 + vertex 54.002309837942 24.730643081307 0 + vertex 60 40 0 + endloop +endfacet +facet normal 0 0.831469612301 0.555570233022 + outer loop + vertex 54.002309837942 15.269356918694 0 + vertex 48.011309837942 15.269356918694 0 + vertex 54.002309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.464705848856 10 0 + vertex 15.830303993361 11.883210818105 0 + vertex 16.5 11.75 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 52 30 0 + vertex 43.737436867076 27.737436867076 0 + vertex 43.169696006639 28.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 43.169696006639 15.116789181895 0 + vertex 42.5 15.25 0 + vertex 41.422110817733 17 0 + endloop +endfacet +facet normal 0 -0.831469612304 0.555570233017 + outer loop + vertex 48.011309837942 24.730643081307 0 + vertex 54.002309837942 24.730643081307 0 + vertex 48.011309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 52 30 0 + vertex 43.169696006639 28.116789181895 0 + vertex 42.5 28.25 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 60 40 0 + vertex 52 30 0 + vertex 52 40 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.883210818105 14.169696006639 0 + vertex 14.75 13.5 0 + vertex 12.002309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.883210818105 27.169696006639 0 + vertex 14.042553191489 30 0 + vertex 15.262563132924 27.737436867076 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.464705848856 10 0 + vertex 16.5 11.75 0 + vertex 17.169696006639 11.883210818105 0 + endloop +endfacet +facet normal 0 0.831469612301 0.555570233022 + outer loop + vertex 48.011309837942 15.195826430941 0.110046151702 + vertex 54.002309837942 15.195826430941 0.110046151702 + vertex 48.011309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.042553191489 30 0 + vertex 17.169696006639 28.116789181895 0 + vertex 16.5 28.25 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 15.269356918694 0 + vertex 14.75 13.5 0 + vertex 14.883210818105 12.830303993361 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 54.002309837942 17 0 + vertex 60 17 0 + vertex 54.002309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 52 10 0 + vertex 42.5 11.75 0 + vertex 43.169696006639 11.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 15.269356918694 0 + vertex 15.262563132924 14.737436867076 0 + vertex 14.883210818105 14.169696006639 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 52 10 0 + vertex 14.464705848856 10 0 + vertex 42.5 11.75 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.042553191489 30 0 + vertex 15.830303993361 28.116789181895 0 + vertex 15.262563132924 27.737436867076 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.464705848856 10 0 + vertex 15.262563132924 12.262563132924 0 + vertex 15.830303993361 11.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.042553191489 30 0 + vertex 16.5 28.25 0 + vertex 15.830303993361 28.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 54.002309837942 24.730643081307 0 + vertex 60 23 0 + vertex 54.002309837942 23 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 23 0 + vertex 16.5 24.75 0 + vertex 17.169696006639 24.883210818105 0 + endloop +endfacet +facet normal 0 0.831469612301 0.555570233022 + outer loop + vertex 6.011309837942 15.269356918694 0 + vertex 6.011309837942 15.195826430941 0.110046151702 + vertex 12.002309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.75 26.5 0 + vertex 12.002309837942 24.730643081307 0 + vertex 14.883210818105 27.169696006639 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 52 30 0 + vertex 60 40 0 + vertex 54.002309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 41.422110817733 17 0 + vertex 48.011309837942 17 0 + vertex 43.169696006639 15.116789181895 0 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 14.75 13.5 0 + vertex 14.883210818105 12.830303993361 0.77775 + vertex 14.883210818105 12.830303993361 0 + endloop +endfacet +facet normal -0 -0 -1 + outer loop + vertex 6.011309837942 17 0 + vertex 6.011309837942 15.269356918694 0 + vertex 0 17 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 41.422110817733 17 0 + vertex 41.830303993361 15.116789181895 0 + vertex 41.262563132924 14.737436867076 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.169696006639 11.883210818105 0 + vertex 17.737436867076 12.262563132924 0 + vertex 41.830303993361 11.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 6.011309837942 15.269356918694 0 + vertex 0 10 0 + vertex 0 17 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.883210818105 25.830303993361 0 + vertex 12.002309837942 24.730643081307 0 + vertex 14.75 26.5 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 48.011309837942 24.730643081307 0 + vertex 52 30 0 + vertex 54.002309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 48.011309837942 17 0 + vertex 43.737436867076 14.737436867076 0 + vertex 43.169696006639 15.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 42.5 15.25 0 + vertex 41.830303993361 15.116789181895 0 + vertex 41.422110817733 17 0 + endloop +endfacet +facet normal -0 -0 -1 + outer loop + vertex 12.002309837942 15.269356918694 0 + vertex 14.883210818105 12.830303993361 0 + vertex 6.011309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 0 23 0 + vertex 0 30 0 + vertex 6.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0.831469612301 0.555570233022 + outer loop + vertex 12.002309837942 15.269356918694 0 + vertex 6.011309837942 15.269356918694 0 + vertex 12.002309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 54.002309837942 15.269356918694 0 + vertex 60 17 0 + vertex 52 10 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 6.011309837942 23 0 + vertex 0 23 0 + vertex 6.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.116789181895 14.169696006639 0 + vertex 48.011309837942 15.269356918694 0 + vertex 44.25 13.5 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 18.116789181895 27.169696006639 0 + vertex 41.262563132924 25.262563132924 0 + vertex 18.25 26.5 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 18.116789181895 25.830303993361 0 + vertex 18.25 26.5 0 + vertex 41.830303993361 24.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.737436867076 14.737436867076 0 + vertex 41.262563132924 14.737436867076 0 + vertex 40.883210818105 14.169696006639 0 + endloop +endfacet +facet normal -0 -0 -1 + outer loop + vertex 41.262563132924 25.262563132924 0 + vertex 41.830303993361 24.883210818105 0 + vertex 18.25 26.5 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.737436867076 25.262563132924 0 + vertex 18.116789181895 25.830303993361 0 + vertex 41.830303993361 24.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.737436867076 12.262563132924 0 + vertex 18.116789181895 12.830303993361 0 + vertex 40.883210818105 12.830303993361 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.737436867076 12.262563132924 0 + vertex 41.262563132924 12.262563132924 0 + vertex 41.830303993361 11.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 41.262563132924 27.737436867076 0 + vertex 17.169696006639 28.116789181895 0 + vertex 41.830303993361 28.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 42.5 11.75 0 + vertex 14.464705848856 10 0 + vertex 17.169696006639 11.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.25 26.5 0 + vertex 44.116789181895 27.169696006639 0 + vertex 48.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 40.883210818105 12.830303993361 0 + vertex 41.262563132924 12.262563132924 0 + vertex 17.737436867076 12.262563132924 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.169696006639 24.883210818105 0 + vertex 17.737436867076 25.262563132924 0 + vertex 41.830303993361 24.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.116789181895 12.830303993361 0 + vertex 44.25 13.5 0 + vertex 48.011309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 18.116789181895 12.830303993361 0 + vertex 40.75 13.5 0 + vertex 40.883210818105 12.830303993361 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 18.25 13.5 0 + vertex 18.116789181895 14.169696006639 0 + vertex 40.883210818105 14.169696006639 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 41.262563132924 14.737436867076 0 + vertex 17.737436867076 14.737436867076 0 + vertex 17.169696006639 15.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 18.116789181895 14.169696006639 0 + vertex 17.737436867076 14.737436867076 0 + vertex 40.883210818105 14.169696006639 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.116789181895 25.830303993361 0 + vertex 44.25 26.5 0 + vertex 48.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 14.883210818105 14.169696006639 0.77775 + vertex 14.75 13.5 0.77775 + vertex 14.883210818105 14.169696006639 0 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 14.75 13.5 0.77775 + vertex 14.75 13.5 0 + vertex 14.883210818105 14.169696006639 0 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 41.830303993361 28.116789181895 0 + vertex 42.5 28.25 0.77775 + vertex 41.830303993361 28.116789181895 0.77775 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 15.830303993361 24.883210818105 0.77775 + vertex 15.830303993361 24.883210818105 0 + vertex 15.262563132924 25.262563132924 0 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 0 + outer loop + vertex 16.5 28.25 0 + vertex 17.169696006639 28.116789181895 0.77775 + vertex 16.5 28.25 0.77775 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 43.169696006639 24.883210818105 0 + vertex 42.5 24.75 0.77775 + vertex 43.169696006639 24.883210818105 0.77775 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 16.5 28.25 0.77775 + vertex 15.830303993361 28.116789181895 0.77775 + vertex 16.5 28.25 0 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 15.830303993361 28.116789181895 0.77775 + vertex 15.830303993361 28.116789181895 0 + vertex 16.5 28.25 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 0 23 0 + vertex 6.011309837942 23 0.77775 + vertex 0 23 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 60 0 0.77775 + vertex 52 10 0.77775 + vertex 52 0 0.77775 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 40.883210818105 27.169696006639 0 + vertex 40.883210818105 27.169696006639 0.77775 + vertex 40.75 26.5 0 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 17.169696006639 24.883210818105 0.77775 + vertex 17.169696006639 24.883210818105 0 + vertex 16.5 24.75 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 52 0 0 + vertex 52 0 0.77775 + vertex 52 10 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 34.416922535211 17 0.77775 + vertex 40.883210818105 12.830303993361 0.77775 + vertex 40.75 13.5 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 12.002309837942 23 0.77775 + vertex 12.002309837942 24.936988122232 0.77775 + vertex 12.002309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 42.5 24.75 0 + vertex 41.830303993361 24.883210818105 0.77775 + vertex 42.5 24.75 0.77775 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 60 0 0 + vertex 60 0 0.77775 + vertex 52 0 0 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 15.830303993361 28.116789181895 0.77775 + vertex 15.262563132924 27.737436867076 0.77775 + vertex 15.830303993361 28.116789181895 0 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 15.262563132924 27.737436867076 0.77775 + vertex 15.262563132924 27.737436867076 0 + vertex 15.830303993361 28.116789181895 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 6.011309837942 17 0.77775 + vertex 6.011309837942 17 0 + vertex 0 17 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 48.011309837942 17 0 + vertex 48.011309837942 17 0.77775 + vertex 48.011309837942 15.063011877768 0.77775 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 0 + outer loop + vertex 18.25 26.5 0 + vertex 18.25 26.5 0.77775 + vertex 18.116789181895 27.169696006639 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 15.262563132924 14.737436867076 0.77775 + vertex 15.830303993361 15.116789181895 0.77775 + vertex 15.455907969142 17 0.77775 + endloop +endfacet +facet normal 0 0.980785280403 0.195090322015 + outer loop + vertex 54.002309837942 15.063011877768 0.77775 + vertex 54.002309837942 15.195826430941 0.110046151702 + vertex 48.011309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 0 + outer loop + vertex 44.25 26.5 0 + vertex 44.25 26.5 0.77775 + vertex 44.116789181895 27.169696006639 0.77775 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 52 10 0 + vertex 15.306548743796 10 0.77775 + vertex 14.464705848856 10 0 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 40.75 26.5 0.77775 + vertex 40.883210818105 25.830303993361 0 + vertex 40.75 26.5 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 48.011309837942 15.063011877768 0.77775 + vertex 48.011309837942 15.195826430941 0.110046151702 + vertex 48.011309837942 15.269356918694 0 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 -0 + outer loop + vertex 44.116789181895 27.169696006639 0.77775 + vertex 43.737436867076 27.737436867076 0.77775 + vertex 44.116789181895 27.169696006639 0 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 -0 + outer loop + vertex 43.737436867076 27.737436867076 0.77775 + vertex 43.737436867076 27.737436867076 0 + vertex 44.116789181895 27.169696006639 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 52 10 0 + vertex 52 10 0.77775 + vertex 15.306548743796 10 0.77775 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 6.011309837942 23 0.77775 + vertex 6.011309837942 23 0 + vertex 6.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 34.416922535211 17 0.77775 + vertex 40.883210818105 14.169696006639 0.77775 + vertex 41.262563132924 14.737436867076 0.77775 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 14.75 26.5 0 + vertex 14.883210818105 25.830303993361 0.77775 + vertex 14.883210818105 25.830303993361 0 + endloop +endfacet +facet normal 0 -0.980785280403 0.195090322015 + outer loop + vertex 6.011309837942 24.936988122232 0.77775 + vertex 6.011309837942 24.804173569059 0.110046151702 + vertex 12.002309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 -0 + outer loop + vertex 18.116789181895 14.169696006639 0.77775 + vertex 17.737436867076 14.737436867076 0.77775 + vertex 18.116789181895 14.169696006639 0 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 -0 + outer loop + vertex 17.737436867076 14.737436867076 0.77775 + vertex 17.737436867076 14.737436867076 0 + vertex 18.116789181895 14.169696006639 0 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 42.5 15.25 0.77775 + vertex 41.830303993361 15.116789181895 0.77775 + vertex 42.5 15.25 0 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 41.830303993361 15.116789181895 0.77775 + vertex 41.830303993361 15.116789181895 0 + vertex 42.5 15.25 0 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 15.262563132924 14.737436867076 0.77775 + vertex 14.883210818105 14.169696006639 0.77775 + vertex 15.262563132924 14.737436867076 0 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 14.883210818105 14.169696006639 0.77775 + vertex 14.883210818105 14.169696006639 0 + vertex 15.262563132924 14.737436867076 0 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 0 + outer loop + vertex 43.169696006639 15.116789181895 0 + vertex 43.737436867076 14.737436867076 0.77775 + vertex 43.169696006639 15.116789181895 0.77775 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 14.75 13.5 0.77775 + vertex 14.883210818105 12.830303993361 0.77775 + vertex 14.75 13.5 0 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 41.262563132924 14.737436867076 0.77775 + vertex 40.883210818105 14.169696006639 0.77775 + vertex 40.883210818105 14.169696006639 0 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 42.5 11.75 0.77775 + vertex 43.169696006639 11.883210818105 0.77775 + vertex 43.169696006639 11.883210818105 0 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 -0 + outer loop + vertex 17.169696006639 15.116789181895 0.77775 + vertex 17.169696006639 15.116789181895 0 + vertex 17.737436867076 14.737436867076 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 12.002309837942 23 0.77775 + vertex 12.002309837942 24.730643081307 0 + vertex 12.002309837942 23 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 6.011309837942 23 0.77775 + vertex 0 23 0 + vertex 6.011309837942 23 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.116789181895 25.830303993361 0 + vertex 48.011309837942 23 0 + vertex 43.737436867076 25.262563132924 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 12.002309837942 15.195826430941 0.110046151702 + vertex 12.002309837942 17 0.77775 + vertex 12.002309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 0 10 0 + vertex 6.011309837942 15.269356918694 0 + vertex 14.883210818105 12.830303993361 0 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 -0 + outer loop + vertex 17.169696006639 28.116789181895 0.77775 + vertex 16.5 28.25 0 + vertex 17.169696006639 28.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 48.011309837942 24.730643081307 0 + vertex 44.116789181895 27.169696006639 0 + vertex 43.737436867076 27.737436867076 0 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 44.25 26.5 0.77775 + vertex 44.25 26.5 0 + vertex 44.116789181895 25.830303993361 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 40.75 26.5 0.77775 + vertex 37.474084379442 23 0.77775 + vertex 40.883210818105 25.830303993361 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 34.416922535211 17 0.77775 + vertex 40.75 13.5 0.77775 + vertex 40.883210818105 14.169696006639 0.77775 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 17.169696006639 11.883210818105 0.77775 + vertex 17.169696006639 11.883210818105 0 + vertex 16.5 11.75 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 43.737436867076 27.737436867076 0.77775 + vertex 52 30 0.77775 + vertex 41.866228156844 30 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 12.002309837942 15.063011877768 0.77775 + vertex 14.883210818105 12.830303993361 0.77775 + vertex 14.75 13.5 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 15.063011877768 0.77775 + vertex 48.011309837942 15.063011877768 0.77775 + vertex 52 10 0.77775 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 41.830303993361 24.883210818105 0.77775 + vertex 42.5 24.75 0 + vertex 41.830303993361 24.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 6.011309837942 24.730643081307 0 + vertex 14.883210818105 27.169696006639 0 + vertex 12.002309837942 24.730643081307 0 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 -0 + outer loop + vertex 43.737436867076 14.737436867076 0.77775 + vertex 43.169696006639 15.116789181895 0 + vertex 43.737436867076 14.737436867076 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 43.737436867076 27.737436867076 0.77775 + vertex 41.866228156844 30 0.77775 + vertex 43.169696006639 28.116789181895 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 34.416922535211 17 0.77775 + vertex 41.830303993361 15.116789181895 0.77775 + vertex 42.5 15.25 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 16.5 11.75 0.77775 + vertex 15.830303993361 11.883210818105 0.77775 + vertex 15.306548743796 10 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 0 23 0.77775 + vertex 0 30 0 + vertex 0 23 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 0 30 0 + vertex 0 23 0.77775 + vertex 0 30 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 52 10 0 + vertex 52 0 0.77775 + vertex 52 10 0.77775 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 17.169696006639 11.883210818105 0 + vertex 17.169696006639 11.883210818105 0.77775 + vertex 17.737436867076 12.262563132924 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 43.169696006639 28.116789181895 0.77775 + vertex 41.866228156844 30 0.77775 + vertex 42.5 28.25 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 14.042553191489 30 0 + vertex 41.866228156844 30 0.77775 + vertex 52 30 0 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 41.830303993361 11.883210818105 0.77775 + vertex 42.5 11.75 0 + vertex 41.830303993361 11.883210818105 0 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 15.262563132924 12.262563132924 0.77775 + vertex 15.830303993361 11.883210818105 0 + vertex 15.262563132924 12.262563132924 0 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 40.883210818105 25.830303993361 0 + vertex 40.75 26.5 0.77775 + vertex 40.883210818105 25.830303993361 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 18.116789181895 27.169696006639 0 + vertex 40.883210818105 25.830303993361 0 + vertex 41.262563132924 25.262563132924 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 42.5 28.25 0 + vertex 17.169696006639 28.116789181895 0 + vertex 14.042553191489 30 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.607403236621 30 0.77775 + vertex 15.262563132924 27.737436867076 0.77775 + vertex 15.830303993361 28.116789181895 0.77775 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 14.883210818105 25.830303993361 0.77775 + vertex 15.262563132924 25.262563132924 0.77775 + vertex 14.883210818105 25.830303993361 0 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 15.262563132924 25.262563132924 0.77775 + vertex 15.262563132924 25.262563132924 0 + vertex 14.883210818105 25.830303993361 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 48.011309837942 24.804173569059 0.110046151702 + vertex 48.011309837942 23 0.77775 + vertex 48.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 48.011309837942 24.730643081307 0 + vertex 43.737436867076 27.737436867076 0 + vertex 52 30 0 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 18.116789181895 25.830303993361 0 + vertex 18.116789181895 25.830303993361 0.77775 + vertex 18.25 26.5 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 54.002309837942 15.195826430941 0.110046151702 + vertex 54.002309837942 15.063011877768 0.77775 + vertex 54.002309837942 17 0.77775 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 41.830303993361 15.116789181895 0 + vertex 41.830303993361 15.116789181895 0.77775 + vertex 41.262563132924 14.737436867076 0.77775 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 -0 + outer loop + vertex 43.737436867076 27.737436867076 0.77775 + vertex 43.169696006639 28.116789181895 0.77775 + vertex 43.737436867076 27.737436867076 0 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 -0 + outer loop + vertex 43.169696006639 28.116789181895 0.77775 + vertex 43.169696006639 28.116789181895 0 + vertex 43.737436867076 27.737436867076 0 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 41.830303993361 11.883210818105 0.77775 + vertex 42.5 11.75 0.77775 + vertex 42.5 11.75 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 12.002309837942 23 0.77775 + vertex 15.830303993361 24.883210818105 0.77775 + vertex 15.262563132924 25.262563132924 0.77775 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 41.262563132924 14.737436867076 0.77775 + vertex 41.262563132924 14.737436867076 0 + vertex 41.830303993361 15.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 60 17 0 + vertex 60 0 0 + vertex 52 0 0 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 15.262563132924 25.262563132924 0 + vertex 15.262563132924 25.262563132924 0.77775 + vertex 15.830303993361 24.883210818105 0.77775 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 12.002309837942 23 0.77775 + vertex 12.002309837942 23 0 + vertex 17.379510917074 23 0.77775 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 6.011309837942 24.936988122232 0.77775 + vertex 6.011309837942 23 0.77775 + vertex 6.011309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.169696006639 11.883210818105 0 + vertex 41.830303993361 11.883210818105 0 + vertex 42.5 11.75 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 42.5 28.25 0 + vertex 14.042553191489 30 0 + vertex 52 30 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 17 0.77775 + vertex 43.169696006639 15.116789181895 0.77775 + vertex 43.737436867076 14.737436867076 0.77775 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 6.011309837942 15.269356918694 0 + vertex 6.011309837942 17 0.77775 + vertex 6.011309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 -0 + outer loop + vertex 18.116789181895 27.169696006639 0.77775 + vertex 18.116789181895 27.169696006639 0 + vertex 18.25 26.5 0 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 40.75 26.5 0 + vertex 40.883210818105 27.169696006639 0.77775 + vertex 40.75 26.5 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.607403236621 30 0.77775 + vertex 0 30 0.77775 + vertex 14.883210818105 27.169696006639 0.77775 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 42.5 11.75 0.77775 + vertex 43.169696006639 11.883210818105 0 + vertex 42.5 11.75 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.607403236621 30 0.77775 + vertex 15.830303993361 28.116789181895 0.77775 + vertex 16.5 28.25 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 52 30 0.77775 + vertex 52 30 0 + vertex 41.866228156844 30 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 60 17 0 + vertex 52 0 0 + vertex 52 10 0 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 0 + outer loop + vertex 44.116789181895 14.169696006639 0.77775 + vertex 43.737436867076 14.737436867076 0.77775 + vertex 43.737436867076 14.737436867076 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 14.607403236621 30 0.77775 + vertex 41.866228156844 30 0.77775 + vertex 14.042553191489 30 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.169696006639 11.883210818105 0.77775 + vertex 15.306548743796 10 0.77775 + vertex 17.737436867076 12.262563132924 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 12.002309837942 15.269356918694 0 + vertex 12.002309837942 17 0.77775 + vertex 12.002309837942 17 0 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 17.169696006639 24.883210818105 0.77775 + vertex 16.5 24.75 0 + vertex 16.5 24.75 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 23 0.77775 + vertex 43.169696006639 24.883210818105 0.77775 + vertex 42.5 24.75 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 6.011309837942 17 0.77775 + vertex 0 17 0 + vertex 0 17 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.607403236621 30 0.77775 + vertex 16.5 28.25 0.77775 + vertex 17.169696006639 28.116789181895 0.77775 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 18.116789181895 25.830303993361 0.77775 + vertex 17.737436867076 25.262563132924 0 + vertex 17.737436867076 25.262563132924 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 16.5 24.75 0.77775 + vertex 17.379510917074 23 0.77775 + vertex 17.169696006639 24.883210818105 0.77775 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 -0 + outer loop + vertex 44.116789181895 27.169696006639 0.77775 + vertex 44.116789181895 27.169696006639 0 + vertex 44.25 26.5 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 40.75 13.5 0 + vertex 18.25 13.5 0 + vertex 40.883210818105 14.169696006639 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.116789181895 25.830303993361 0 + vertex 48.011309837942 24.730643081307 0 + vertex 48.011309837942 23 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 0 10 0.77775 + vertex 0 10 0 + vertex 14.464705848856 10 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 15.306548743796 10 0.77775 + vertex 15.830303993361 11.883210818105 0.77775 + vertex 15.262563132924 12.262563132924 0.77775 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 0 + outer loop + vertex 43.169696006639 28.116789181895 0 + vertex 43.169696006639 28.116789181895 0.77775 + vertex 42.5 28.25 0.77775 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 17.169696006639 11.883210818105 0.77775 + vertex 16.5 11.75 0 + vertex 16.5 11.75 0.77775 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 0 + outer loop + vertex 18.25 13.5 0.77775 + vertex 18.116789181895 14.169696006639 0.77775 + vertex 18.116789181895 14.169696006639 0 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 17.169696006639 24.883210818105 0.77775 + vertex 17.737436867076 25.262563132924 0 + vertex 17.169696006639 24.883210818105 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 0 10 0.77775 + vertex 14.464705848856 10 0 + vertex 15.306548743796 10 0.77775 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 0 + outer loop + vertex 17.169696006639 15.116789181895 0.77775 + vertex 17.737436867076 14.737436867076 0 + vertex 17.737436867076 14.737436867076 0.77775 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 48.011309837942 23 0.77775 + vertex 48.011309837942 24.804173569059 0.110046151702 + vertex 48.011309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.830303993361 28.116789181895 0.77775 + vertex 41.866228156844 30 0.77775 + vertex 41.262563132924 27.737436867076 0.77775 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 16.5 11.75 0.77775 + vertex 15.830303993361 11.883210818105 0 + vertex 15.830303993361 11.883210818105 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.75 26.5 0.77775 + vertex 12.002309837942 24.936988122232 0.77775 + vertex 14.883210818105 25.830303993361 0.77775 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 -0 + outer loop + vertex 44.116789181895 14.169696006639 0.77775 + vertex 43.737436867076 14.737436867076 0 + vertex 44.116789181895 14.169696006639 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 12.002309837942 23 0.77775 + vertex 12.002309837942 24.804173569059 0.110046151702 + vertex 12.002309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 15.063011877768 0.77775 + vertex 60 17 0.77775 + vertex 54.002309837942 17 0.77775 + endloop +endfacet +facet normal 0 0.980785280403 0.195090322015 + outer loop + vertex 6.011309837942 15.063011877768 0.77775 + vertex 12.002309837942 15.195826430941 0.110046151702 + vertex 6.011309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 12.002309837942 23 0.77775 + vertex 17.379510917074 23 0.77775 + vertex 15.830303993361 24.883210818105 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 15.830303993361 24.883210818105 0.77775 + vertex 17.379510917074 23 0.77775 + vertex 16.5 24.75 0.77775 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 18.25 26.5 0.77775 + vertex 18.25 26.5 0 + vertex 18.116789181895 25.830303993361 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.262563132924 25.262563132924 0.77775 + vertex 37.474084379442 23 0.77775 + vertex 41.830303993361 24.883210818105 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.607403236621 30 0.77775 + vertex 14.883210818105 27.169696006639 0.77775 + vertex 15.262563132924 27.737436867076 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.737436867076 14.737436867076 0.77775 + vertex 15.455907969142 17 0.77775 + vertex 17.169696006639 15.116789181895 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.737436867076 14.737436867076 0.77775 + vertex 34.416922535211 17 0.77775 + vertex 15.455907969142 17 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 15.306548743796 10 0.77775 + vertex 15.262563132924 12.262563132924 0.77775 + vertex 14.883210818105 12.830303993361 0.77775 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 17.169696006639 24.883210818105 0.77775 + vertex 17.737436867076 25.262563132924 0.77775 + vertex 17.737436867076 25.262563132924 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 16.5 15.25 0.77775 + vertex 15.455907969142 17 0.77775 + vertex 15.830303993361 15.116789181895 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.830303993361 28.116789181895 0.77775 + vertex 42.5 28.25 0.77775 + vertex 41.866228156844 30 0.77775 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 18.116789181895 12.830303993361 0.77775 + vertex 18.25 13.5 0.77775 + vertex 18.25 13.5 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 60 17 0.77775 + vertex 60 17 0 + vertex 54.002309837942 17 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 15.455907969142 17 0.77775 + vertex 12.002309837942 17 0 + vertex 12.002309837942 17 0.77775 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 43.169696006639 11.883210818105 0 + vertex 43.737436867076 12.262563132924 0.77775 + vertex 43.737436867076 12.262563132924 0 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 43.169696006639 11.883210818105 0 + vertex 43.169696006639 11.883210818105 0.77775 + vertex 43.737436867076 12.262563132924 0.77775 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 -0 + outer loop + vertex 18.25 13.5 0.77775 + vertex 18.116789181895 14.169696006639 0 + vertex 18.25 13.5 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 42.5 28.25 0 + vertex 41.830303993361 28.116789181895 0 + vertex 17.169696006639 28.116789181895 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 48.011309837942 23 0.77775 + vertex 48.011309837942 23 0 + vertex 48.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.169696006639 15.116789181895 0.77775 + vertex 15.455907969142 17 0.77775 + vertex 16.5 15.25 0.77775 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 17.737436867076 12.262563132924 0.77775 + vertex 17.737436867076 12.262563132924 0 + vertex 17.169696006639 11.883210818105 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 52 40 0.77775 + vertex 60 23 0.77775 + vertex 60 40 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 40.75 13.5 0 + vertex 18.116789181895 12.830303993361 0 + vertex 18.25 13.5 0 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 0 + outer loop + vertex 43.169696006639 28.116789181895 0 + vertex 42.5 28.25 0.77775 + vertex 42.5 28.25 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 48.011309837942 23 0 + vertex 12.002309837942 23 0 + vertex 42.5 24.75 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 23 0.77775 + vertex 60 23 0.77775 + vertex 54.002309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 6.011309837942 23 0.77775 + vertex 6.011309837942 24.730643081307 0 + vertex 6.011309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 0 + outer loop + vertex 17.169696006639 15.116789181895 0 + vertex 16.5 15.25 0.77775 + vertex 16.5 15.25 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 6.011309837942 17 0.77775 + vertex 6.011309837942 15.063011877768 0.77775 + vertex 6.011309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 60 17 0.77775 + vertex 60 0 0 + vertex 60 17 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 60 0 0.77775 + vertex 60 0 0 + vertex 60 17 0.77775 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 15.262563132924 12.262563132924 0.77775 + vertex 15.830303993361 11.883210818105 0.77775 + vertex 15.830303993361 11.883210818105 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 52 10 0.77775 + vertex 44.116789181895 12.830303993361 0.77775 + vertex 43.737436867076 12.262563132924 0.77775 + endloop +endfacet +facet normal 0 0.980785280403 0.195090322015 + outer loop + vertex 6.011309837942 15.063011877768 0.77775 + vertex 12.002309837942 15.063011877768 0.77775 + vertex 12.002309837942 15.195826430941 0.110046151702 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.169696006639 24.883210818105 0.77775 + vertex 17.379510917074 23 0.77775 + vertex 17.737436867076 25.262563132924 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 52 10 0.77775 + vertex 43.737436867076 12.262563132924 0.77775 + vertex 43.169696006639 11.883210818105 0.77775 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 42.5 28.25 0.77775 + vertex 41.830303993361 28.116789181895 0 + vertex 42.5 28.25 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 6.011309837942 15.269356918694 0 + vertex 6.011309837942 17 0 + vertex 6.011309837942 17 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 40.883210818105 25.830303993361 0.77775 + vertex 37.474084379442 23 0.77775 + vertex 41.262563132924 25.262563132924 0.77775 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 18.116789181895 12.830303993361 0.77775 + vertex 18.25 13.5 0 + vertex 18.116789181895 12.830303993361 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.737436867076 25.262563132924 0.77775 + vertex 17.379510917074 23 0.77775 + vertex 18.116789181895 25.830303993361 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 34.416922535211 17 0.77775 + vertex 41.262563132924 14.737436867076 0.77775 + vertex 41.830303993361 15.116789181895 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 0 30 0.77775 + vertex 6.011309837942 24.936988122232 0.77775 + vertex 12.002309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.116789181895 25.830303993361 0.77775 + vertex 17.379510917074 23 0.77775 + vertex 37.474084379442 23 0.77775 + endloop +endfacet +facet normal 0 -0.980785280403 0.195090322015 + outer loop + vertex 6.011309837942 24.936988122232 0.77775 + vertex 12.002309837942 24.804173569059 0.110046151702 + vertex 12.002309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 52 10 0.77775 + vertex 43.169696006639 11.883210818105 0.77775 + vertex 42.5 11.75 0.77775 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 41.262563132924 14.737436867076 0.77775 + vertex 40.883210818105 14.169696006639 0 + vertex 41.262563132924 14.737436867076 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 48.011309837942 23 0 + vertex 43.169696006639 24.883210818105 0 + vertex 43.737436867076 25.262563132924 0 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 44.25 26.5 0.77775 + vertex 44.116789181895 25.830303993361 0 + vertex 44.116789181895 25.830303993361 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 60 17 0.77775 + vertex 54.002309837942 17 0 + vertex 54.002309837942 17 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 48.011309837942 23 0 + vertex 42.5 24.75 0 + vertex 43.169696006639 24.883210818105 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 43.737436867076 27.737436867076 0.77775 + vertex 44.116789181895 27.169696006639 0.77775 + vertex 52 30 0.77775 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 18.116789181895 25.830303993361 0.77775 + vertex 18.116789181895 25.830303993361 0 + vertex 17.737436867076 25.262563132924 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 54.002309837942 15.195826430941 0.110046151702 + vertex 54.002309837942 17 0.77775 + vertex 54.002309837942 15.269356918694 0 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 14.883210818105 27.169696006639 0.77775 + vertex 14.75 26.5 0 + vertex 14.883210818105 27.169696006639 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 52 0 0.77775 + vertex 52 0 0 + vertex 60 0 0.77775 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 14.883210818105 27.169696006639 0.77775 + vertex 14.75 26.5 0.77775 + vertex 14.75 26.5 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 54.002309837942 15.269356918694 0 + vertex 54.002309837942 17 0.77775 + vertex 54.002309837942 17 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 16.5 11.75 0.77775 + vertex 15.306548743796 10 0.77775 + vertex 17.169696006639 11.883210818105 0.77775 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 0 + outer loop + vertex 17.169696006639 15.116789181895 0 + vertex 17.169696006639 15.116789181895 0.77775 + vertex 16.5 15.25 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 12.002309837942 15.195826430941 0.110046151702 + vertex 12.002309837942 15.063011877768 0.77775 + vertex 12.002309837942 17 0.77775 + endloop +endfacet +facet normal 0 0.980785280403 0.195090322015 + outer loop + vertex 54.002309837942 15.063011877768 0.77775 + vertex 48.011309837942 15.195826430941 0.110046151702 + vertex 48.011309837942 15.063011877768 0.77775 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 16.5 11.75 0.77775 + vertex 16.5 11.75 0 + vertex 15.830303993361 11.883210818105 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 48.011309837942 15.063011877768 0.77775 + vertex 48.011309837942 15.269356918694 0 + vertex 48.011309837942 17 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 24.936988122232 0.77775 + vertex 52 30 0.77775 + vertex 48.011309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal -0.195090322016 0.980785280403 0 + outer loop + vertex 42.5 24.75 0.77775 + vertex 43.169696006639 24.883210818105 0 + vertex 42.5 24.75 0 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 14.75 26.5 0 + vertex 14.75 26.5 0.77775 + vertex 14.883210818105 25.830303993361 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.116789181895 14.169696006639 0 + vertex 43.737436867076 14.737436867076 0 + vertex 48.011309837942 17 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 44.116789181895 14.169696006639 0 + vertex 48.011309837942 17 0 + vertex 48.011309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.169696006639 24.883210818105 0 + vertex 41.830303993361 24.883210818105 0 + vertex 42.5 24.75 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 17.169696006639 24.883210818105 0 + vertex 42.5 24.75 0 + vertex 12.002309837942 23 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 52 40 0 + vertex 52 40 0.77775 + vertex 60 40 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 52 40 0 + vertex 60 40 0.77775 + vertex 60 40 0 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 0 + outer loop + vertex 18.116789181895 27.169696006639 0.77775 + vertex 17.737436867076 27.737436867076 0.77775 + vertex 17.737436867076 27.737436867076 0 + endloop +endfacet +facet normal -0.831469612302 -0.555570233021 -0 + outer loop + vertex 18.116789181895 27.169696006639 0.77775 + vertex 17.737436867076 27.737436867076 0 + vertex 18.116789181895 27.169696006639 0 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 15.830303993361 24.883210818105 0 + vertex 15.830303993361 24.883210818105 0.77775 + vertex 16.5 24.75 0.77775 + endloop +endfacet +facet normal 0.195090322016 0.980785280403 0 + outer loop + vertex 15.830303993361 24.883210818105 0 + vertex 16.5 24.75 0.77775 + vertex 16.5 24.75 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 12.002309837942 24.936988122232 0.77775 + vertex 12.002309837942 23 0.77775 + vertex 15.262563132924 25.262563132924 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 12.002309837942 24.936988122232 0.77775 + vertex 15.262563132924 25.262563132924 0.77775 + vertex 14.883210818105 25.830303993361 0.77775 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 60 40 0.77775 + vertex 60 23 0.77775 + vertex 60 23 0 + endloop +endfacet +facet normal 1 0 0 + outer loop + vertex 60 40 0.77775 + vertex 60 23 0 + vertex 60 40 0 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 15.262563132924 27.737436867076 0 + vertex 15.262563132924 27.737436867076 0.77775 + vertex 14.883210818105 27.169696006639 0.77775 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 15.262563132924 27.737436867076 0 + vertex 14.883210818105 27.169696006639 0.77775 + vertex 14.883210818105 27.169696006639 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 37.474084379442 23 0.77775 + vertex 48.011309837942 23 0.77775 + vertex 42.5 24.75 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 37.474084379442 23 0.77775 + vertex 42.5 24.75 0.77775 + vertex 41.830303993361 24.883210818105 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 41.262563132924 14.737436867076 0 + vertex 12.002309837942 17 0 + vertex 41.422110817733 17 0 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 0 + outer loop + vertex 43.169696006639 15.116789181895 0.77775 + vertex 42.5 15.25 0.77775 + vertex 42.5 15.25 0 + endloop +endfacet +facet normal -0.195090322016 -0.980785280403 -0 + outer loop + vertex 43.169696006639 15.116789181895 0.77775 + vertex 42.5 15.25 0 + vertex 43.169696006639 15.116789181895 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 15.063011877768 0.77775 + vertex 52 10 0.77775 + vertex 60 0 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 15.063011877768 0.77775 + vertex 60 0 0.77775 + vertex 60 17 0.77775 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 40.75 13.5 0.77775 + vertex 40.883210818105 12.830303993361 0.77775 + vertex 40.883210818105 12.830303993361 0 + endloop +endfacet +facet normal 0.980785280403 0.195090322016 0 + outer loop + vertex 40.75 13.5 0.77775 + vertex 40.883210818105 12.830303993361 0 + vertex 40.75 13.5 0 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 0 + outer loop + vertex 17.737436867076 27.737436867076 0 + vertex 17.737436867076 27.737436867076 0.77775 + vertex 17.169696006639 28.116789181895 0.77775 + endloop +endfacet +facet normal -0.555570233021 -0.831469612302 0 + outer loop + vertex 17.737436867076 27.737436867076 0 + vertex 17.169696006639 28.116789181895 0.77775 + vertex 17.169696006639 28.116789181895 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.883210818105 27.169696006639 0.77775 + vertex 0 30 0.77775 + vertex 12.002309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.883210818105 27.169696006639 0.77775 + vertex 12.002309837942 24.936988122232 0.77775 + vertex 14.75 26.5 0.77775 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 43.737436867076 12.262563132924 0.77775 + vertex 44.116789181895 12.830303993361 0.77775 + vertex 44.116789181895 12.830303993361 0 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 43.737436867076 12.262563132924 0.77775 + vertex 44.116789181895 12.830303993361 0 + vertex 43.737436867076 12.262563132924 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 48.011309837942 23 0 + vertex 48.011309837942 23 0.77775 + vertex 37.474084379442 23 0.77775 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 16.5 15.25 0.77775 + vertex 15.830303993361 15.116789181895 0.77775 + vertex 15.830303993361 15.116789181895 0 + endloop +endfacet +facet normal 0.195090322016 -0.980785280403 0 + outer loop + vertex 16.5 15.25 0.77775 + vertex 15.830303993361 15.116789181895 0 + vertex 16.5 15.25 0 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 40.883210818105 12.830303993361 0.77775 + vertex 41.262563132924 12.262563132924 0.77775 + vertex 41.262563132924 12.262563132924 0 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 40.883210818105 12.830303993361 0.77775 + vertex 41.262563132924 12.262563132924 0 + vertex 40.883210818105 12.830303993361 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 17 0 + vertex 16.5 15.25 0 + vertex 15.830303993361 15.116789181895 0 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 43.737436867076 25.262563132924 0 + vertex 43.737436867076 25.262563132924 0.77775 + vertex 44.116789181895 25.830303993361 0.77775 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 43.737436867076 25.262563132924 0 + vertex 44.116789181895 25.830303993361 0.77775 + vertex 44.116789181895 25.830303993361 0 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 40.883210818105 14.169696006639 0.77775 + vertex 40.75 13.5 0.77775 + vertex 40.75 13.5 0 + endloop +endfacet +facet normal 0.980785280403 -0.195090322016 0 + outer loop + vertex 40.883210818105 14.169696006639 0.77775 + vertex 40.75 13.5 0 + vertex 40.883210818105 14.169696006639 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 60 23 0 + vertex 60 23 0.77775 + vertex 54.002309837942 23 0.77775 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 60 23 0 + vertex 54.002309837942 23 0.77775 + vertex 54.002309837942 23 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 0 30 0 + vertex 14.042553191489 30 0 + vertex 14.883210818105 27.169696006639 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 0 30 0 + vertex 14.883210818105 27.169696006639 0 + vertex 6.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 41.262563132924 25.262563132924 0 + vertex 41.262563132924 25.262563132924 0.77775 + vertex 41.830303993361 24.883210818105 0.77775 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 41.262563132924 25.262563132924 0 + vertex 41.830303993361 24.883210818105 0.77775 + vertex 41.830303993361 24.883210818105 0 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 41.830303993361 28.116789181895 0.77775 + vertex 41.262563132924 27.737436867076 0.77775 + vertex 41.262563132924 27.737436867076 0 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 41.830303993361 28.116789181895 0.77775 + vertex 41.262563132924 27.737436867076 0 + vertex 41.830303993361 28.116789181895 0 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 43.169696006639 24.883210818105 0.77775 + vertex 43.737436867076 25.262563132924 0.77775 + vertex 43.737436867076 25.262563132924 0 + endloop +endfacet +facet normal -0.555570233021 0.831469612302 0 + outer loop + vertex 43.169696006639 24.883210818105 0.77775 + vertex 43.737436867076 25.262563132924 0 + vertex 43.169696006639 24.883210818105 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 52 10 0 + vertex 43.169696006639 11.883210818105 0 + vertex 43.737436867076 12.262563132924 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 52 10 0 + vertex 43.737436867076 12.262563132924 0 + vertex 54.002309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 24.936988122232 0.77775 + vertex 60 23 0.77775 + vertex 52 40 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 54.002309837942 24.936988122232 0.77775 + vertex 52 40 0.77775 + vertex 52 30 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 15.455907969142 17 0.77775 + vertex 34.416922535211 17 0.77775 + vertex 41.422110817733 17 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 15.455907969142 17 0.77775 + vertex 41.422110817733 17 0 + vertex 12.002309837942 17 0 + endloop +endfacet +facet normal 0 -0.831469612304 0.555570233017 + outer loop + vertex 12.002309837942 24.804173569059 0.110046151702 + vertex 6.011309837942 24.804173569059 0.110046151702 + vertex 6.011309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 -0.831469612304 0.555570233017 + outer loop + vertex 12.002309837942 24.804173569059 0.110046151702 + vertex 6.011309837942 24.730643081307 0 + vertex 12.002309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 15.262563132924 14.737436867076 0.77775 + vertex 15.455907969142 17 0.77775 + vertex 12.002309837942 17 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 15.262563132924 14.737436867076 0.77775 + vertex 12.002309837942 17 0.77775 + vertex 14.883210818105 14.169696006639 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 44.116789181895 25.830303993361 0.77775 + vertex 43.737436867076 25.262563132924 0.77775 + vertex 48.011309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 0 17 0.77775 + vertex 0 10 0.77775 + vertex 6.011309837942 15.063011877768 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 0 17 0.77775 + vertex 6.011309837942 15.063011877768 0.77775 + vertex 6.011309837942 17 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 17 0.77775 + vertex 34.416922535211 17 0.77775 + vertex 42.5 15.25 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 17 0.77775 + vertex 42.5 15.25 0.77775 + vertex 43.169696006639 15.116789181895 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.883210818105 14.169696006639 0.77775 + vertex 12.002309837942 17 0.77775 + vertex 12.002309837942 15.063011877768 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 14.883210818105 14.169696006639 0.77775 + vertex 12.002309837942 15.063011877768 0.77775 + vertex 14.75 13.5 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 6.011309837942 24.936988122232 0.77775 + vertex 0 30 0.77775 + vertex 0 23 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 6.011309837942 24.936988122232 0.77775 + vertex 0 23 0.77775 + vertex 6.011309837942 23 0.77775 + endloop +endfacet +facet normal -0 -0 -1 + outer loop + vertex 40.75 26.5 0 + vertex 40.883210818105 25.830303993361 0 + vertex 18.116789181895 27.169696006639 0 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 0 + outer loop + vertex 44.25 13.5 0.77775 + vertex 44.116789181895 14.169696006639 0.77775 + vertex 44.116789181895 14.169696006639 0 + endloop +endfacet +facet normal -0.980785280403 -0.195090322016 -0 + outer loop + vertex 44.25 13.5 0.77775 + vertex 44.116789181895 14.169696006639 0 + vertex 44.25 13.5 0 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 17.737436867076 12.262563132924 0 + vertex 17.737436867076 12.262563132924 0.77775 + vertex 18.116789181895 12.830303993361 0.77775 + endloop +endfacet +facet normal -0.831469612302 0.555570233021 0 + outer loop + vertex 17.737436867076 12.262563132924 0 + vertex 18.116789181895 12.830303993361 0.77775 + vertex 18.116789181895 12.830303993361 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 0 10 0.77775 + vertex 15.306548743796 10 0.77775 + vertex 14.883210818105 12.830303993361 0.77775 + endloop +endfacet +facet normal 0 -0.980785280403 0.195090322015 + outer loop + vertex 54.002309837942 24.804173569059 0.110046151702 + vertex 54.002309837942 24.936988122232 0.77775 + vertex 48.011309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 -0.980785280403 0.195090322015 + outer loop + vertex 54.002309837942 24.804173569059 0.110046151702 + vertex 48.011309837942 24.936988122232 0.77775 + vertex 48.011309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 14.883210818105 12.830303993361 0 + vertex 14.883210818105 12.830303993361 0.77775 + vertex 15.262563132924 12.262563132924 0.77775 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 14.883210818105 12.830303993361 0 + vertex 15.262563132924 12.262563132924 0.77775 + vertex 15.262563132924 12.262563132924 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 54.002309837942 15.269356918694 0 + vertex 43.737436867076 12.262563132924 0 + vertex 44.116789181895 12.830303993361 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 54.002309837942 15.269356918694 0 + vertex 44.116789181895 12.830303993361 0 + vertex 48.011309837942 15.269356918694 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 0 30 0 + vertex 0 30 0.77775 + vertex 14.607403236621 30 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 0 30 0 + vertex 14.607403236621 30 0.77775 + vertex 14.042553191489 30 0 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 41.262563132924 12.262563132924 0 + vertex 41.262563132924 12.262563132924 0.77775 + vertex 41.830303993361 11.883210818105 0.77775 + endloop +endfacet +facet normal 0.555570233021 0.831469612302 0 + outer loop + vertex 41.262563132924 12.262563132924 0 + vertex 41.830303993361 11.883210818105 0.77775 + vertex 41.830303993361 11.883210818105 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 0 10 0 + vertex 0 10 0.77775 + vertex 0 17 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 0 10 0 + vertex 0 17 0.77775 + vertex 0 17 0 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 44.116789181895 12.830303993361 0.77775 + vertex 44.25 13.5 0.77775 + vertex 44.25 13.5 0 + endloop +endfacet +facet normal -0.980785280403 0.195090322016 0 + outer loop + vertex 44.116789181895 12.830303993361 0.77775 + vertex 44.25 13.5 0 + vertex 44.116789181895 12.830303993361 0 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 41.422110817733 17 0 + vertex 34.416922535211 17 0.77775 + vertex 48.011309837942 17 0.77775 + endloop +endfacet +facet normal 0 1 0 + outer loop + vertex 41.422110817733 17 0 + vertex 48.011309837942 17 0.77775 + vertex 48.011309837942 17 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 52 30 0.77775 + vertex 52 40 0.77775 + vertex 52 40 0 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 52 30 0.77775 + vertex 52 40 0 + vertex 52 30 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 15.830303993361 24.883210818105 0 + vertex 16.5 24.75 0 + vertex 12.002309837942 23 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 41.262563132924 27.737436867076 0 + vertex 40.883210818105 27.169696006639 0 + vertex 17.737436867076 27.737436867076 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 41.262563132924 27.737436867076 0 + vertex 17.737436867076 27.737436867076 0 + vertex 17.169696006639 28.116789181895 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 6.011309837942 15.063011877768 0.77775 + vertex 0 10 0.77775 + vertex 14.883210818105 12.830303993361 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 6.011309837942 15.063011877768 0.77775 + vertex 14.883210818105 12.830303993361 0.77775 + vertex 12.002309837942 15.063011877768 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 17 0.77775 + vertex 43.737436867076 14.737436867076 0.77775 + vertex 44.116789181895 14.169696006639 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 17 0.77775 + vertex 44.116789181895 14.169696006639 0.77775 + vertex 48.011309837942 15.063011877768 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 15.262563132924 25.262563132924 0 + vertex 15.830303993361 24.883210818105 0 + vertex 12.002309837942 23 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 15.262563132924 25.262563132924 0 + vertex 12.002309837942 23 0 + vertex 12.002309837942 24.730643081307 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 44.25 26.5 0.77775 + vertex 44.116789181895 25.830303993361 0.77775 + vertex 48.011309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 40.883210818105 27.169696006639 0 + vertex 40.75 26.5 0 + vertex 18.116789181895 27.169696006639 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 40.883210818105 27.169696006639 0 + vertex 18.116789181895 27.169696006639 0 + vertex 17.737436867076 27.737436867076 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 17 0 + vertex 41.262563132924 14.737436867076 0 + vertex 17.169696006639 15.116789181895 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 17 0 + vertex 17.169696006639 15.116789181895 0 + vertex 16.5 15.25 0 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 15.830303993361 15.116789181895 0 + vertex 15.830303993361 15.116789181895 0.77775 + vertex 15.262563132924 14.737436867076 0.77775 + endloop +endfacet +facet normal 0.555570233021 -0.831469612302 0 + outer loop + vertex 15.830303993361 15.116789181895 0 + vertex 15.262563132924 14.737436867076 0.77775 + vertex 15.262563132924 14.737436867076 0 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 41.262563132924 27.737436867076 0.77775 + vertex 40.883210818105 27.169696006639 0.77775 + vertex 40.883210818105 27.169696006639 0 + endloop +endfacet +facet normal 0.831469612302 -0.555570233021 0 + outer loop + vertex 41.262563132924 27.737436867076 0.77775 + vertex 40.883210818105 27.169696006639 0 + vertex 41.262563132924 27.737436867076 0 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 48.011309837942 23 0 + vertex 37.474084379442 23 0.77775 + vertex 17.379510917074 23 0.77775 + endloop +endfacet +facet normal 0 -1 0 + outer loop + vertex 48.011309837942 23 0 + vertex 17.379510917074 23 0.77775 + vertex 12.002309837942 23 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 17 0 + vertex 15.830303993361 15.116789181895 0 + vertex 15.262563132924 14.737436867076 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 12.002309837942 17 0 + vertex 15.262563132924 14.737436867076 0 + vertex 12.002309837942 15.269356918694 0 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 40.883210818105 25.830303993361 0 + vertex 40.883210818105 25.830303993361 0.77775 + vertex 41.262563132924 25.262563132924 0.77775 + endloop +endfacet +facet normal 0.831469612302 0.555570233021 0 + outer loop + vertex 40.883210818105 25.830303993361 0 + vertex 41.262563132924 25.262563132924 0.77775 + vertex 41.262563132924 25.262563132924 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.883210818105 12.830303993361 0 + vertex 15.262563132924 12.262563132924 0 + vertex 14.464705848856 10 0 + endloop +endfacet +facet normal 0 0 -1 + outer loop + vertex 14.883210818105 12.830303993361 0 + vertex 14.464705848856 10 0 + vertex 0 10 0 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.737436867076 27.737436867076 0.77775 + vertex 40.883210818105 27.169696006639 0.77775 + vertex 41.262563132924 27.737436867076 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.262563132924 27.737436867076 0.77775 + vertex 17.169696006639 28.116789181895 0.77775 + vertex 17.737436867076 27.737436867076 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 23 0.77775 + vertex 48.011309837942 24.936988122232 0.77775 + vertex 43.737436867076 25.262563132924 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 23 0.77775 + vertex 43.737436867076 25.262563132924 0.77775 + vertex 43.169696006639 24.883210818105 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 15.063011877768 0.77775 + vertex 44.116789181895 14.169696006639 0.77775 + vertex 44.25 13.5 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 44.116789181895 27.169696006639 0.77775 + vertex 44.25 26.5 0.77775 + vertex 48.011309837942 24.936988122232 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 44.116789181895 27.169696006639 0.77775 + vertex 48.011309837942 24.936988122232 0.77775 + vertex 52 30 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.116789181895 27.169696006639 0.77775 + vertex 40.883210818105 27.169696006639 0.77775 + vertex 17.737436867076 27.737436867076 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 54.002309837942 23 0.77775 + vertex 54.002309837942 24.936988122232 0.77775 + vertex 54.002309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.262563132924 27.737436867076 0.77775 + vertex 41.866228156844 30 0.77775 + vertex 14.607403236621 30 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.262563132924 27.737436867076 0.77775 + vertex 14.607403236621 30 0.77775 + vertex 17.169696006639 28.116789181895 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 15.063011877768 0.77775 + vertex 44.25 13.5 0.77775 + vertex 44.116789181895 12.830303993361 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 48.011309837942 15.063011877768 0.77775 + vertex 44.116789181895 12.830303993361 0.77775 + vertex 52 10 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.116789181895 14.169696006639 0.77775 + vertex 18.25 13.5 0.77775 + vertex 41.262563132924 12.262563132924 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.25 26.5 0.77775 + vertex 40.883210818105 27.169696006639 0.77775 + vertex 18.116789181895 27.169696006639 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 54.002309837942 24.730643081307 0 + vertex 54.002309837942 23 0 + vertex 54.002309837942 23 0.77775 + endloop +endfacet +facet normal -1 0 0 + outer loop + vertex 54.002309837942 24.730643081307 0 + vertex 54.002309837942 23 0.77775 + vertex 54.002309837942 24.804173569059 0.110046151702 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.830303993361 11.883210818105 0.77775 + vertex 41.262563132924 12.262563132924 0.77775 + vertex 18.25 13.5 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 41.830303993361 11.883210818105 0.77775 + vertex 18.25 13.5 0.77775 + vertex 18.116789181895 12.830303993361 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.116789181895 12.830303993361 0.77775 + vertex 17.737436867076 12.262563132924 0.77775 + vertex 42.5 11.75 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.116789181895 12.830303993361 0.77775 + vertex 42.5 11.75 0.77775 + vertex 41.830303993361 11.883210818105 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.737436867076 12.262563132924 0.77775 + vertex 15.306548743796 10 0.77775 + vertex 52 10 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 17.737436867076 12.262563132924 0.77775 + vertex 52 10 0.77775 + vertex 42.5 11.75 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 40.883210818105 12.830303993361 0.77775 + vertex 18.116789181895 14.169696006639 0.77775 + vertex 41.262563132924 12.262563132924 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.25 26.5 0.77775 + vertex 40.75 26.5 0.77775 + vertex 40.883210818105 27.169696006639 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.116789181895 25.830303993361 0.77775 + vertex 37.474084379442 23 0.77775 + vertex 40.75 26.5 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 18.116789181895 25.830303993361 0.77775 + vertex 40.75 26.5 0.77775 + vertex 18.25 26.5 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 40.883210818105 12.830303993361 0.77775 + vertex 34.416922535211 17 0.77775 + vertex 17.737436867076 14.737436867076 0.77775 + endloop +endfacet +facet normal 0 0 1 + outer loop + vertex 40.883210818105 12.830303993361 0.77775 + vertex 17.737436867076 14.737436867076 0.77775 + vertex 18.116789181895 14.169696006639 0.77775 + endloop +endfacet +endsolid diff --git a/build.sh b/build.sh index f28f92b..aa75b50 100755 --- a/build.sh +++ b/build.sh @@ -1,67 +1,103 @@ #!/bin/sh +############################# +# CONFIGURATION +############################# + +PYPY_VERSION=1.8 +WIN_PORTABLE_PY_VERSION=2.7.2.1 +WIN_PYSERIAL_VERSION=2.5 + +############################# +# Actual build script +############################# + +#Check if we have 7zip, needed to extract and packup a bunch of packages. 7z > /dev/null 2>&1 if [ $? != 0 ]; then echo $0 requires 7zip to run. exit 1 fi -#Get portable python and extract it. -if [ ! -f "PortablePython_2.7.2.1.exe" ]; then - wget http://ftp.nluug.nl/languages/python/portablepython/v2.7/PortablePython_2.7.2.1.exe -fi -if [ ! -f pyserial.exe ]; then - wget http://sourceforge.net/projects/pyserial/files/pyserial/2.5/pyserial-2.5.win32.exe/download - mv download pyserial.exe -fi -if [ ! -d target/python ]; then - 7z x PortablePython_2.7.2.1.exe \$_OUTDIR/App - 7z x PortablePython_2.7.2.1.exe \$_OUTDIR/Lib/site-packages - 7z x pyserial.exe PURELIB - mkdir -p target/python - mv \$_OUTDIR/App/* target/python - mv \$_OUTDIR/Lib/site-packages/wx* target/python/Lib/site-packages/ - mv PURELIB/serial target/python/Lib - rm -rf \$_OUTDIR - rm -rf PURELIB -fi +############################# +# Download all needed files. +############################# -#Get pypy and extract it -if [ ! -f "pypy-1.7-win32.zip" ]; then - wget https://bitbucket.org/pypy/pypy/downloads/pypy-1.7-win32.zip +#Get portable python for windows and extract it. (Linux and Mac need to install python themselfs) +if [ ! -f "PortablePython_${WIN_PORTABLE_PY_VERSION}.exe" ]; then + wget http://ftp.nluug.nl/languages/python/portablepython/v2.7/PortablePython_${WIN_PORTABLE_PY_VERSION}.exe fi -if [ ! -d target/pypy-1.7 ]; then - mkdir -p target/pypy-1.7 - cd target - 7z x ../pypy-1.7-win32.zip - cd .. +if [ ! -f pyserial-${WIN_PYSERIAL_VERSION}.exe ]; then + wget http://sourceforge.net/projects/pyserial/files/pyserial/${WIN_PYSERIAL_VERSION}/pyserial-${WIN_PYSERIAL_VERSION}.win32.exe/download + mv download pyserial-${WIN_PYSERIAL_VERSION}.exe fi - -for NR in `ls patches`; do - if [ ! -f "${NR}_reprap_python_beanshell.zip" ]; then - wget http://fabmetheus.crsndoo.com/files/${NR}_reprap_python_beanshell.zip - fi - if [ ! -d "ori/${NR}" ]; then - mkdir -p ori/${NR} - cd ori/${NR} - 7z x ../../${NR}_reprap_python_beanshell.zip - cd ../.. - fi - rm -rf target/SF${NR} - cp -a ori/${NR} target/SF${NR} - cd target/SF${NR} - patch -p 2 < ../../patches/${NR} - cd ../.. - echo "python\\python.exe SF${NR}\\skeinforge_application\\skeinforge.py" > target/SF${NR}.bat - echo $NR -done - +#Get pypy +if [ ! -f "pypy-${PYPY_VERSION}-win32.zip" ]; then + wget https://bitbucket.org/pypy/pypy/downloads/pypy-${PYPY_VERSION}-win32.zip +fi +if [ ! -f "pypy-${PYPY_VERSION}-linux.tar.bz2" ]; then + wget https://bitbucket.org/pypy/pypy/downloads/pypy-${PYPY_VERSION}-linux.tar.bz2 +fi +if [ ! -f "pypy-${PYPY_VERSION}-osx64.tar.bz2" ]; then + wget https://bitbucket.org/pypy/pypy/downloads/pypy-${PYPY_VERSION}-osx64.tar.bz2 +fi +#Get our own version of Printrun rm -rf Printrun git clone git://github.com/daid/Printrun.git rm -rf Printrun/.git -echo "python\\python.exe printrun\\pronterface.py" > printrun.bat -cd target -7z a ../Skeinforge_PyPy.zip * +############################# +# Build the packages +############################# +rm -rf target_win32 target_linux target_osx64 +mkdir -p target_win32 target_linux target_osx64 + +7z x PortablePython_${WIN_PORTABLE_PY_VERSION}.exe \$_OUTDIR/App +7z x PortablePython_${WIN_PORTABLE_PY_VERSION}.exe \$_OUTDIR/Lib/site-packages +7z x pyserial-${WIN_PYSERIAL_VERSION}.exe PURELIB + +mkdir -p target_win32/python +mv \$_OUTDIR/App/* target_win32/python +mv \$_OUTDIR/Lib/site-packages/wx* target_win32/python/Lib/site-packages/ +mv PURELIB/serial target_win32/python/Lib +rm -rf \$_OUTDIR +rm -rf PURELIB + +#Extract pypy +7z x pypy-${PYPY_VERSION}-win32.zip -otarget_win32 +mv target_win32/pypy-${PYPY_VERSION} target_win32/pypy +7z x pypy-${PYPY_VERSION}-linux.bz2 -otarget_linux +mv target_linux/pypy-${PYPY_VERSION} target_linux/pypy +7z x pypy-${PYPY_VERSION}-osx64.bz2 -otarget_osx64 +mv target_linux/pypy-${PYPY_VERSION} target_osx64/pypy + +#add Skeinforge +cp -a SkeinPyPy target_win32/SkeinPyPy +cp -a SkeinPyPy target_linux/SkeinPyPy +cp -a SkeinPyPy target_osx64/SkeinPyPy + +#add printrun +cp -a Printrun target_win32/Printrun +cp -a Printrun target_linux/Printrun +cp -a Printrun target_osx64/Printrun + +#add windows batch files +echo "python\\python.exe SkeinPyPy\\skeinforge_application\\skeinforge.py" > target_win32/skeinforge.bat +echo "python\\python.exe printrun\\pronterface.py" > target_win32/printrun.bat + +#add readme file +cp README target_win32/README.txt +cp README target_linux/README.txt +cp README target_osx64/README.txt + +#package the result +cd target_win32 +7z a ../SkeinPyPy_Win32.zip * +cd .. +cd target_linux +7z a ../SkeinPyPy_Linux.zip * +cd .. +cd target_osx64 +7z a ../SkeinPyPy_MacOSX.zip * cd .. diff --git a/build_patches.sh b/build_patches.sh deleted file mode 100755 index 4057f16..0000000 --- a/build_patches.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -for NR in `ls patches`; do - if [ -d target/SF${NR} ]; then - diff -r -x*.pyc -N -u ori/${NR}/ target/SF${NR} | filterdiff --remove-timestamps > patches/${NR} - fi -done - diff --git a/patches/41 b/patches/41 deleted file mode 100644 index 1978fbf..0000000 --- a/patches/41 +++ /dev/null @@ -1,769 +0,0 @@ ---- ori/41/fabmetheus_utilities/archive.py -+++ target/SF41/fabmetheus_utilities/archive.py -@@ -18,7 +18,7 @@ - __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' - - --globalTemporarySettingsPath = os.path.join(os.path.expanduser('~'), '.skeinforge') -+globalTemporarySettingsPath = os.path.join(os.path.expanduser('~'), '.skeinforge_pypy') - - - def addToNamePathDictionary(directoryPath, namePathDictionary): ---- ori/41/fabmetheus_utilities/euclidean.py -+++ target/SF41/fabmetheus_utilities/euclidean.py -@@ -64,7 +64,7 @@ - - def addElementToPixelList( element, pixelDictionary, x, y ): - 'Add an element to the pixel list.' -- stepKey = getStepKey(x, y) -+ stepKey = (x, y) - addElementToListDictionary( element, stepKey, pixelDictionary ) - - def addElementToPixelListFromPoint( element, pixelDictionary, point ): -@@ -116,7 +116,7 @@ - - def addPixelToPixelTable( pixelDictionary, value, x, y ): - 'Add pixel to the pixel table.' -- pixelDictionary[getStepKey(x, y)] = value -+ pixelDictionary[(x, y)] = value - - def addPixelToPixelTableWithSteepness( isSteep, pixelDictionary, value, x, y ): - 'Add pixels to the pixel table with steepness.' -@@ -174,12 +174,20 @@ - xBegin = int(round(beginComplex.real)) - xEnd = int(round(endComplex.real)) - yIntersection = beginComplex.imag - beginComplex.real * gradient -- addPixelToPixelTableWithSteepness( isSteep, pixelDictionary, None, xBegin, int( round( beginComplex.imag ) ) ) -- addPixelToPixelTableWithSteepness( isSteep, pixelDictionary, None, xEnd, int( round( endComplex.imag ) ) ) -- for x in xrange( xBegin + 1, xEnd ): -- y = int( math.floor( yIntersection + x * gradient ) ) -- addPixelToPixelTableWithSteepness( isSteep, pixelDictionary, None, x, y ) -- addPixelToPixelTableWithSteepness( isSteep, pixelDictionary, None, x, y + 1 ) -+ if isSteep: -+ pixelDictionary[( int( round( beginComplex.imag ) ), xBegin)] = None -+ pixelDictionary[( int( round( endComplex.imag ) ), xEnd )] = None -+ for x in xrange( xBegin + 1, xEnd ): -+ y = int( math.floor( yIntersection + x * gradient ) ) -+ pixelDictionary[(y, x)] = None -+ pixelDictionary[(y + 1, x)] = None -+ else: -+ pixelDictionary[(xBegin, int( round( beginComplex.imag ) ) )] = None -+ pixelDictionary[(xEnd, int( round( endComplex.imag ) ) )] = None -+ for x in xrange( xBegin + 1, xEnd ): -+ y = int( math.floor( yIntersection + x * gradient ) ) -+ pixelDictionary[(x, y)] = None -+ pixelDictionary[(x, y + 1)] = None - - def addSquareTwoToPixelDictionary(pixelDictionary, point, value, width): - 'Add square with two pixels around the center to pixel dictionary.' -@@ -188,7 +196,7 @@ - y = int(round(point.imag)) - for xStep in xrange(x - 2, x + 3): - for yStep in xrange(y - 2, y + 3): -- pixelDictionary[getStepKey(xStep, yStep)] = value -+ pixelDictionary[(xStep, yStep)] = value - - def addSurroundingLoopBeginning( distanceFeedRate, loop, z ): - 'Add surrounding loop beginning to gcode output.' -@@ -244,12 +252,20 @@ - xBegin = int(round(beginComplex.real)) - xEnd = int(round(endComplex.real)) - yIntersection = beginComplex.imag - beginComplex.real * gradient -- addPixelToPixelTableWithSteepness( isSteep, pixelDictionary, value, xBegin, int( round( beginComplex.imag ) ) ) -- addPixelToPixelTableWithSteepness( isSteep, pixelDictionary, value, xEnd, int( round( endComplex.imag ) ) ) -- for x in xrange( xBegin + 1, xEnd ): -- y = int( math.floor( yIntersection + x * gradient ) ) -- addPixelToPixelTableWithSteepness( isSteep, pixelDictionary, value, x, y ) -- addPixelToPixelTableWithSteepness( isSteep, pixelDictionary, value, x, y + 1 ) -+ if isSteep: -+ pixelDictionary[(int( round( beginComplex.imag ) ), xBegin)] = value -+ pixelDictionary[(int( round( endComplex.imag ) ), xEnd)] = value -+ for x in xrange( xBegin + 1, xEnd ): -+ y = int( math.floor( yIntersection + x * gradient ) ) -+ pixelDictionary[(y, x)] = value -+ pixelDictionary[(y + 1, x)] = value -+ else: -+ pixelDictionary[(xBegin, int( round( beginComplex.imag ) ))] = value -+ pixelDictionary[(xEnd, int( round( endComplex.imag ) ))] = value -+ for x in xrange( xBegin + 1, xEnd ): -+ y = int( math.floor( yIntersection + x * gradient ) ) -+ pixelDictionary[(x, y)] = value -+ pixelDictionary[(x, y + 1)] = value - - def addValueToOutput(depth, keyInput, output, value): - 'Add value to the output.' -@@ -468,8 +484,7 @@ - y = int(point.imag * oneOverOverlapDistance) - if not getSquareIsOccupied(pixelDictionary, x, y): - away.append(point) -- stepKey = getStepKey(x, y) -- pixelDictionary[stepKey] = None -+ pixelDictionary[(x, y)] = None - return away - - def getBackOfLoops(loops): -@@ -1592,7 +1607,7 @@ - squareValues = [] - for xStep in xrange(x - 1, x + 2): - for yStep in xrange(y - 1, y + 2): -- stepKey = getStepKey(xStep, yStep) -+ stepKey = (xStep, yStep) - if stepKey in pixelDictionary: - return True - return False -@@ -1608,7 +1623,7 @@ - squareValues = [] - for xStep in xrange(x - 1, x + 2): - for yStep in xrange(y - 1, y + 2): -- stepKey = getStepKey(xStep, yStep) -+ stepKey = (xStep, yStep) - if stepKey in pixelDictionary: - squareValues += pixelDictionary[ stepKey ] - return squareValues ---- ori/41/fabmetheus_utilities/hidden_scrollbar.py -+++ target/SF41/fabmetheus_utilities/hidden_scrollbar.py -@@ -8,6 +8,17 @@ - import __init__ - try: - import Tkinter -+ class HiddenScrollbar(Tkinter.Scrollbar): -+ 'A class to hide the scrollbar if it is not needed.' -+ def set(self, lo, hi): -+ 'Add to grid is needed, remove if not.' -+ if float(lo) <= 0.0 and float(hi) >= 1.0: -+ self.grid_remove() -+ self.visible = False -+ else: -+ self.grid() -+ self.visible = True -+ Tkinter.Scrollbar.set(self, lo, hi) - except: - pass - -@@ -17,15 +28,3 @@ - __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' - - --class HiddenScrollbar(Tkinter.Scrollbar): -- 'A class to hide the scrollbar if it is not needed.' -- def set(self, lo, hi): -- 'Add to grid is needed, remove if not.' -- if float(lo) <= 0.0 and float(hi) >= 1.0: -- self.grid_remove() -- self.visible = False -- else: -- self.grid() -- self.visible = True -- Tkinter.Scrollbar.set(self, lo, hi) -- ---- ori/41/fabmetheus_utilities/settings.py -+++ target/SF41/fabmetheus_utilities/settings.py -@@ -271,7 +271,7 @@ - if repository.baseNameSynonym != None: - text = archive.getFileText(archive.getProfilesPath(getProfileBaseNameSynonym(repository)), False) - if text == '': -- print('The default %s will be written in the .skeinforge folder in the home directory.' % repository.title.lower() ) -+ print('The default %s will be written in the .skeinforge_pypy folder in the home directory.' % repository.title.lower() ) - text = archive.getFileText(getProfilesDirectoryInAboveDirectory(getProfileBaseName(repository)), False) - if text != '': - readSettingsFromText(repository, text) ---- ori/41/skeinforge_application/alterations/end.gcode -+++ target/SF41/skeinforge_application/alterations/end.gcode -@@ -0,0 +1,10 @@ -+(start of end.gcode) -+M104 S0 (extruder heat off) -+M106 (fan on) -+G91 (relative positioning) -+G1 Z+10 E-5 F400 (move Z up a bit and retract filament by 5mm) -+G1 X-20 Y-20 F1500 (move X and Y over a bit) -+M84 (steppers off) -+G90 (absolute positioning) -+(end of end.gcode) -+ ---- ori/41/skeinforge_application/alterations/start.gcode -+++ target/SF41/skeinforge_application/alterations/start.gcode -@@ -0,0 +1,31 @@ -+(start of start.txt) -+M92 E926.5 (the number of extruder steps to take in 1mm of filament) -+G21 (metric values) -+G21 -+G21 (all the extra G21 commands are comments - skeinforge eats lines without a gcode) -+G21 -+G90 (absolute positioning) -+G21 -+G28 X0 Y0 (move X/Y to min endstops) -+G28 Z0 (move Z to min endstops) -+G21 -+G21 ( if your prints start too high, try changing the Z0.0 below ) -+G21 ( to Z1.0 - the number after the Z is the actual, physical ) -+G21 ( height of the nozzle in mm. This can take some messing around ) -+G21 ( with to get just right... ) -+G21 -+G92 X0 Y0 Z0 E0 (reset software position to front/left/z=0.0) -+G21 -+G1 Z15.0 F400 (move the platform down 15mm) -+G92 E0 (zero the extruded length) -+G21 -+G1 F75 E5 (extrude 5mm of feed stock) -+G1 F75 E3.5 (reverse feed stock by 1.5mm) -+G92 E0 (zero the extruded length again) -+G21 -+M1 (Clean the nozzle then press YES to continue...) -+G21 -+G1 X100 Y100 F3500 (go to the middle of the platform) -+G1 Z0.0 F400 (back to Z=0 and start the print!) -+(end of start.txt) -+ ---- ori/41/skeinforge_application/skeinforge_plugins/analyze_plugins/skeiniso.py -+++ target/SF41/skeinforge_application/skeinforge_plugins/analyze_plugins/skeiniso.py -@@ -297,7 +297,7 @@ - self.baseNameSynonym = 'behold.csv' - self.fileNameInput = settings.FileNameInput().getFromFileName( [ ('Gcode text files', '*.gcode') ], 'Open File for Skeiniso', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Skeiniso') -- self.activateSkeiniso = settings.BooleanSetting().getFromValue('Activate Skeiniso', self, True ) -+ self.activateSkeiniso = settings.BooleanSetting().getFromValue('Activate Skeiniso', self, False ) - self.addAnimation() - self.axisRulings = settings.BooleanSetting().getFromValue('Axis Rulings', self, True ) - settings.LabelSeparator().getFromRepository(self) ---- ori/41/skeinforge_application/skeinforge_plugins/analyze_plugins/skeinlayer.py -+++ target/SF41/skeinforge_application/skeinforge_plugins/analyze_plugins/skeinlayer.py -@@ -195,7 +195,7 @@ - self.baseNameSynonym = 'skeinview.csv' - self.fileNameInput = settings.FileNameInput().getFromFileName( [ ('Gcode text files', '*.gcode') ], 'Open File for Skeinlayer', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Skeinlayer') -- self.activateSkeinlayer = settings.BooleanSetting().getFromValue('Activate Skeinlayer', self, True ) -+ self.activateSkeinlayer = settings.BooleanSetting().getFromValue('Activate Skeinlayer', self, False ) - self.addAnimation() - self.drawArrows = settings.BooleanSetting().getFromValue('Draw Arrows', self, True ) - self.goAroundExtruderOffTravel = settings.BooleanSetting().getFromValue('Go Around Extruder Off Travel', self, False ) ---- ori/41/skeinforge_application/skeinforge_plugins/craft_plugins/carve.py -+++ target/SF41/skeinforge_application/skeinforge_plugins/craft_plugins/carve.py -@@ -157,7 +157,7 @@ - self.extraDecimalPlaces = settings.FloatSpin().getFromValue(0.0, 'Extra Decimal Places (float):', self, 3.0, 2.0) - self.importCoarseness = settings.FloatSpin().getFromValue( 0.5, 'Import Coarseness (ratio):', self, 2.0, 1.0 ) - self.infillInDirectionOfBridge = settings.BooleanSetting().getFromValue('Infill in Direction of Bridge', self, True ) -- self.layerThickness = settings.FloatSpin().getFromValue( 0.1, 'Layer Thickness (mm):', self, 1.0, 0.4 ) -+ self.layerThickness = settings.FloatSpin().getFromValue( 0.1, 'Layer Thickness (mm):', self, 1.0, 0.2 ) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Layers -', self ) - self.layersFrom = settings.IntSpin().getFromValue( 0, 'Layers From (index):', self, 20, 0 ) -@@ -167,7 +167,7 @@ - importLatentStringVar = settings.LatentStringVar() - self.correctMesh = settings.Radio().getFromRadio( importLatentStringVar, 'Correct Mesh', self, True ) - self.unprovenMesh = settings.Radio().getFromRadio( importLatentStringVar, 'Unproven Mesh', self, False ) -- self.perimeterWidthOverThickness = settings.FloatSpin().getFromValue( 1.4, 'Perimeter Width over Thickness (ratio):', self, 2.2, 1.8 ) -+ self.perimeterWidth = settings.FloatSpin().getFromValue( 0.1, 'Perimeter Width:', self, 2.2, 0.4 ) - self.svgViewer = settings.StringSetting().getFromValue('SVG Viewer:', self, 'webbrowser') - settings.LabelSeparator().getFromRepository(self) - self.executeTitle = 'Carve' -@@ -184,7 +184,7 @@ - def getCarvedSVG(self, carving, fileName, repository): - "Parse gnu triangulated surface text and store the carved gcode." - layerThickness = repository.layerThickness.value -- perimeterWidth = repository.perimeterWidthOverThickness.value * layerThickness -+ perimeterWidth = repository.perimeterWidth.value - carving.setCarveInfillInDirectionOfBridge(repository.infillInDirectionOfBridge.value) - carving.setCarveLayerThickness(layerThickness) - importRadius = 0.5 * repository.importCoarseness.value * abs(perimeterWidth) -@@ -196,7 +196,7 @@ - return '' - layerThickness = carving.getCarveLayerThickness() - decimalPlacesCarried = euclidean.getDecimalPlacesCarried(repository.extraDecimalPlaces.value, layerThickness) -- perimeterWidth = repository.perimeterWidthOverThickness.value * layerThickness -+ perimeterWidth = repository.perimeterWidth.value - svgWriter = svg_writer.SVGWriter( - repository.addLayerTemplateToSVG.value, - carving.getCarveCornerMaximum(), ---- ori/41/skeinforge_application/skeinforge_plugins/craft_plugins/chamber.py -+++ target/SF41/skeinforge_application/skeinforge_plugins/craft_plugins/chamber.py -@@ -166,7 +166,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.chamber.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Chamber', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Chamber') -- self.activateChamber = settings.BooleanSetting().getFromValue('Activate Chamber:', self, True ) -+ self.activateChamber = settings.BooleanSetting().getFromValue('Activate Chamber:', self, False ) - self.bedTemperature = settings.FloatSpin().getFromValue( 20.0, 'Bed Temperature (Celcius):', self, 90.0, 60.0 ) - self.chamberTemperature = settings.FloatSpin().getFromValue( 20.0, 'Chamber Temperature (Celcius):', self, 90.0, 30.0 ) - self.holdingForce = settings.FloatSpin().getFromValue( 0.0, 'Holding Force (bar):', self, 100.0, 0.0 ) ---- ori/41/skeinforge_application/skeinforge_plugins/craft_plugins/clip.py -+++ target/SF41/skeinforge_application/skeinforge_plugins/craft_plugins/clip.py -@@ -86,7 +86,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.clip.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Clip', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Clip') -- self.activateClip = settings.BooleanSetting().getFromValue('Activate Clip', self, True ) -+ self.activateClip = settings.BooleanSetting().getFromValue('Activate Clip', self, False ) - self.clipOverPerimeterWidth = settings.FloatSpin().getFromValue( 0.1, 'Clip Over Perimeter Width (ratio):', self, 0.8, 0.5 ) - self.maximumConnectionDistanceOverPerimeterWidth = settings.FloatSpin().getFromValue( 1.0, 'Maximum Connection Distance Over Perimeter Width (ratio):', self, 20.0, 10.0 ) - self.executeTitle = 'Clip' ---- ori/41/skeinforge_application/skeinforge_plugins/craft_plugins/comb.py -+++ target/SF41/skeinforge_application/skeinforge_plugins/craft_plugins/comb.py -@@ -126,7 +126,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.comb.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Comb', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Comb') -- self.activateComb = settings.BooleanSetting().getFromValue('Activate Comb', self, False ) -+ self.activateComb = settings.BooleanSetting().getFromValue('Activate Comb', self, True ) - self.executeTitle = 'Comb' - - def execute(self): ---- ori/41/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py -+++ target/SF41/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py -@@ -142,7 +142,7 @@ - self.orbit = settings.MenuRadio().getFromMenuButtonDisplay(self.coolType, 'Orbit', self, False) - self.slowDown = settings.MenuRadio().getFromMenuButtonDisplay(self.coolType, 'Slow Down', self, True) - self.maximumCool = settings.FloatSpin().getFromValue(0.0, 'Maximum Cool (Celcius):', self, 10.0, 2.0) -- self.minimumLayerTime = settings.FloatSpin().getFromValue(0.0, 'Minimum Layer Time (seconds):', self, 120.0, 60.0) -+ self.minimumLayerTime = settings.FloatSpin().getFromValue(0.0, 'Minimum Layer Time (seconds):', self, 120.0, 10.0) - self.minimumOrbitalRadius = settings.FloatSpin().getFromValue( - 0.0, 'Minimum Orbital Radius (millimeters):', self, 20.0, 10.0) - settings.LabelSeparator().getFromRepository(self) ---- ori/41/skeinforge_application/skeinforge_plugins/craft_plugins/dimension.py -+++ target/SF41/skeinforge_application/skeinforge_plugins/craft_plugins/dimension.py -@@ -124,7 +124,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.dimension.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Dimension', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Dimension') -- self.activateDimension = settings.BooleanSetting().getFromValue('Activate Dimension', self, False ) -+ self.activateDimension = settings.BooleanSetting().getFromValue('Activate Dimension', self, True ) - extrusionDistanceFormatLatentStringVar = settings.LatentStringVar() - self.extrusionDistanceFormatChoiceLabel = settings.LabelDisplay().getFromName('Extrusion Distance Format Choice: ', self ) - settings.Radio().getFromRadio( extrusionDistanceFormatLatentStringVar, 'Absolute Extrusion Distance', self, True ) -@@ -132,7 +132,7 @@ - self.extruderRetractionSpeed = settings.FloatSpin().getFromValue( 4.0, 'Extruder Retraction Speed (mm/s):', self, 34.0, 13.3 ) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Filament -', self ) -- self.filamentDiameter = settings.FloatSpin().getFromValue(1.0, 'Filament Diameter (mm):', self, 6.0, 2.8) -+ self.filamentDiameter = settings.FloatSpin().getFromValue(1.0, 'Filament Diameter (mm):', self, 6.0, 2.89) - self.filamentPackingDensity = settings.FloatSpin().getFromValue(0.7, 'Filament Packing Density (ratio):', self, 1.0, 0.85) - settings.LabelSeparator().getFromRepository(self) - self.retractionDistance = settings.FloatSpin().getFromValue( 0.0, 'Retraction Distance (millimeters):', self, 100.0, 0.0 ) -@@ -160,9 +160,11 @@ - - def addLinearMoveExtrusionDistanceLine( self, extrusionDistance ): - "Get the extrusion distance string from the extrusion distance." -- self.distanceFeedRate.output.write('G1 F%s\n' % self.extruderRetractionSpeedMinuteString ) -- self.distanceFeedRate.output.write('G1%s\n' % self.getExtrusionDistanceStringFromExtrusionDistance( extrusionDistance ) ) -- self.distanceFeedRate.output.write('G1 F%s\n' % self.distanceFeedRate.getRounded( self.feedRateMinute ) ) -+ -+ if self.repository.retractionDistance.value != 0.0: -+ self.distanceFeedRate.output.write('G1 F%s\n' % self.extruderRetractionSpeedMinuteString ) -+ self.distanceFeedRate.output.write('G1%s\n' % self.getExtrusionDistanceStringFromExtrusionDistance( extrusionDistance ) ) -+ self.distanceFeedRate.output.write('G1 F%s\n' % self.distanceFeedRate.getRounded( self.feedRateMinute ) ) - - def getCraftedGcode(self, gcodeText, repository): - "Parse gcode text and store the dimension gcode." -@@ -262,9 +264,12 @@ - self.absoluteDistanceMode = False - elif firstWord == 'M101': - self.addLinearMoveExtrusionDistanceLine( self.restartDistance ) -- if not self.repository.relativeExtrusionDistance.value: -- self.distanceFeedRate.addLine('G92 E0') -- self.totalExtrusionDistance = 0.0 -+ -+ if self.totalExtrusionDistance > 999999.0: -+ if not self.repository.relativeExtrusionDistance.value: -+ self.distanceFeedRate.addLine('G92 E0') -+ self.totalExtrusionDistance = 0.0 -+ - self.isExtruderActive = True - elif firstWord == 'M103': - self.addLinearMoveExtrusionDistanceLine( - self.repository.retractionDistance.value ) ---- ori/41/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/static_plugins/gcode_tiny.py -+++ target/SF41/skeinforge_application/skeinforge_plugins/craft_plugins/export_plugins/static_plugins/gcode_tiny.py -@@ -0,0 +1,117 @@ -+""" -+This page is in the table of contents. -+Gcode_tiny is an export plugin to remove the comments and the redundant z and feed rate parameters from a gcode file. -+ -+An export plugin is a script in the export_plugins folder which has the getOutput function, the globalIsReplaceable variable and if it's output is not replaceable, the writeOutput function. It is meant to be run from the export tool. To ensure that the plugin works on platforms which do not handle file capitalization properly, give the plugin a lower case name. -+ -+The getOutput function of this script takes a gcode text and returns that text without comments and redundant z and feed rate parameters. The writeOutput function of this script takes a gcode text and writes that text without comments and redundant z and feed rate parameters to a file. -+ -+Many of the functions in this script are copied from gcodec in skeinforge_utilities. They are copied rather than imported so developers making new plugins do not have to learn about gcodec, the code here is all they need to learn. -+ -+""" -+ -+from __future__ import absolute_import -+import cStringIO -+import os -+ -+ -+__author__ = 'Enrique Perez (perez_enrique@yahoo.com)' -+__date__ = '$Date: 2008/21/04 $' -+__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' -+ -+ -+# This is true if the output is text and false if it is binary." -+globalIsReplaceable = True -+ -+ -+def getOutput(gcodeText): -+ 'Get the exported version of a gcode file.' -+ return GcodeTinySkein().getCraftedGcode(gcodeText) -+ -+def getSplitLineBeforeBracketSemicolon(line): -+ "Get the split line before a bracket or semicolon." -+ bracketSemicolonIndex = min( line.find(';'), line.find('(') ) -+ if bracketSemicolonIndex < 0: -+ return line.split() -+ return line[ : bracketSemicolonIndex ].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 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 getTextLines(text): -+ "Get the all the lines of text of a text." -+ return text.replace('\r', '\n').split('\n') -+ -+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 -+ -+ -+class GcodeTinySkein: -+ "A class to remove redundant z and feed rate parameters from a skein of extrusions." -+ "Also remove spaces and minimize the exported numbers, this will create GCode which not every tool will understand." -+ def __init__(self): -+ self.lastFeedRateString = None -+ self.lastZString = None -+ self.output = cStringIO.StringIO() -+ -+ def getCraftedGcode( self, gcodeText ): -+ "Parse gcode text and store the gcode." -+ lines = getTextLines(gcodeText) -+ for line in lines: -+ self.parseLine(line) -+ return self.output.getvalue() -+ -+ def fixStringNumber(self, s): -+ if s == None: -+ return None -+ return str(float(s)) -+ -+ def parseLine(self, line): -+ "Parse a gcode line." -+ splitLine = getSplitLineBeforeBracketSemicolon(line) -+ if len(splitLine) < 1: -+ return -+ firstWord = splitLine[0] -+ if len(firstWord) < 1: -+ return -+ if firstWord[0] == '(': -+ return -+ if firstWord != 'G1': -+ self.output.write(line + '\n') -+ return -+ eString = self.fixStringNumber(getStringFromCharacterSplitLine('E', splitLine )) -+ xString = self.fixStringNumber(getStringFromCharacterSplitLine('X', splitLine )) -+ yString = self.fixStringNumber(getStringFromCharacterSplitLine('Y', splitLine )) -+ zString = self.fixStringNumber(getStringFromCharacterSplitLine('Z', splitLine )) -+ feedRateString = self.fixStringNumber(getStringFromCharacterSplitLine('F', splitLine )) -+ self.output.write('G1') -+ if xString != None: -+ self.output.write('X' + xString ) -+ if yString != None: -+ self.output.write('Y' + yString ) -+ if zString != None and zString != self.lastZString: -+ self.output.write('Z' + zString ) -+ if feedRateString != None and feedRateString != self.lastFeedRateString: -+ self.output.write('F' + feedRateString ) -+ if eString != None: -+ self.output.write('E' + eString ) -+ self.lastFeedRateString = feedRateString -+ self.lastZString = zString -+ self.output.write('\n') -+ ---- ori/41/skeinforge_application/skeinforge_plugins/craft_plugins/export.py -+++ target/SF41/skeinforge_application/skeinforge_plugins/craft_plugins/export.py -@@ -214,17 +214,20 @@ - self.exportLabel = settings.LabelDisplay().getFromName('Export Operations: ', self) - self.exportPlugins = [] - exportLatentStringVar = settings.LatentStringVar() -- self.doNotChangeOutput = settings.RadioCapitalized().getFromRadio(exportLatentStringVar, 'Do Not Change Output', self, True) -+ self.doNotChangeOutput = settings.RadioCapitalized().getFromRadio(exportLatentStringVar, 'Do Not Change Output', self, False) - self.doNotChangeOutput.directoryPath = None - allExportPluginFileNames = exportPluginFileNames + exportStaticPluginFileNames - for exportPluginFileName in allExportPluginFileNames: - exportPlugin = None -+ default = False -+ if exportPluginFileName == "gcode_small": -+ default = True - if exportPluginFileName in exportPluginFileNames: - path = os.path.join(exportPluginsFolderPath, exportPluginFileName) -- exportPlugin = settings.RadioCapitalizedButton().getFromPath(exportLatentStringVar, exportPluginFileName, path, self, False) -+ exportPlugin = settings.RadioCapitalizedButton().getFromPath(exportLatentStringVar, exportPluginFileName, path, self, default) - exportPlugin.directoryPath = exportPluginsFolderPath - else: -- exportPlugin = settings.RadioCapitalized().getFromRadio(exportLatentStringVar, exportPluginFileName, self, False) -+ exportPlugin = settings.RadioCapitalized().getFromRadio(exportLatentStringVar, exportPluginFileName, self, default) - exportPlugin.directoryPath = exportStaticDirectoryPath - self.exportPlugins.append(exportPlugin) - self.fileExtension = settings.StringSetting().getFromValue('File Extension:', self, 'gcode') ---- ori/41/skeinforge_application/skeinforge_plugins/craft_plugins/fill.py -+++ target/SF41/skeinforge_application/skeinforge_plugins/craft_plugins/fill.py -@@ -808,7 +808,7 @@ - self.infillPatternLine = settings.Radio().getFromRadio( infillLatentStringVar, 'Line', self, True ) - self.infillPerimeterOverlap = settings.FloatSpin().getFromValue( 0.0, 'Infill Perimeter Overlap (ratio):', self, 0.4, 0.15 ) - self.infillSolidity = settings.FloatSpin().getFromValue( 0.04, 'Infill Solidity (ratio):', self, 0.3, 0.2 ) -- self.infillWidthOverThickness = settings.FloatSpin().getFromValue( 1.3, 'Infill Width over Thickness (ratio):', self, 1.7, 1.5 ) -+ self.infillWidth = settings.FloatSpin().getFromValue( 0.1, 'Infill Width:', self, 1.7, 0.4 ) - settings.LabelSeparator().getFromRepository(self) - self.solidSurfaceThickness = settings.IntSpin().getFromValue( 0, 'Solid Surface Thickness (layers):', self, 5, 3 ) - self.startFromChoice = settings.MenuButtonDisplay().getFromName('Start From Choice:', self) -@@ -1273,7 +1273,7 @@ - self.bridgeWidthMultiplier = float(splitLine[1]) - elif firstWord == '(': - self.layerThickness = float(splitLine[1]) -- self.infillWidth = self.repository.infillWidthOverThickness.value * self.layerThickness -+ self.infillWidth = self.repository.infillWidth.value - self.distanceFeedRate.addTagRoundedLine('infillWidth', self.infillWidth) - self.distanceFeedRate.addLine(line) - ---- ori/41/skeinforge_application/skeinforge_plugins/craft_plugins/home.py -+++ target/SF41/skeinforge_application/skeinforge_plugins/craft_plugins/home.py -@@ -82,7 +82,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.home.html', self) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Home', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_home') -- self.activateHome = settings.BooleanSetting().getFromValue('Activate Home', self, True ) -+ self.activateHome = settings.BooleanSetting().getFromValue('Activate Home', self, False ) - self.nameOfHomingFile = settings.StringSetting().getFromValue('Name of Homing File:', self, 'homing.gcode') - self.executeTitle = 'Home' - ---- ori/41/skeinforge_application/skeinforge_plugins/craft_plugins/jitter.py -+++ target/SF41/skeinforge_application/skeinforge_plugins/craft_plugins/jitter.py -@@ -113,7 +113,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.jitter.html', self) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Jitter', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Jitter') -- self.activateJitter = settings.BooleanSetting().getFromValue('Activate Jitter', self, True) -+ self.activateJitter = settings.BooleanSetting().getFromValue('Activate Jitter', self, False) - self.jitterOverPerimeterWidth = settings.FloatSpin().getFromValue(1.0, 'Jitter Over Perimeter Width (ratio):', self, 3.0, 2.0) - self.executeTitle = 'Jitter' - ---- ori/41/skeinforge_application/skeinforge_plugins/craft_plugins/limit.py -+++ target/SF41/skeinforge_application/skeinforge_plugins/craft_plugins/limit.py -@@ -88,7 +88,7 @@ - 'Set the default settings, execute title & settings fileName.' - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.limit.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Limit', self, '') -- self.activateLimit = settings.BooleanSetting().getFromValue('Activate Limit', self, True) -+ self.activateLimit = settings.BooleanSetting().getFromValue('Activate Limit', self, False) - self.maximumInitialFeedRate = settings.FloatSpin().getFromValue(0.5, 'Maximum Initial Feed Rate (mm/s):', self, 10.0, 1.0) - self.maximumZFeedRatePerSecond = settings.FloatSpin().getFromValue(0.5, 'Maximum Z Feed Rate (mm/s):', self, 10.0, 1.0) - self.executeTitle = 'Limit' ---- ori/41/skeinforge_application/skeinforge_plugins/craft_plugins/multiply.py -+++ target/SF41/skeinforge_application/skeinforge_plugins/craft_plugins/multiply.py -@@ -105,11 +105,11 @@ - self.fileNameInput = settings.FileNameInput().getFromFileName( - fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Multiply', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Multiply') -- self.activateMultiply = settings.BooleanSetting().getFromValue('Activate Multiply:', self, False ) -+ self.activateMultiply = settings.BooleanSetting().getFromValue('Activate Multiply:', self, True ) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Center -', self ) -- self.centerX = settings.FloatSpin().getFromValue(-100.0, 'Center X (mm):', self, 100.0, 0.0) -- self.centerY = settings.FloatSpin().getFromValue(-100.0, 'Center Y (mm):', self, 100.0, 0.0) -+ self.centerX = settings.FloatSpin().getFromValue(-100.0, 'Center X (mm):', self, 100.0, 105.0) -+ self.centerY = settings.FloatSpin().getFromValue(-100.0, 'Center Y (mm):', self, 100.0, 105.0) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Number of Cells -', self) - self.numberOfColumns = settings.IntSpin().getFromValue(1, 'Number of Columns (integer):', self, 10, 1) ---- ori/41/skeinforge_application/skeinforge_plugins/craft_plugins/raft.py -+++ target/SF41/skeinforge_application/skeinforge_plugins/craft_plugins/raft.py -@@ -358,7 +358,7 @@ - self.baseInfillDensity = settings.FloatSpin().getFromValue(0.3, 'Base Infill Density (ratio):', self, 0.9, 0.5) - self.baseLayerThicknessOverLayerThickness = settings.FloatSpin().getFromValue( - 1.0, 'Base Layer Thickness over Layer Thickness:', self, 3.0, 2.0) -- self.baseLayers = settings.IntSpin().getFromValue(0, 'Base Layers (integer):', self, 3, 1) -+ self.baseLayers = settings.IntSpin().getFromValue(0, 'Base Layers (integer):', self, 3, 0) - self.baseNozzleLiftOverBaseLayerThickness = settings.FloatSpin().getFromValue( - 0.2, 'Base Nozzle Lift over Base Layer Thickness (ratio):', self, 0.8, 0.4) - settings.LabelSeparator().getFromRepository(self) -@@ -376,7 +376,7 @@ - self.interfaceLayerThicknessOverLayerThickness = settings.FloatSpin().getFromValue( - 1.0, 'Interface Layer Thickness over Layer Thickness:', self, 3.0, 1.0) - self.interfaceLayers = settings.IntSpin().getFromValue( -- 0, 'Interface Layers (integer):', self, 3, 2) -+ 0, 'Interface Layers (integer):', self, 3, 0) - self.interfaceNozzleLiftOverInterfaceLayerThickness = settings.FloatSpin().getFromValue( - 0.25, 'Interface Nozzle Lift over Interface Layer Thickness (ratio):', self, 0.85, 0.45) - settings.LabelSeparator().getFromRepository(self) -@@ -385,7 +385,7 @@ - self.nameOfSupportStartFile = settings.StringSetting().getFromValue( - 'Name of Support Start File:', self, 'support_start.gcode') - settings.LabelSeparator().getFromRepository(self) -- settings.LabelDisplay().getFromName('- Object First Layer -', self) -+ settings.LabelDisplay().getFromName('- Object First Layers -', self) - self.objectFirstLayerFeedRateInfillMultiplier = settings.FloatSpin().getFromValue( - 0.2, 'Object First Layer Feed Rate Infill Multiplier (ratio):', self, 1.0, 0.4) - self.objectFirstLayerFeedRatePerimeterMultiplier = settings.FloatSpin().getFromValue( -@@ -394,6 +394,8 @@ - 0.2, 'Object First Layer Flow Rate Infill Multiplier (ratio):', self, 1.0, 0.4) - self.objectFirstLayerFlowRatePerimeterMultiplier = settings.FloatSpin().getFromValue( - 0.2, 'Object First Layer Flow Rate Perimeter Multiplier (ratio):', self, 1.0, 0.4) -+ self.objectFirstLayersLayerAmount = settings.IntSpin().getFromValue( -+ 1, 'Object First Layers Amount Of Layers For Speed Change:', self, 10, 3) - settings.LabelSeparator().getFromRepository(self) - self.operatingNozzleLiftOverLayerThickness = settings.FloatSpin().getFromValue( - 0.3, 'Operating Nozzle Lift over Layer Thickness (ratio):', self, 0.7, 0.5) -@@ -744,9 +746,9 @@ - paths = euclidean.getPathsFromEndpoints(endpoints, 1.5 * self.interfaceStep, aroundPixelTable, aroundWidth) - feedRateMinuteMultiplied = self.feedRateMinute - supportFlowRateMultiplied = self.supportFlowRate -- if self.layerIndex == 0: -- feedRateMinuteMultiplied *= self.repository.objectFirstLayerFeedRateInfillMultiplier.value -- supportFlowRateMultiplied *= self.repository.objectFirstLayerFlowRateInfillMultiplier.value -+ if self.layerIndex < self.repository.objectFirstLayersLayerAmount.value: -+ feedRateMinuteMultiplied *= ((self.repository.objectFirstLayerFeedRateInfillMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value -+ supportFlowRateMultiplied *= ((self.repository.objectFirstLayerFlowRatePerimeterMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value - self.addFlowRateValueIfDifferent(supportFlowRateMultiplied) - for path in paths: - self.distanceFeedRate.addGcodeFromFeedRateThreadZ(feedRateMinuteMultiplied, path, self.travelFeedRateMinute, z) -@@ -874,14 +876,17 @@ - z += self.operatingJump - flowRate = self.oldFlowRateInput - temperature = self.objectNextLayersTemperature -+ if self.layerIndex < self.repository.objectFirstLayersLayerAmount.value: -+ if self.isPerimeterPath: -+ feedRateMinuteMultiplied *= ((self.repository.objectFirstLayerFeedRatePerimeterMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value -+ flowRate *= ((self.repository.objectFirstLayerFlowRatePerimeterMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value -+ else: -+ feedRateMinuteMultiplied *= ((self.repository.objectFirstLayerFeedRateInfillMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value -+ flowRate *= ((self.repository.objectFirstLayerFlowRateInfillMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value - if self.layerIndex == 0: - if self.isPerimeterPath: -- feedRateMinuteMultiplied *= self.repository.objectFirstLayerFeedRatePerimeterMultiplier.value -- flowRate *= self.repository.objectFirstLayerFlowRatePerimeterMultiplier.value - temperature = self.objectFirstLayerPerimeterTemperature - else: -- feedRateMinuteMultiplied *= self.repository.objectFirstLayerFeedRateInfillMultiplier.value -- flowRate *= self.repository.objectFirstLayerFlowRateInfillMultiplier.value - temperature = self.objectFirstLayerInfillTemperature - self.addFlowRateValueIfDifferent(flowRate) - self.addTemperatureLineIfDifferent(temperature) ---- ori/41/skeinforge_application/skeinforge_plugins/craft_plugins/speed.py -+++ target/SF41/skeinforge_application/skeinforge_plugins/craft_plugins/speed.py -@@ -148,15 +148,15 @@ - self.dutyCycleAtBeginning = settings.FloatSpin().getFromValue( 0.0, 'Duty Cyle at Beginning (portion):', self, 1.0, 1.0 ) - self.dutyCycleAtEnding = settings.FloatSpin().getFromValue( 0.0, 'Duty Cyle at Ending (portion):', self, 1.0, 0.0 ) - settings.LabelSeparator().getFromRepository(self) -- self.feedRatePerSecond = settings.FloatSpin().getFromValue( 2.0, 'Feed Rate (mm/s):', self, 50.0, 16.0 ) -- self.flowRateSetting = settings.FloatSpin().getFromValue( 50.0, 'Flow Rate Setting (float):', self, 250.0, 210.0 ) -+ self.feedRatePerSecond = settings.FloatSpin().getFromValue( 2.0, 'Feed Rate (mm/s):', self, 250.0, 50.0 ) -+ self.flowRateSetting = settings.FloatSpin().getFromValue( 50.0, 'Flow Rate Setting (float):', self, 250.0, 50.0 ) - self.orbitalFeedRateOverOperatingFeedRate = settings.FloatSpin().getFromValue( 0.1, 'Orbital Feed Rate over Operating Feed Rate (ratio):', self, 0.9, 0.5 ) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Perimeter -', self ) -- self.perimeterFeedRateOverOperatingFeedRate = settings.FloatSpin().getFromValue( 0.5, 'Perimeter Feed Rate over Operating Feed Rate (ratio):', self, 1.0, 1.0 ) -- self.perimeterFlowRateOverOperatingFlowRate = settings.FloatSpin().getFromValue( 0.5, 'Perimeter Flow Rate over Operating Flow Rate (ratio):', self, 1.0, 1.0 ) -+ self.perimeterFeedRateOverOperatingFeedRate = settings.FloatSpin().getFromValue( 0.5, 'Perimeter Feed Rate over Operating Feed Rate (ratio):', self, 1.0, 0.5 ) -+ self.perimeterFlowRateOverOperatingFlowRate = settings.FloatSpin().getFromValue( 0.5, 'Perimeter Flow Rate over Operating Flow Rate (ratio):', self, 1.0, 0.5 ) - settings.LabelSeparator().getFromRepository(self) -- self.travelFeedRatePerSecond = settings.FloatSpin().getFromValue( 2.0, 'Travel Feed Rate (mm/s):', self, 50.0, 16.0 ) -+ self.travelFeedRatePerSecond = settings.FloatSpin().getFromValue( 2.0, 'Travel Feed Rate (mm/s):', self, 350.0, 250.0 ) - self.executeTitle = 'Speed' - - def execute(self): ---- ori/41/skeinforge_application/skeinforge_plugins/craft_plugins/temperature.py -+++ target/SF41/skeinforge_application/skeinforge_plugins/craft_plugins/temperature.py -@@ -122,7 +122,7 @@ - "Set the default settings, execute title & settings fileName." - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.temperature.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Temperature', self, '') -- self.activateTemperature = settings.BooleanSetting().getFromValue('Activate Temperature:', self, True ) -+ self.activateTemperature = settings.BooleanSetting().getFromValue('Activate Temperature:', self, False ) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Rate -', self ) - self.coolingRate = settings.FloatSpin().getFromValue( 1.0, 'Cooling Rate (Celcius/second):', self, 20.0, 3.0 ) ---- ori/41/skeinforge_application/skeinforge.py -+++ target/SF41/skeinforge_application/skeinforge.py -@@ -228,6 +228,8 @@ - from skeinforge_application.skeinforge_utilities import skeinforge_profile - import os - import sys -+import platform -+import subprocess - - - # infill or inset bug -@@ -545,7 +547,6 @@ - repository = getNewRepository() - repository.fileNameInput.value = fileName - repository.execute() -- settings.startMainLoopFromConstructor(repository) - - - class SkeinforgeRepository: -@@ -568,13 +569,35 @@ - settings.LabelDisplay().getFromName('', self) - importantFileNames = ['craft', 'profile'] - getRadioPluginsAddPluginGroupFrame(archive.getSkeinforgePluginsPath(), importantFileNames, getPluginFileNames(), self) -- self.executeTitle = 'Skeinforge' -+ self.executeTitle = 'Skeinforge a file...' -+ -+ def getPyPyExe(self): -+ pypyExe = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../pypy-1.7/pypy.exe")); -+ if os.path.exists(pypyExe): -+ return pypyExe -+ pypyExe = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../pypy-1.7/bin/pypy")); -+ if os.path.exists(pypyExe): -+ return pypyExe -+ pypyExe = "/bin/pypy"; -+ if os.path.exists(pypyExe): -+ return pypyExe -+ pypyExe = "/usr/bin/pypy"; -+ if os.path.exists(pypyExe): -+ return pypyExe -+ pypyExe = "/usr/local/bin/pypy"; -+ if os.path.exists(pypyExe): -+ return pypyExe -+ return False - - def execute(self): - 'Skeinforge button has been clicked.' - fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) -+ pypyExe = self.getPyPyExe() - for fileName in fileNames: -- skeinforge_craft.writeOutput(fileName) -+ if pypyExe == False or platform.python_implementation() == "PyPy": -+ skeinforge_craft.writeOutput(fileName) -+ else: -+ subprocess.call([pypyExe, __file__, fileName]) - - def save(self): - 'Profile has been saved and profile menu should be updated.' ---- ori/41/skeinforge_application/skeinforge_utilities/skeinforge_profile.py -+++ target/SF41/skeinforge_application/skeinforge_utilities/skeinforge_profile.py -@@ -14,7 +14,6 @@ - #Init has to be imported first because it has code to workaround the python bug where relative imports don't work if the module is imported as a main module. - import __init__ - --from fabmetheus_utilities.hidden_scrollbar import HiddenScrollbar - from fabmetheus_utilities import archive - from fabmetheus_utilities import euclidean - from fabmetheus_utilities import gcodec -@@ -243,6 +242,7 @@ - #http://www.pythonware.com/library/tkinter/introduction/x5453-patterns.htm - self.root = gridPosition.master - gridPosition.increment() -+ from fabmetheus_utilities.hidden_scrollbar import HiddenScrollbar - scrollbar = HiddenScrollbar( gridPosition.master ) - self.listbox = settings.Tkinter.Listbox( gridPosition.master, selectmode = settings.Tkinter.SINGLE, yscrollcommand = scrollbar.set ) - self.listbox.bind('', self.buttonReleaseOne ) diff --git a/patches/45 b/patches/45 deleted file mode 100644 index c650742..0000000 --- a/patches/45 +++ /dev/null @@ -1,438 +0,0 @@ ---- ori/45/fabmetheus_utilities/archive.py -+++ target/SF45/fabmetheus_utilities/archive.py -@@ -18,7 +18,7 @@ - __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' - - --globalTemporarySettingsPath = os.path.join(os.path.expanduser('~'), '.skeinforge') -+globalTemporarySettingsPath = os.path.join(os.path.expanduser('~'), '.skeinforge_pypy') - - - def addToNamePathDictionary(directoryPath, namePathDictionary): ---- ori/45/fabmetheus_utilities/settings.py -+++ target/SF45/fabmetheus_utilities/settings.py -@@ -289,7 +289,7 @@ - if repository.baseNameSynonym != None: - text = archive.getFileText(archive.getProfilesPath(getProfileBaseNameSynonym(repository)), False) - if text == '': -- print('The default %s will be written in the .skeinforge folder in the home directory.' % repository.title.lower() ) -+ print('The default %s will be written in the .skeinforge_pypy folder in the home directory.' % repository.title.lower() ) - text = archive.getFileText(getProfilesDirectoryInAboveDirectory(getProfileBaseName(repository)), False) - if text != '': - readSettingsFromText(repository, text) ---- ori/45/skeinforge_application/alterations/end.gcode -+++ target/SF45/skeinforge_application/alterations/end.gcode -@@ -0,0 +1,10 @@ -+(start of end.gcode) -+M104 S0 (extruder heat off) -+M106 (fan on) -+G91 (relative positioning) -+G1 Z+10 E-5 F400 (move Z up a bit and retract filament by 5mm) -+G1 X-20 Y-20 F1500 (move X and Y over a bit) -+M84 (steppers off) -+G90 (absolute positioning) -+(end of end.gcode) -+ ---- ori/45/skeinforge_application/alterations/start.gcode -+++ target/SF45/skeinforge_application/alterations/start.gcode -@@ -0,0 +1,31 @@ -+(start of start.txt) -+M92 E926.5 (the number of extruder steps to take in 1mm of filament) -+G21 (metric values) -+G21 -+G21 (all the extra G21 commands are comments - skeinforge eats lines without a gcode) -+G21 -+G90 (absolute positioning) -+G21 -+G28 X0 Y0 (move X/Y to min endstops) -+G28 Z0 (move Z to min endstops) -+G21 -+G21 ( if your prints start too high, try changing the Z0.0 below ) -+G21 ( to Z1.0 - the number after the Z is the actual, physical ) -+G21 ( height of the nozzle in mm. This can take some messing around ) -+G21 ( with to get just right... ) -+G21 -+G92 X0 Y0 Z0 E0 (reset software position to front/left/z=0.0) -+G21 -+G1 Z15.0 F400 (move the platform down 15mm) -+G92 E0 (zero the extruded length) -+G21 -+G1 F75 E5 (extrude 5mm of feed stock) -+G1 F75 E3.5 (reverse feed stock by 1.5mm) -+G92 E0 (zero the extruded length again) -+G21 -+M1 (Clean the nozzle then press YES to continue...) -+G21 -+G1 X100 Y100 F3500 (go to the middle of the platform) -+G1 Z0.0 F400 (back to Z=0 and start the print!) -+(end of start.txt) -+ ---- ori/45/skeinforge_application/skeinforge_plugins/analyze_plugins/skeinlayer.py -+++ target/SF45/skeinforge_application/skeinforge_plugins/analyze_plugins/skeinlayer.py -@@ -200,7 +200,7 @@ - self.baseNameSynonym = 'skeinview.csv' - self.fileNameInput = settings.FileNameInput().getFromFileName( [ ('Gcode text files', '*.gcode') ], 'Open File for Skeinlayer', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Skeinlayer') -- self.activateSkeinlayer = settings.BooleanSetting().getFromValue('Activate Skeinlayer', self, True ) -+ self.activateSkeinlayer = settings.BooleanSetting().getFromValue('Activate Skeinlayer', self, False ) - self.addAnimation() - self.drawArrows = settings.BooleanSetting().getFromValue('Draw Arrows', self, True ) - self.goAroundExtruderOffTravel = settings.BooleanSetting().getFromValue('Go Around Extruder Off Travel', self, False ) ---- ori/45/skeinforge_application/skeinforge_plugins/craft_plugins/carve.py -+++ target/SF45/skeinforge_application/skeinforge_plugins/craft_plugins/carve.py -@@ -163,7 +163,7 @@ - self.addLayerTemplateToSVG = settings.BooleanSetting().getFromValue('Add Layer Template to SVG', self, True) - self.extraDecimalPlaces = settings.FloatSpin().getFromValue(0.0, 'Extra Decimal Places (float):', self, 3.0, 2.0) - self.importCoarseness = settings.FloatSpin().getFromValue( 0.5, 'Import Coarseness (ratio):', self, 2.0, 1.0 ) -- self.layerThickness = settings.FloatSpin().getFromValue( 0.1, 'Layer Thickness (mm):', self, 1.0, 0.4 ) -+ self.layerThickness = settings.FloatSpin().getFromValue( 0.1, 'Layer Thickness (mm):', self, 1.0, 0.2 ) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Layers -', self ) - self.layersFrom = settings.IntSpin().getFromValue( 0, 'Layers From (index):', self, 20, 0 ) -@@ -173,7 +173,7 @@ - importLatentStringVar = settings.LatentStringVar() - self.correctMesh = settings.Radio().getFromRadio( importLatentStringVar, 'Correct Mesh', self, True ) - self.unprovenMesh = settings.Radio().getFromRadio( importLatentStringVar, 'Unproven Mesh', self, False ) -- self.perimeterWidthOverThickness = settings.FloatSpin().getFromValue( 1.4, 'Perimeter Width over Thickness (ratio):', self, 2.2, 1.8 ) -+ self.perimeterWidth = settings.FloatSpin().getFromValue( 0.1, 'Perimeter Width:', self, 2.2, 0.4 ) - self.svgViewer = settings.StringSetting().getFromValue('SVG Viewer:', self, 'webbrowser') - settings.LabelSeparator().getFromRepository(self) - self.executeTitle = 'Carve' -@@ -190,7 +190,7 @@ - def getCarvedSVG(self, carving, fileName, repository): - "Parse gnu triangulated surface text and store the carved gcode." - layerThickness = repository.layerThickness.value -- perimeterWidth = repository.perimeterWidthOverThickness.value * layerThickness -+ perimeterWidth = repository.perimeterWidth.value - carving.setCarveLayerThickness(layerThickness) - importRadius = 0.5 * repository.importCoarseness.value * abs(perimeterWidth) - carving.setCarveImportRadius(max(importRadius, 0.01 * layerThickness)) -@@ -201,7 +201,7 @@ - return '' - layerThickness = carving.getCarveLayerThickness() - decimalPlacesCarried = euclidean.getDecimalPlacesCarried(repository.extraDecimalPlaces.value, layerThickness) -- perimeterWidth = repository.perimeterWidthOverThickness.value * layerThickness -+ perimeterWidth = repository.perimeterWidth.value - svgWriter = svg_writer.SVGWriter( - repository.addLayerTemplateToSVG.value, - carving.getCarveCornerMaximum(), ---- ori/45/skeinforge_application/skeinforge_plugins/craft_plugins/chamber.py -+++ target/SF45/skeinforge_application/skeinforge_plugins/craft_plugins/chamber.py -@@ -201,7 +201,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.chamber.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Chamber', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Chamber') -- self.activateChamber = settings.BooleanSetting().getFromValue('Activate Chamber', self, True ) -+ self.activateChamber = settings.BooleanSetting().getFromValue('Activate Chamber', self, False ) - self.bedTemperature = settings.FloatSpin().getFromValue( 20.0, 'Bed Temperature (Celcius):', self, 90.0, 60.0 ) - self.chamberTemperature = settings.FloatSpin().getFromValue( 20.0, 'Chamber Temperature (Celcius):', self, 90.0, 30.0 ) - self.holdingForce = settings.FloatSpin().getFromValue( 0.0, 'Holding Force (bar):', self, 100.0, 0.0 ) ---- ori/45/skeinforge_application/skeinforge_plugins/craft_plugins/clip.py -+++ target/SF45/skeinforge_application/skeinforge_plugins/craft_plugins/clip.py -@@ -92,7 +92,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.clip.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Clip', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Clip') -- self.activateClip = settings.BooleanSetting().getFromValue('Activate Clip', self, True ) -+ self.activateClip = settings.BooleanSetting().getFromValue('Activate Clip', self, False ) - self.clipOverPerimeterWidth = settings.FloatSpin().getFromValue( 0.1, 'Clip Over Perimeter Width (ratio):', self, 0.8, 0.5 ) - self.maximumConnectionDistanceOverPerimeterWidth = settings.FloatSpin().getFromValue( 1.0, 'Maximum Connection Distance Over Perimeter Width (ratio):', self, 20.0, 10.0 ) - self.executeTitle = 'Clip' ---- ori/45/skeinforge_application/skeinforge_plugins/craft_plugins/comb.py -+++ target/SF45/skeinforge_application/skeinforge_plugins/craft_plugins/comb.py -@@ -126,7 +126,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.comb.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Comb', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Comb') -- self.activateComb = settings.BooleanSetting().getFromValue('Activate Comb', self, False ) -+ self.activateComb = settings.BooleanSetting().getFromValue('Activate Comb', self, True ) - self.executeTitle = 'Comb' - - def execute(self): ---- ori/45/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py -+++ target/SF45/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py -@@ -149,7 +149,7 @@ - self.orbit = settings.MenuRadio().getFromMenuButtonDisplay(self.coolType, 'Orbit', self, False) - self.slowDown = settings.MenuRadio().getFromMenuButtonDisplay(self.coolType, 'Slow Down', self, True) - self.maximumCool = settings.FloatSpin().getFromValue(0.0, 'Maximum Cool (Celcius):', self, 10.0, 2.0) -- self.minimumLayerTime = settings.FloatSpin().getFromValue(0.0, 'Minimum Layer Time (seconds):', self, 120.0, 60.0) -+ self.minimumLayerTime = settings.FloatSpin().getFromValue(0.0, 'Minimum Layer Time (seconds):', self, 120.0, 10.0) - self.minimumOrbitalRadius = settings.FloatSpin().getFromValue( - 0.0, 'Minimum Orbital Radius (millimeters):', self, 20.0, 10.0) - settings.LabelSeparator().getFromRepository(self) ---- ori/45/skeinforge_application/skeinforge_plugins/craft_plugins/dimension.py -+++ target/SF45/skeinforge_application/skeinforge_plugins/craft_plugins/dimension.py -@@ -148,7 +148,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.dimension.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Dimension', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Dimension') -- self.activateDimension = settings.BooleanSetting().getFromValue('Activate Dimension', self, False ) -+ self.activateDimension = settings.BooleanSetting().getFromValue('Activate Dimension', self, True ) - extrusionDistanceFormatLatentStringVar = settings.LatentStringVar() - self.extrusionDistanceFormatChoiceLabel = settings.LabelDisplay().getFromName('Extrusion Distance Format Choice: ', self ) - settings.Radio().getFromRadio( extrusionDistanceFormatLatentStringVar, 'Absolute Extrusion Distance', self, True ) -@@ -156,7 +156,7 @@ - self.extruderRetractionSpeed = settings.FloatSpin().getFromValue( 4.0, 'Extruder Retraction Speed (mm/s):', self, 34.0, 13.3 ) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Filament -', self ) -- self.filamentDiameter = settings.FloatSpin().getFromValue(1.0, 'Filament Diameter (mm):', self, 6.0, 2.8) -+ self.filamentDiameter = settings.FloatSpin().getFromValue(1.0, 'Filament Diameter (mm):', self, 6.0, 2.89) - self.filamentPackingDensity = settings.FloatSpin().getFromValue(0.7, 'Filament Packing Density (ratio):', self, 1.0, 0.85) - settings.LabelSeparator().getFromRepository(self) - self.maximumEValueBeforeReset = settings.FloatSpin().getFromValue(0.0, 'Maximum E Value before Reset (float):', self, 999999.9, 91234.0) ---- ori/45/skeinforge_application/skeinforge_plugins/craft_plugins/export.py -+++ target/SF45/skeinforge_application/skeinforge_plugins/craft_plugins/export.py -@@ -339,17 +339,20 @@ - self.exportLabel = settings.LabelDisplay().getFromName('Export Operations: ', self) - self.exportPlugins = [] - exportLatentStringVar = settings.LatentStringVar() -- self.doNotChangeOutput = settings.RadioCapitalized().getFromRadio(exportLatentStringVar, 'Do Not Change Output', self, True) -+ self.doNotChangeOutput = settings.RadioCapitalized().getFromRadio(exportLatentStringVar, 'Do Not Change Output', self, False) - self.doNotChangeOutput.directoryPath = None - allExportPluginFileNames = exportPluginFileNames + exportStaticPluginFileNames - for exportPluginFileName in allExportPluginFileNames: - exportPlugin = None -+ default = False -+ if exportPluginFileName == "gcode_small": -+ default = True - if exportPluginFileName in exportPluginFileNames: - path = os.path.join(exportPluginsFolderPath, exportPluginFileName) -- exportPlugin = settings.RadioCapitalizedButton().getFromPath(exportLatentStringVar, exportPluginFileName, path, self, False) -+ exportPlugin = settings.RadioCapitalizedButton().getFromPath(exportLatentStringVar, exportPluginFileName, path, self, default) - exportPlugin.directoryPath = exportPluginsFolderPath - else: -- exportPlugin = settings.RadioCapitalized().getFromRadio(exportLatentStringVar, exportPluginFileName, self, False) -+ exportPlugin = settings.RadioCapitalized().getFromRadio(exportLatentStringVar, exportPluginFileName, self, default) - exportPlugin.directoryPath = exportStaticDirectoryPath - self.exportPlugins.append(exportPlugin) - self.fileExtension = settings.StringSetting().getFromValue('File Extension:', self, 'gcode') ---- ori/45/skeinforge_application/skeinforge_plugins/craft_plugins/fill.py -+++ target/SF45/skeinforge_application/skeinforge_plugins/craft_plugins/fill.py -@@ -803,7 +803,7 @@ - self.infillPatternLine = settings.Radio().getFromRadio( infillLatentStringVar, 'Line', self, True ) - self.infillPerimeterOverlap = settings.FloatSpin().getFromValue( 0.0, 'Infill Perimeter Overlap (ratio):', self, 0.4, 0.15 ) - self.infillSolidity = settings.FloatSpin().getFromValue( 0.04, 'Infill Solidity (ratio):', self, 0.3, 0.2 ) -- self.infillWidthOverThickness = settings.FloatSpin().getFromValue(1.3, 'Infill Width over Thickness (ratio):', self, 1.7, 1.5) -+ self.infillWidth = settings.FloatSpin().getFromValue( 0.1, 'Infill Width:', self, 1.7, 0.4 ) - settings.LabelSeparator().getFromRepository(self) - self.solidSurfaceThickness = settings.IntSpin().getFromValue(0, 'Solid Surface Thickness (layers):', self, 5, 3) - self.startFromChoice = settings.MenuButtonDisplay().getFromName('Start From Choice:', self) -@@ -1256,7 +1256,7 @@ - return - elif firstWord == '(': - self.layerThickness = float(splitLine[1]) -- self.infillWidth = self.repository.infillWidthOverThickness.value * self.layerThickness -+ self.infillWidth = self.repository.infillWidth.value - self.surroundingSlope = math.tan(math.radians(min(self.repository.surroundingAngle.value, 80.0))) - self.distanceFeedRate.addTagRoundedLine('infillPerimeterOverlap', self.repository.infillPerimeterOverlap.value) - self.distanceFeedRate.addTagRoundedLine('infillWidth', self.infillWidth) ---- ori/45/skeinforge_application/skeinforge_plugins/craft_plugins/home.py -+++ target/SF45/skeinforge_application/skeinforge_plugins/craft_plugins/home.py -@@ -82,7 +82,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.home.html', self) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Home', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_home') -- self.activateHome = settings.BooleanSetting().getFromValue('Activate Home', self, True ) -+ self.activateHome = settings.BooleanSetting().getFromValue('Activate Home', self, False ) - self.nameOfHomeFile = settings.StringSetting().getFromValue('Name of Home File:', self, 'home.gcode') - self.executeTitle = 'Home' - ---- ori/45/skeinforge_application/skeinforge_plugins/craft_plugins/jitter.py -+++ target/SF45/skeinforge_application/skeinforge_plugins/craft_plugins/jitter.py -@@ -116,7 +116,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.jitter.html', self) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Jitter', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Jitter') -- self.activateJitter = settings.BooleanSetting().getFromValue('Activate Jitter', self, True) -+ self.activateJitter = settings.BooleanSetting().getFromValue('Activate Jitter', self, False) - self.jitterOverPerimeterWidth = settings.FloatSpin().getFromValue(1.0, 'Jitter Over Perimeter Width (ratio):', self, 3.0, 2.0) - self.executeTitle = 'Jitter' - ---- ori/45/skeinforge_application/skeinforge_plugins/craft_plugins/limit.py -+++ target/SF45/skeinforge_application/skeinforge_plugins/craft_plugins/limit.py -@@ -85,7 +85,7 @@ - 'Set the default settings, execute title & settings fileName.' - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.limit.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Limit', self, '') -- self.activateLimit = settings.BooleanSetting().getFromValue('Activate Limit', self, True) -+ self.activateLimit = settings.BooleanSetting().getFromValue('Activate Limit', self, False) - self.maximumInitialFeedRate = settings.FloatSpin().getFromValue(0.5, 'Maximum Initial Feed Rate (mm/s):', self, 10.0, 1.0) - self.executeTitle = 'Limit' - ---- ori/45/skeinforge_application/skeinforge_plugins/craft_plugins/multiply.py -+++ target/SF45/skeinforge_application/skeinforge_plugins/craft_plugins/multiply.py -@@ -112,11 +112,11 @@ - self.fileNameInput = settings.FileNameInput().getFromFileName( - fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Multiply', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Multiply') -- self.activateMultiply = settings.BooleanSetting().getFromValue('Activate Multiply', self, False) -+ self.activateMultiply = settings.BooleanSetting().getFromValue('Activate Multiply', self, True) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Center -', self ) -- self.centerX = settings.FloatSpin().getFromValue(-100.0, 'Center X (mm):', self, 100.0, 0.0) -- self.centerY = settings.FloatSpin().getFromValue(-100.0, 'Center Y (mm):', self, 100.0, 0.0) -+ self.centerX = settings.FloatSpin().getFromValue(-100.0, 'Center X (mm):', self, 100.0, 105.0) -+ self.centerY = settings.FloatSpin().getFromValue(-100.0, 'Center Y (mm):', self, 100.0, 105.0) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Number of Cells -', self) - self.numberOfColumns = settings.IntSpin().getFromValue(1, 'Number of Columns (integer):', self, 10, 1) ---- ori/45/skeinforge_application/skeinforge_plugins/craft_plugins/raft.py -+++ target/SF45/skeinforge_application/skeinforge_plugins/craft_plugins/raft.py -@@ -338,7 +338,7 @@ - self.baseInfillDensity = settings.FloatSpin().getFromValue(0.3, 'Base Infill Density (ratio):', self, 0.9, 0.5) - self.baseLayerThicknessOverLayerThickness = settings.FloatSpin().getFromValue( - 1.0, 'Base Layer Thickness over Layer Thickness:', self, 3.0, 2.0) -- self.baseLayers = settings.IntSpin().getFromValue(0, 'Base Layers (integer):', self, 3, 1) -+ self.baseLayers = settings.IntSpin().getFromValue(0, 'Base Layers (integer):', self, 3, 0) - self.baseNozzleLiftOverBaseLayerThickness = settings.FloatSpin().getFromValue( - 0.2, 'Base Nozzle Lift over Base Layer Thickness (ratio):', self, 0.8, 0.4) - settings.LabelSeparator().getFromRepository(self) -@@ -356,7 +356,7 @@ - self.interfaceLayerThicknessOverLayerThickness = settings.FloatSpin().getFromValue( - 1.0, 'Interface Layer Thickness over Layer Thickness:', self, 3.0, 1.0) - self.interfaceLayers = settings.IntSpin().getFromValue( -- 0, 'Interface Layers (integer):', self, 3, 2) -+ 0, 'Interface Layers (integer):', self, 3, 0) - self.interfaceNozzleLiftOverInterfaceLayerThickness = settings.FloatSpin().getFromValue( - 0.25, 'Interface Nozzle Lift over Interface Layer Thickness (ratio):', self, 0.85, 0.45) - settings.LabelSeparator().getFromRepository(self) ---- ori/45/skeinforge_application/skeinforge_plugins/craft_plugins/speed.py -+++ target/SF45/skeinforge_application/skeinforge_plugins/craft_plugins/speed.py -@@ -180,18 +180,22 @@ - self.dutyCycleAtBeginning = settings.FloatSpin().getFromValue( 0.0, 'Duty Cyle at Beginning (portion):', self, 1.0, 1.0 ) - self.dutyCycleAtEnding = settings.FloatSpin().getFromValue( 0.0, 'Duty Cyle at Ending (portion):', self, 1.0, 0.0 ) - settings.LabelSeparator().getFromRepository(self) -- self.feedRatePerSecond = settings.FloatSpin().getFromValue( 2.0, 'Feed Rate (mm/s):', self, 50.0, 16.0 ) -- self.flowRateSetting = settings.FloatSpin().getFromValue( 50.0, 'Flow Rate Setting (float):', self, 250.0, 210.0 ) -+ self.feedRatePerSecond = settings.FloatSpin().getFromValue( 2.0, 'Feed Rate (mm/s):', self, 250.0, 50.0 ) -+ self.flowRateSetting = settings.FloatSpin().getFromValue( 50.0, 'Flow Rate Setting (float):', self, 250.0, 50.0 ) - settings.LabelSeparator().getFromRepository(self) -- settings.LabelDisplay().getFromName('- Object First Layer -', self) -+ settings.LabelDisplay().getFromName('- Object First Layers -', self) - self.objectFirstLayerFeedRateInfillMultiplier = settings.FloatSpin().getFromValue( - 0.2, 'Object First Layer Feed Rate Infill Multiplier (ratio):', self, 1.0, 0.4) - self.objectFirstLayerFeedRatePerimeterMultiplier = settings.FloatSpin().getFromValue( - 0.2, 'Object First Layer Feed Rate Perimeter Multiplier (ratio):', self, 1.0, 0.4) -+ self.objectFirstLayerFeedRateTravelMultiplier = settings.FloatSpin().getFromValue( -+ 0.2, 'Object First Layer Feed Rate Travel Multiplier (ratio):', self, 1.0, 0.4) - self.objectFirstLayerFlowRateInfillMultiplier = settings.FloatSpin().getFromValue( - 0.2, 'Object First Layer Flow Rate Infill Multiplier (ratio):', self, 1.0, 0.4) - self.objectFirstLayerFlowRatePerimeterMultiplier = settings.FloatSpin().getFromValue( - 0.2, 'Object First Layer Flow Rate Perimeter Multiplier (ratio):', self, 1.0, 0.4) -+ self.objectFirstLayersLayerAmount = settings.IntSpin().getFromValue( -+ 1, 'Object First Layers Amount Of Layers For Speed Change:', self, 10, 3) - settings.LabelSeparator().getFromRepository(self) - self.orbitalFeedRateOverOperatingFeedRate = settings.FloatSpin().getFromValue( 0.1, 'Orbital Feed Rate over Operating Feed Rate (ratio):', self, 0.9, 0.5 ) - self.maximumZFeedRatePerSecond = settings.FloatSpin().getFromValue(0.5, 'Maximum Z Feed Rate (mm/s):', self, 10.0, 1.0) -@@ -200,7 +204,7 @@ - self.perimeterFeedRateMultiplier = settings.FloatSpin().getFromValue(0.5, 'Perimeter Feed Rate Multiplier (ratio):', self, 1.0, 1.0) - self.perimeterFlowRateMultiplier = settings.FloatSpin().getFromValue(0.5, 'Perimeter Flow Rate Multiplier (ratio):', self, 1.0, 1.0) - settings.LabelSeparator().getFromRepository(self) -- self.travelFeedRatePerSecond = settings.FloatSpin().getFromValue( 2.0, 'Travel Feed Rate (mm/s):', self, 50.0, 16.0 ) -+ self.travelFeedRatePerSecond = settings.FloatSpin().getFromValue( 2.0, 'Travel Feed Rate (mm/s):', self, 350.0, 250.0 ) - self.executeTitle = 'Speed' - - def execute(self): -@@ -233,11 +237,11 @@ - flowRate *= self.repository.bridgeFlowRateMultiplier.value - if self.isPerimeterPath: - flowRate *= self.repository.perimeterFlowRateMultiplier.value -- if self.layerIndex == 0: -+ if self.layerIndex < self.repository.objectFirstLayersLayerAmount.value: - if self.isPerimeterPath: -- flowRate *= self.repository.objectFirstLayerFlowRatePerimeterMultiplier.value -+ flowRate *= ((self.repository.objectFirstLayerFlowRatePerimeterMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value - else: -- flowRate *= self.repository.objectFirstLayerFlowRateInfillMultiplier.value -+ flowRate *= ((self.repository.objectFirstLayerFlowRateInfillMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value - if flowRate != self.oldFlowRate: - self.distanceFeedRate.addLine('M108 S' + euclidean.getFourSignificantFigures(flowRate)) - self.oldFlowRate = flowRate -@@ -270,14 +274,16 @@ - feedRateMinute *= self.repository.bridgeFeedRateMultiplier.value - if self.isPerimeterPath: - feedRateMinute *= self.repository.perimeterFeedRateMultiplier.value -- if self.layerIndex == 0: -+ if self.layerIndex < self.repository.objectFirstLayersLayerAmount.value: - if self.isPerimeterPath: -- feedRateMinute *= self.repository.objectFirstLayerFeedRatePerimeterMultiplier.value -+ feedRateMinute *= ((self.repository.objectFirstLayerFeedRatePerimeterMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value - else: -- feedRateMinute *= self.repository.objectFirstLayerFeedRateInfillMultiplier.value -+ feedRateMinute *= ((self.repository.objectFirstLayerFeedRateInfillMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value - self.addFlowRateLine() - if not self.isExtruderActive: - feedRateMinute = self.travelFeedRateMinute -+ if self.layerIndex < self.repository.objectFirstLayersLayerAmount.value: -+ feedRateMinute *= ((self.repository.objectFirstLayerFeedRateTravelMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value - return self.distanceFeedRate.getLineWithFeedRate(feedRateMinute, line, splitLine) - - def parseInitialization(self): ---- ori/45/skeinforge_application/skeinforge_plugins/craft_plugins/temperature.py -+++ target/SF45/skeinforge_application/skeinforge_plugins/craft_plugins/temperature.py -@@ -122,7 +122,7 @@ - "Set the default settings, execute title & settings fileName." - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.temperature.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Temperature', self, '') -- self.activateTemperature = settings.BooleanSetting().getFromValue('Activate Temperature', self, True ) -+ self.activateTemperature = settings.BooleanSetting().getFromValue('Activate Temperature', self, False ) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Rate -', self ) - self.coolingRate = settings.FloatSpin().getFromValue( 1.0, 'Cooling Rate (Celcius/second):', self, 20.0, 3.0 ) ---- ori/45/skeinforge_application/skeinforge.py -+++ target/SF45/skeinforge_application/skeinforge.py -@@ -228,6 +228,8 @@ - from skeinforge_application.skeinforge_utilities import skeinforge_profile - import os - import sys -+import platform -+import subprocess - - - # document after stretch, then carve, comb, fill, home, inset, oozebane, raft, splodge, temperature once they are updated, maybe later subplugins like export static, maybe later mill cut and coil plugins, maybe later still export plugins & change file extension to output file extension http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge -@@ -559,7 +561,6 @@ - repository = getNewRepository() - repository.fileNameInput.value = fileName - repository.execute() -- settings.startMainLoopFromConstructor(repository) - - - class SkeinforgeRepository: -@@ -576,13 +577,35 @@ - settings.LabelDisplay().getFromName('', self) - importantFileNames = ['craft', 'profile'] - getRadioPluginsAddPluginGroupFrame(archive.getSkeinforgePluginsPath(), importantFileNames, getPluginFileNames(), self) -- self.executeTitle = 'Skeinforge' -+ self.executeTitle = 'Skeinforge a file...' -+ -+ def getPyPyExe(self): -+ pypyExe = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../pypy-1.7/pypy.exe")); -+ if os.path.exists(pypyExe): -+ return pypyExe -+ pypyExe = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../pypy-1.7/bin/pypy")); -+ if os.path.exists(pypyExe): -+ return pypyExe -+ pypyExe = "/bin/pypy"; -+ if os.path.exists(pypyExe): -+ return pypyExe -+ pypyExe = "/usr/bin/pypy"; -+ if os.path.exists(pypyExe): -+ return pypyExe -+ pypyExe = "/usr/local/bin/pypy"; -+ if os.path.exists(pypyExe): -+ return pypyExe -+ return False - - def execute(self): - 'Skeinforge button has been clicked.' - fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) -+ pypyExe = self.getPyPyExe() - for fileName in fileNames: -- skeinforge_craft.writeOutput(fileName) -+ if pypyExe == False or platform.python_implementation() == "PyPy": -+ skeinforge_craft.writeOutput(fileName) -+ else: -+ subprocess.call([pypyExe, __file__, fileName]) - - def save(self): - 'Profile has been saved and profile menu should be updated.' diff --git a/patches/46 b/patches/46 deleted file mode 100644 index afb7e49..0000000 --- a/patches/46 +++ /dev/null @@ -1,442 +0,0 @@ ---- ori/46/fabmetheus_utilities/archive.py -+++ target/SF46/fabmetheus_utilities/archive.py -@@ -18,7 +18,7 @@ - __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' - - --globalTemporarySettingsPath = os.path.join(os.path.expanduser('~'), '.skeinforge') -+globalTemporarySettingsPath = os.path.join(os.path.expanduser('~'), '.skeinforge_pypy') - - - def addToNamePathDictionary(directoryPath, namePathDictionary): ---- ori/46/fabmetheus_utilities/settings.py -+++ target/SF46/fabmetheus_utilities/settings.py -@@ -289,7 +289,7 @@ - if repository.baseNameSynonym != None: - text = archive.getFileText(archive.getProfilesPath(getProfileBaseNameSynonym(repository)), False) - if text == '': -- print('The default %s will be written in the .skeinforge folder in the home directory.' % repository.title.lower() ) -+ print('The default %s will be written in the .skeinforge_pypy folder in the home directory.' % repository.title.lower() ) - text = archive.getFileText(getProfilesDirectoryInAboveDirectory(getProfileBaseName(repository)), False) - if text != '': - readSettingsFromText(repository, text) ---- ori/46/skeinforge_application/alterations/end.gcode -+++ target/SF46/skeinforge_application/alterations/end.gcode -@@ -0,0 +1,10 @@ -+(start of end.gcode) -+M104 S0 (extruder heat off) -+M106 (fan on) -+G91 (relative positioning) -+G1 Z+10 E-5 F400 (move Z up a bit and retract filament by 5mm) -+G1 X-20 Y-20 F1500 (move X and Y over a bit) -+M84 (steppers off) -+G90 (absolute positioning) -+(end of end.gcode) -+ ---- ori/46/skeinforge_application/alterations/start.gcode -+++ target/SF46/skeinforge_application/alterations/start.gcode -@@ -0,0 +1,31 @@ -+(start of start.txt) -+M92 E926.5 (the number of extruder steps to take in 1mm of filament) -+G21 (metric values) -+G21 -+G21 (all the extra G21 commands are comments - skeinforge eats lines without a gcode) -+G21 -+G90 (absolute positioning) -+G21 -+G28 X0 Y0 (move X/Y to min endstops) -+G28 Z0 (move Z to min endstops) -+G21 -+G21 ( if your prints start too high, try changing the Z0.0 below ) -+G21 ( to Z1.0 - the number after the Z is the actual, physical ) -+G21 ( height of the nozzle in mm. This can take some messing around ) -+G21 ( with to get just right... ) -+G21 -+G92 X0 Y0 Z0 E0 (reset software position to front/left/z=0.0) -+G21 -+G1 Z15.0 F400 (move the platform down 15mm) -+G92 E0 (zero the extruded length) -+G21 -+G1 F75 E5 (extrude 5mm of feed stock) -+G1 F75 E3.5 (reverse feed stock by 1.5mm) -+G92 E0 (zero the extruded length again) -+G21 -+M1 (Clean the nozzle then press YES to continue...) -+G21 -+G1 X100 Y100 F3500 (go to the middle of the platform) -+G1 Z0.0 F400 (back to Z=0 and start the print!) -+(end of start.txt) -+ ---- ori/46/skeinforge_application/skeinforge_plugins/analyze_plugins/skeinlayer.py -+++ target/SF46/skeinforge_application/skeinforge_plugins/analyze_plugins/skeinlayer.py -@@ -200,7 +200,7 @@ - self.baseNameSynonym = 'skeinview.csv' - self.fileNameInput = settings.FileNameInput().getFromFileName( [ ('Gcode text files', '*.gcode') ], 'Open File for Skeinlayer', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Skeinlayer') -- self.activateSkeinlayer = settings.BooleanSetting().getFromValue('Activate Skeinlayer', self, True ) -+ self.activateSkeinlayer = settings.BooleanSetting().getFromValue('Activate Skeinlayer', self, False ) - self.addAnimation() - self.drawArrows = settings.BooleanSetting().getFromValue('Draw Arrows', self, True ) - self.goAroundExtruderOffTravel = settings.BooleanSetting().getFromValue('Go Around Extruder Off Travel', self, False ) ---- ori/46/skeinforge_application/skeinforge_plugins/craft_plugins/carve.py -+++ target/SF46/skeinforge_application/skeinforge_plugins/craft_plugins/carve.py -@@ -163,7 +163,7 @@ - self.addLayerTemplateToSVG = settings.BooleanSetting().getFromValue('Add Layer Template to SVG', self, True) - self.extraDecimalPlaces = settings.FloatSpin().getFromValue(0.0, 'Extra Decimal Places (float):', self, 3.0, 2.0) - self.importCoarseness = settings.FloatSpin().getFromValue( 0.5, 'Import Coarseness (ratio):', self, 2.0, 1.0 ) -- self.layerThickness = settings.FloatSpin().getFromValue( 0.1, 'Layer Thickness (mm):', self, 1.0, 0.4 ) -+ self.layerThickness = settings.FloatSpin().getFromValue( 0.1, 'Layer Thickness (mm):', self, 1.0, 0.2 ) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Layers -', self ) - self.layersFrom = settings.IntSpin().getFromValue( 0, 'Layers From (index):', self, 20, 0 ) -@@ -173,7 +173,7 @@ - importLatentStringVar = settings.LatentStringVar() - self.correctMesh = settings.Radio().getFromRadio( importLatentStringVar, 'Correct Mesh', self, True ) - self.unprovenMesh = settings.Radio().getFromRadio( importLatentStringVar, 'Unproven Mesh', self, False ) -- self.perimeterWidthOverThickness = settings.FloatSpin().getFromValue( 1.4, 'Perimeter Width over Thickness (ratio):', self, 2.2, 1.8 ) -+ self.perimeterWidth = settings.FloatSpin().getFromValue( 0.1, 'Perimeter Width:', self, 2.2, 0.4 ) - self.svgViewer = settings.StringSetting().getFromValue('SVG Viewer:', self, 'webbrowser') - settings.LabelSeparator().getFromRepository(self) - self.executeTitle = 'Carve' -@@ -190,7 +190,7 @@ - def getCarvedSVG(self, carving, fileName, repository): - "Parse gnu triangulated surface text and store the carved gcode." - layerThickness = repository.layerThickness.value -- perimeterWidth = repository.perimeterWidthOverThickness.value * layerThickness -+ perimeterWidth = repository.perimeterWidth.value - carving.setCarveLayerThickness(layerThickness) - importRadius = 0.5 * repository.importCoarseness.value * abs(perimeterWidth) - carving.setCarveImportRadius(max(importRadius, 0.001 * layerThickness)) -@@ -201,7 +201,7 @@ - return '' - layerThickness = carving.getCarveLayerThickness() - decimalPlacesCarried = euclidean.getDecimalPlacesCarried(repository.extraDecimalPlaces.value, layerThickness) -- perimeterWidth = repository.perimeterWidthOverThickness.value * layerThickness -+ perimeterWidth = repository.perimeterWidth.value - svgWriter = svg_writer.SVGWriter( - repository.addLayerTemplateToSVG.value, - carving.getCarveCornerMaximum(), ---- ori/46/skeinforge_application/skeinforge_plugins/craft_plugins/chamber.py -+++ target/SF46/skeinforge_application/skeinforge_plugins/craft_plugins/chamber.py -@@ -205,7 +205,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.chamber.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Chamber', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Chamber') -- self.activateChamber = settings.BooleanSetting().getFromValue('Activate Chamber', self, True ) -+ self.activateChamber = settings.BooleanSetting().getFromValue('Activate Chamber', self, False ) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Bed -', self ) - self.bedTemperature = settings.FloatSpin().getFromValue(20.0, 'Bed Temperature (Celcius):', self, 90.0, 60.0) ---- ori/46/skeinforge_application/skeinforge_plugins/craft_plugins/clip.py -+++ target/SF46/skeinforge_application/skeinforge_plugins/craft_plugins/clip.py -@@ -92,7 +92,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.clip.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Clip', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Clip') -- self.activateClip = settings.BooleanSetting().getFromValue('Activate Clip', self, True ) -+ self.activateClip = settings.BooleanSetting().getFromValue('Activate Clip', self, False ) - self.clipOverPerimeterWidth = settings.FloatSpin().getFromValue( 0.1, 'Clip Over Perimeter Width (ratio):', self, 0.8, 0.5 ) - self.maximumConnectionDistanceOverPerimeterWidth = settings.FloatSpin().getFromValue( 1.0, 'Maximum Connection Distance Over Perimeter Width (ratio):', self, 20.0, 10.0 ) - self.executeTitle = 'Clip' ---- ori/46/skeinforge_application/skeinforge_plugins/craft_plugins/comb.py -+++ target/SF46/skeinforge_application/skeinforge_plugins/craft_plugins/comb.py -@@ -160,7 +160,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.comb.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Comb', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Comb') -- self.activateComb = settings.BooleanSetting().getFromValue('Activate Comb', self, False ) -+ self.activateComb = settings.BooleanSetting().getFromValue('Activate Comb', self, True ) - self.runningJumpSpace = settings.FloatSpin().getFromValue(0.0, 'Running Jump Space (mm):', self, 5.0, 2.0) - self.executeTitle = 'Comb' - ---- ori/46/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py -+++ target/SF46/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py -@@ -149,7 +149,7 @@ - self.orbit = settings.MenuRadio().getFromMenuButtonDisplay(self.coolType, 'Orbit', self, False) - self.slowDown = settings.MenuRadio().getFromMenuButtonDisplay(self.coolType, 'Slow Down', self, True) - self.maximumCool = settings.FloatSpin().getFromValue(0.0, 'Maximum Cool (Celcius):', self, 10.0, 2.0) -- self.minimumLayerTime = settings.FloatSpin().getFromValue(0.0, 'Minimum Layer Time (seconds):', self, 120.0, 60.0) -+ self.minimumLayerTime = settings.FloatSpin().getFromValue(0.0, 'Minimum Layer Time (seconds):', self, 120.0, 10.0) - self.minimumOrbitalRadius = settings.FloatSpin().getFromValue( - 0.0, 'Minimum Orbital Radius (millimeters):', self, 20.0, 10.0) - settings.LabelSeparator().getFromRepository(self) ---- ori/46/skeinforge_application/skeinforge_plugins/craft_plugins/dimension.py -+++ target/SF46/skeinforge_application/skeinforge_plugins/craft_plugins/dimension.py -@@ -148,7 +148,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.dimension.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Dimension', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Dimension') -- self.activateDimension = settings.BooleanSetting().getFromValue('Activate Dimension', self, False ) -+ self.activateDimension = settings.BooleanSetting().getFromValue('Activate Dimension', self, True ) - extrusionDistanceFormatLatentStringVar = settings.LatentStringVar() - self.extrusionDistanceFormatChoiceLabel = settings.LabelDisplay().getFromName('Extrusion Distance Format Choice: ', self ) - settings.Radio().getFromRadio( extrusionDistanceFormatLatentStringVar, 'Absolute Extrusion Distance', self, True ) -@@ -156,7 +156,7 @@ - self.extruderRetractionSpeed = settings.FloatSpin().getFromValue( 4.0, 'Extruder Retraction Speed (mm/s):', self, 34.0, 13.3 ) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Filament -', self ) -- self.filamentDiameter = settings.FloatSpin().getFromValue(1.0, 'Filament Diameter (mm):', self, 6.0, 2.8) -+ self.filamentDiameter = settings.FloatSpin().getFromValue(1.0, 'Filament Diameter (mm):', self, 6.0, 2.89) - self.filamentPackingDensity = settings.FloatSpin().getFromValue(0.7, 'Filament Packing Density (ratio):', self, 1.0, 0.85) - settings.LabelSeparator().getFromRepository(self) - self.maximumEValueBeforeReset = settings.FloatSpin().getFromValue(0.0, 'Maximum E Value before Reset (float):', self, 999999.9, 91234.0) ---- ori/46/skeinforge_application/skeinforge_plugins/craft_plugins/export.py -+++ target/SF46/skeinforge_application/skeinforge_plugins/craft_plugins/export.py -@@ -339,17 +339,20 @@ - self.exportLabel = settings.LabelDisplay().getFromName('Export Operations: ', self) - self.exportPlugins = [] - exportLatentStringVar = settings.LatentStringVar() -- self.doNotChangeOutput = settings.RadioCapitalized().getFromRadio(exportLatentStringVar, 'Do Not Change Output', self, True) -+ self.doNotChangeOutput = settings.RadioCapitalized().getFromRadio(exportLatentStringVar, 'Do Not Change Output', self, False) - self.doNotChangeOutput.directoryPath = None - allExportPluginFileNames = exportPluginFileNames + exportStaticPluginFileNames - for exportPluginFileName in allExportPluginFileNames: - exportPlugin = None -+ default = False -+ if exportPluginFileName == "gcode_small": -+ default = True - if exportPluginFileName in exportPluginFileNames: - path = os.path.join(exportPluginsFolderPath, exportPluginFileName) -- exportPlugin = settings.RadioCapitalizedButton().getFromPath(exportLatentStringVar, exportPluginFileName, path, self, False) -+ exportPlugin = settings.RadioCapitalizedButton().getFromPath(exportLatentStringVar, exportPluginFileName, path, self, default) - exportPlugin.directoryPath = exportPluginsFolderPath - else: -- exportPlugin = settings.RadioCapitalized().getFromRadio(exportLatentStringVar, exportPluginFileName, self, False) -+ exportPlugin = settings.RadioCapitalized().getFromRadio(exportLatentStringVar, exportPluginFileName, self, default) - exportPlugin.directoryPath = exportStaticDirectoryPath - self.exportPlugins.append(exportPlugin) - self.fileExtension = settings.StringSetting().getFromValue('File Extension:', self, 'gcode') ---- ori/46/skeinforge_application/skeinforge_plugins/craft_plugins/fill.py -+++ target/SF46/skeinforge_application/skeinforge_plugins/craft_plugins/fill.py -@@ -802,7 +802,7 @@ - self.infillPatternLine = settings.Radio().getFromRadio( infillLatentStringVar, 'Line', self, True ) - self.infillPerimeterOverlap = settings.FloatSpin().getFromValue( 0.0, 'Infill Perimeter Overlap (ratio):', self, 0.4, 0.15 ) - self.infillSolidity = settings.FloatSpin().getFromValue( 0.04, 'Infill Solidity (ratio):', self, 0.3, 0.2 ) -- self.infillWidthOverThickness = settings.FloatSpin().getFromValue(1.3, 'Infill Width over Thickness (ratio):', self, 1.7, 1.5) -+ self.infillWidth = settings.FloatSpin().getFromValue( 0.1, 'Infill Width:', self, 1.7, 0.4 ) - settings.LabelSeparator().getFromRepository(self) - self.solidSurfaceThickness = settings.IntSpin().getFromValue(0, 'Solid Surface Thickness (layers):', self, 5, 3) - self.startFromChoice = settings.MenuButtonDisplay().getFromName('Start From Choice:', self) -@@ -1250,7 +1250,7 @@ - return - elif firstWord == '(': - self.layerThickness = float(splitLine[1]) -- self.infillWidth = self.repository.infillWidthOverThickness.value * self.layerThickness -+ self.infillWidth = self.repository.infillWidth.value - self.surroundingSlope = math.tan(math.radians(min(self.repository.surroundingAngle.value, 80.0))) - self.distanceFeedRate.addTagRoundedLine('infillPerimeterOverlap', self.repository.infillPerimeterOverlap.value) - self.distanceFeedRate.addTagRoundedLine('infillWidth', self.infillWidth) ---- ori/46/skeinforge_application/skeinforge_plugins/craft_plugins/home.py -+++ target/SF46/skeinforge_application/skeinforge_plugins/craft_plugins/home.py -@@ -82,10 +82,10 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.home.html', self) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Home', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Home') -- self.activateHome = settings.BooleanSetting().getFromValue('Activate Home', self, True ) -+ self.activateHome = settings.BooleanSetting().getFromValue('Activate Home', self, False ) - self.nameOfHomeFile = settings.StringSetting().getFromValue('Name of Home File:', self, 'home.gcode') - self.executeTitle = 'Home' -- -+ - def execute(self): - "Home button has been clicked." - fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) ---- ori/46/skeinforge_application/skeinforge_plugins/craft_plugins/jitter.py -+++ target/SF46/skeinforge_application/skeinforge_plugins/craft_plugins/jitter.py -@@ -116,7 +116,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.jitter.html', self) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Jitter', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Jitter') -- self.activateJitter = settings.BooleanSetting().getFromValue('Activate Jitter', self, True) -+ self.activateJitter = settings.BooleanSetting().getFromValue('Activate Jitter', self, False) - self.jitterOverPerimeterWidth = settings.FloatSpin().getFromValue(1.0, 'Jitter Over Perimeter Width (ratio):', self, 3.0, 2.0) - self.executeTitle = 'Jitter' - ---- ori/46/skeinforge_application/skeinforge_plugins/craft_plugins/limit.py -+++ target/SF46/skeinforge_application/skeinforge_plugins/craft_plugins/limit.py -@@ -86,7 +86,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.limit.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Limit', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Limit') -- self.activateLimit = settings.BooleanSetting().getFromValue('Activate Limit', self, True) -+ self.activateLimit = settings.BooleanSetting().getFromValue('Activate Limit', self, False) - self.maximumInitialFeedRate = settings.FloatSpin().getFromValue(0.5, 'Maximum Initial Feed Rate (mm/s):', self, 10.0, 1.0) - self.executeTitle = 'Limit' - ---- ori/46/skeinforge_application/skeinforge_plugins/craft_plugins/multiply.py -+++ target/SF46/skeinforge_application/skeinforge_plugins/craft_plugins/multiply.py -@@ -112,11 +112,11 @@ - self.fileNameInput = settings.FileNameInput().getFromFileName( - fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Multiply', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Multiply') -- self.activateMultiply = settings.BooleanSetting().getFromValue('Activate Multiply', self, False) -+ self.activateMultiply = settings.BooleanSetting().getFromValue('Activate Multiply', self, True) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Center -', self ) -- self.centerX = settings.FloatSpin().getFromValue(-100.0, 'Center X (mm):', self, 100.0, 0.0) -- self.centerY = settings.FloatSpin().getFromValue(-100.0, 'Center Y (mm):', self, 100.0, 0.0) -+ self.centerX = settings.FloatSpin().getFromValue(-100.0, 'Center X (mm):', self, 100.0, 105.0) -+ self.centerY = settings.FloatSpin().getFromValue(-100.0, 'Center Y (mm):', self, 100.0, 105.0) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Number of Cells -', self) - self.numberOfColumns = settings.IntSpin().getFromValue(1, 'Number of Columns (integer):', self, 10, 1) ---- ori/46/skeinforge_application/skeinforge_plugins/craft_plugins/raft.py -+++ target/SF46/skeinforge_application/skeinforge_plugins/craft_plugins/raft.py -@@ -351,7 +351,7 @@ - self.baseInfillDensity = settings.FloatSpin().getFromValue(0.3, 'Base Infill Density (ratio):', self, 0.9, 0.5) - self.baseLayerThicknessOverLayerThickness = settings.FloatSpin().getFromValue( - 1.0, 'Base Layer Thickness over Layer Thickness:', self, 3.0, 2.0) -- self.baseLayers = settings.IntSpin().getFromValue(0, 'Base Layers (integer):', self, 3, 1) -+ self.baseLayers = settings.IntSpin().getFromValue(0, 'Base Layers (integer):', self, 3, 0) - self.baseNozzleLiftOverBaseLayerThickness = settings.FloatSpin().getFromValue( - 0.2, 'Base Nozzle Lift over Base Layer Thickness (ratio):', self, 0.8, 0.4) - settings.LabelSeparator().getFromRepository(self) -@@ -369,7 +369,7 @@ - self.interfaceLayerThicknessOverLayerThickness = settings.FloatSpin().getFromValue( - 1.0, 'Interface Layer Thickness over Layer Thickness:', self, 3.0, 1.0) - self.interfaceLayers = settings.IntSpin().getFromValue( -- 0, 'Interface Layers (integer):', self, 3, 2) -+ 0, 'Interface Layers (integer):', self, 3, 0) - self.interfaceNozzleLiftOverInterfaceLayerThickness = settings.FloatSpin().getFromValue( - 0.25, 'Interface Nozzle Lift over Interface Layer Thickness (ratio):', self, 0.85, 0.45) - settings.LabelSeparator().getFromRepository(self) ---- ori/46/skeinforge_application/skeinforge_plugins/craft_plugins/speed.py -+++ target/SF46/skeinforge_application/skeinforge_plugins/craft_plugins/speed.py -@@ -180,18 +180,22 @@ - self.dutyCycleAtBeginning = settings.FloatSpin().getFromValue( 0.0, 'Duty Cyle at Beginning (portion):', self, 1.0, 1.0 ) - self.dutyCycleAtEnding = settings.FloatSpin().getFromValue( 0.0, 'Duty Cyle at Ending (portion):', self, 1.0, 0.0 ) - settings.LabelSeparator().getFromRepository(self) -- self.feedRatePerSecond = settings.FloatSpin().getFromValue( 2.0, 'Feed Rate (mm/s):', self, 50.0, 16.0 ) -- self.flowRateSetting = settings.FloatSpin().getFromValue( 50.0, 'Flow Rate Setting (float):', self, 250.0, 210.0 ) -+ self.feedRatePerSecond = settings.FloatSpin().getFromValue( 2.0, 'Feed Rate (mm/s):', self, 250.0, 50.0 ) -+ self.flowRateSetting = settings.FloatSpin().getFromValue( 50.0, 'Flow Rate Setting (float):', self, 250.0, 50.0 ) - settings.LabelSeparator().getFromRepository(self) -- settings.LabelDisplay().getFromName('- Object First Layer -', self) -+ settings.LabelDisplay().getFromName('- Object First Layers -', self) - self.objectFirstLayerFeedRateInfillMultiplier = settings.FloatSpin().getFromValue( - 0.2, 'Object First Layer Feed Rate Infill Multiplier (ratio):', self, 1.0, 0.4) - self.objectFirstLayerFeedRatePerimeterMultiplier = settings.FloatSpin().getFromValue( - 0.2, 'Object First Layer Feed Rate Perimeter Multiplier (ratio):', self, 1.0, 0.4) -+ self.objectFirstLayerFeedRateTravelMultiplier = settings.FloatSpin().getFromValue( -+ 0.2, 'Object First Layer Feed Rate Travel Multiplier (ratio):', self, 1.0, 0.4) - self.objectFirstLayerFlowRateInfillMultiplier = settings.FloatSpin().getFromValue( - 0.2, 'Object First Layer Flow Rate Infill Multiplier (ratio):', self, 1.0, 0.4) - self.objectFirstLayerFlowRatePerimeterMultiplier = settings.FloatSpin().getFromValue( - 0.2, 'Object First Layer Flow Rate Perimeter Multiplier (ratio):', self, 1.0, 0.4) -+ self.objectFirstLayersLayerAmount = settings.IntSpin().getFromValue( -+ 1, 'Object First Layers Amount Of Layers For Speed Change:', self, 10, 3) - settings.LabelSeparator().getFromRepository(self) - self.orbitalFeedRateOverOperatingFeedRate = settings.FloatSpin().getFromValue( 0.1, 'Orbital Feed Rate over Operating Feed Rate (ratio):', self, 0.9, 0.5 ) - self.maximumZFeedRatePerSecond = settings.FloatSpin().getFromValue(0.5, 'Maximum Z Feed Rate (mm/s):', self, 10.0, 1.0) -@@ -200,7 +204,7 @@ - self.perimeterFeedRateMultiplier = settings.FloatSpin().getFromValue(0.5, 'Perimeter Feed Rate Multiplier (ratio):', self, 1.0, 1.0) - self.perimeterFlowRateMultiplier = settings.FloatSpin().getFromValue(0.5, 'Perimeter Flow Rate Multiplier (ratio):', self, 1.0, 1.0) - settings.LabelSeparator().getFromRepository(self) -- self.travelFeedRatePerSecond = settings.FloatSpin().getFromValue( 2.0, 'Travel Feed Rate (mm/s):', self, 50.0, 16.0 ) -+ self.travelFeedRatePerSecond = settings.FloatSpin().getFromValue( 2.0, 'Travel Feed Rate (mm/s):', self, 350.0, 250.0 ) - self.executeTitle = 'Speed' - - def execute(self): -@@ -233,11 +237,11 @@ - flowRate *= self.repository.bridgeFlowRateMultiplier.value - if self.isPerimeterPath: - flowRate *= self.repository.perimeterFlowRateMultiplier.value -- if self.layerIndex == 0: -+ if self.layerIndex < self.repository.objectFirstLayersLayerAmount.value: - if self.isPerimeterPath: -- flowRate *= self.repository.objectFirstLayerFlowRatePerimeterMultiplier.value -+ flowRate *= ((self.repository.objectFirstLayerFlowRatePerimeterMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value - else: -- flowRate *= self.repository.objectFirstLayerFlowRateInfillMultiplier.value -+ flowRate *= ((self.repository.objectFirstLayerFlowRateInfillMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value - if flowRate != self.oldFlowRate: - self.distanceFeedRate.addLine('M108 S' + euclidean.getFourSignificantFigures(flowRate)) - self.oldFlowRate = flowRate -@@ -270,14 +274,16 @@ - feedRateMinute *= self.repository.bridgeFeedRateMultiplier.value - if self.isPerimeterPath: - feedRateMinute *= self.repository.perimeterFeedRateMultiplier.value -- if self.layerIndex == 0: -+ if self.layerIndex < self.repository.objectFirstLayersLayerAmount.value: - if self.isPerimeterPath: -- feedRateMinute *= self.repository.objectFirstLayerFeedRatePerimeterMultiplier.value -+ feedRateMinute *= ((self.repository.objectFirstLayerFeedRatePerimeterMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value - else: -- feedRateMinute *= self.repository.objectFirstLayerFeedRateInfillMultiplier.value -+ feedRateMinute *= ((self.repository.objectFirstLayerFeedRateInfillMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value - self.addFlowRateLine() - if not self.isExtruderActive: - feedRateMinute = self.travelFeedRateMinute -+ if self.layerIndex < self.repository.objectFirstLayersLayerAmount.value: -+ feedRateMinute *= ((self.repository.objectFirstLayerFeedRateTravelMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value - return self.distanceFeedRate.getLineWithFeedRate(feedRateMinute, line, splitLine) - - def parseInitialization(self): ---- ori/46/skeinforge_application/skeinforge_plugins/craft_plugins/temperature.py -+++ target/SF46/skeinforge_application/skeinforge_plugins/craft_plugins/temperature.py -@@ -126,7 +126,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.temperature.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Temperature', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Temperature') -- self.activateTemperature = settings.BooleanSetting().getFromValue('Activate Temperature', self, True ) -+ self.activateTemperature = settings.BooleanSetting().getFromValue('Activate Temperature', self, False ) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Rate -', self ) - self.coolingRate = settings.FloatSpin().getFromValue( 1.0, 'Cooling Rate (Celcius/second):', self, 20.0, 3.0 ) ---- ori/46/skeinforge_application/skeinforge.py -+++ target/SF46/skeinforge_application/skeinforge.py -@@ -228,6 +228,8 @@ - from skeinforge_application.skeinforge_utilities import skeinforge_profile - import os - import sys -+import platform -+import subprocess - - - # document raft, stretch, then carve, comb, fill, inset, oozebane, splodge, temperature, speed once they are updated -@@ -563,7 +565,6 @@ - repository = getNewRepository() - repository.fileNameInput.value = fileName - repository.execute() -- settings.startMainLoopFromConstructor(repository) - - - class SkeinforgeRepository: -@@ -580,13 +581,35 @@ - settings.LabelDisplay().getFromName('', self) - importantFileNames = ['craft', 'profile'] - getRadioPluginsAddPluginGroupFrame(archive.getSkeinforgePluginsPath(), importantFileNames, getPluginFileNames(), self) -- self.executeTitle = 'Skeinforge' -+ self.executeTitle = 'Skeinforge a file...' -+ -+ def getPyPyExe(self): -+ pypyExe = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../pypy-1.7/pypy.exe")); -+ if os.path.exists(pypyExe): -+ return pypyExe -+ pypyExe = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../pypy-1.7/bin/pypy")); -+ if os.path.exists(pypyExe): -+ return pypyExe -+ pypyExe = "/bin/pypy"; -+ if os.path.exists(pypyExe): -+ return pypyExe -+ pypyExe = "/usr/bin/pypy"; -+ if os.path.exists(pypyExe): -+ return pypyExe -+ pypyExe = "/usr/local/bin/pypy"; -+ if os.path.exists(pypyExe): -+ return pypyExe -+ return False - - def execute(self): - 'Skeinforge button has been clicked.' - fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) -+ pypyExe = self.getPyPyExe() - for fileName in fileNames: -- skeinforge_craft.writeOutput(fileName) -+ if pypyExe == False or platform.python_implementation() == "PyPy": -+ skeinforge_craft.writeOutput(fileName) -+ else: -+ subprocess.call([pypyExe, __file__, fileName]) - - def save(self): - 'Profile has been saved and profile menu should be updated.' diff --git a/patches/48 b/patches/48 deleted file mode 100644 index 74e7769..0000000 --- a/patches/48 +++ /dev/null @@ -1,437 +0,0 @@ ---- ori/48/fabmetheus_utilities/archive.py -+++ target/SF48/fabmetheus_utilities/archive.py -@@ -18,7 +18,7 @@ - __license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html' - - --globalTemporarySettingsPath = os.path.join(os.path.expanduser('~'), '.skeinforge') -+globalTemporarySettingsPath = os.path.join(os.path.expanduser('~'), '.skeinforge_pypy') - - - def addToNamePathDictionary(directoryPath, namePathDictionary): ---- ori/48/fabmetheus_utilities/settings.py -+++ target/SF48/fabmetheus_utilities/settings.py -@@ -301,7 +301,7 @@ - if repository.baseNameSynonym != None: - text = archive.getFileText(archive.getProfilesPath(getProfileName(repository.baseNameSynonym, repository)), False) - if text == '': -- print('The default %s will be written in the .skeinforge folder in the home directory.' % repository.title.lower() ) -+ print('The default %s will be written in the .skeinforge_pypy folder in the home directory.' % repository.title.lower() ) - text = archive.getFileText(getProfilesDirectoryInAboveDirectory(getProfileBaseName(repository)), False) - if text != '': - readSettingsFromText(repository, text) ---- ori/48/skeinforge_application/alterations/end.gcode -+++ target/SF48/skeinforge_application/alterations/end.gcode -@@ -0,0 +1,10 @@ -+(start of end.gcode) -+M104 S0 (extruder heat off) -+M106 (fan on) -+G91 (relative positioning) -+G1 Z+10 E-5 F400 (move Z up a bit and retract filament by 5mm) -+G1 X-20 Y-20 F1500 (move X and Y over a bit) -+M84 (steppers off) -+G90 (absolute positioning) -+(end of end.gcode) -+ ---- ori/48/skeinforge_application/alterations/start.gcode -+++ target/SF48/skeinforge_application/alterations/start.gcode -@@ -0,0 +1,31 @@ -+(start of start.txt) -+M92 E926.5 (the number of extruder steps to take in 1mm of filament) -+G21 (metric values) -+G21 -+G21 (all the extra G21 commands are comments - skeinforge eats lines without a gcode) -+G21 -+G90 (absolute positioning) -+G21 -+G28 X0 Y0 (move X/Y to min endstops) -+G28 Z0 (move Z to min endstops) -+G21 -+G21 ( if your prints start too high, try changing the Z0.0 below ) -+G21 ( to Z1.0 - the number after the Z is the actual, physical ) -+G21 ( height of the nozzle in mm. This can take some messing around ) -+G21 ( with to get just right... ) -+G21 -+G92 X0 Y0 Z0 E0 (reset software position to front/left/z=0.0) -+G21 -+G1 Z15.0 F400 (move the platform down 15mm) -+G92 E0 (zero the extruded length) -+G21 -+G1 F75 E5 (extrude 5mm of feed stock) -+G1 F75 E3.5 (reverse feed stock by 1.5mm) -+G92 E0 (zero the extruded length again) -+G21 -+M1 (Clean the nozzle then press YES to continue...) -+G21 -+G1 X100 Y100 F3500 (go to the middle of the platform) -+G1 Z0.0 F400 (back to Z=0 and start the print!) -+(end of start.txt) -+ ---- ori/48/skeinforge_application/skeinforge_plugins/analyze_plugins/skeinlayer.py -+++ target/SF48/skeinforge_application/skeinforge_plugins/analyze_plugins/skeinlayer.py -@@ -200,7 +200,7 @@ - self.baseNameSynonym = 'skeinview.csv' - self.fileNameInput = settings.FileNameInput().getFromFileName( [ ('Gcode text files', '*.gcode') ], 'Open File for Skeinlayer', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Skeinlayer') -- self.activateSkeinlayer = settings.BooleanSetting().getFromValue('Activate Skeinlayer', self, True ) -+ self.activateSkeinlayer = settings.BooleanSetting().getFromValue('Activate Skeinlayer', self, False ) - self.addAnimation() - self.drawArrows = settings.BooleanSetting().getFromValue('Draw Arrows', self, True ) - self.goAroundExtruderOffTravel = settings.BooleanSetting().getFromValue('Go Around Extruder Off Travel', self, False ) ---- ori/48/skeinforge_application/skeinforge_plugins/craft_plugins/carve.py -+++ target/SF48/skeinforge_application/skeinforge_plugins/craft_plugins/carve.py -@@ -161,10 +161,10 @@ - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getTranslatorFileTypeTuples(), 'Open File for Carve', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Carve') - self.addLayerTemplateToSVG = settings.BooleanSetting().getFromValue('Add Layer Template to SVG', self, True) -- self.edgeWidthOverHeight = settings.FloatSpin().getFromValue( 1.4, 'Edge Width over Height (ratio):', self, 2.2, 1.8 ) -+ self.edgeWidth = settings.FloatSpin().getFromValue( 0.1, 'Edge Width (mm):', self, 2.2, 0.4 ) - self.extraDecimalPlaces = settings.FloatSpin().getFromValue(0.0, 'Extra Decimal Places (float):', self, 3.0, 2.0) - self.importCoarseness = settings.FloatSpin().getFromValue( 0.5, 'Import Coarseness (ratio):', self, 2.0, 1.0 ) -- self.layerHeight = settings.FloatSpin().getFromValue( 0.1, 'Layer Height (mm):', self, 1.0, 0.4 ) -+ self.layerHeight = settings.FloatSpin().getFromValue( 0.1, 'Layer Height (mm):', self, 1.0, 0.2 ) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Layers -', self ) - self.layersFrom = settings.IntSpin().getFromValue( 0, 'Layers From (index):', self, 20, 0 ) -@@ -190,7 +190,7 @@ - def getCarvedSVG(self, carving, fileName, repository): - "Parse gnu triangulated surface text and store the carved gcode." - layerHeight = repository.layerHeight.value -- edgeWidth = repository.edgeWidthOverHeight.value * layerHeight -+ edgeWidth = repository.edgeWidth.value - carving.setCarveLayerHeight(layerHeight) - importRadius = 0.5 * repository.importCoarseness.value * abs(edgeWidth) - carving.setCarveImportRadius(max(importRadius, 0.001 * layerHeight)) -@@ -201,7 +201,7 @@ - return '' - layerHeight = carving.getCarveLayerHeight() - decimalPlacesCarried = euclidean.getDecimalPlacesCarried(repository.extraDecimalPlaces.value, layerHeight) -- edgeWidth = repository.edgeWidthOverHeight.value * layerHeight -+ edgeWidth = repository.edgeWidth.value - svgWriter = svg_writer.SVGWriter( - repository.addLayerTemplateToSVG.value, - carving.getCarveCornerMaximum(), ---- ori/48/skeinforge_application/skeinforge_plugins/craft_plugins/chamber.py -+++ target/SF48/skeinforge_application/skeinforge_plugins/craft_plugins/chamber.py -@@ -205,7 +205,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.chamber.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Chamber', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Chamber') -- self.activateChamber = settings.BooleanSetting().getFromValue('Activate Chamber', self, True ) -+ self.activateChamber = settings.BooleanSetting().getFromValue('Activate Chamber', self, False ) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Bed -', self ) - self.bedTemperature = settings.FloatSpin().getFromValue(20.0, 'Bed Temperature (Celcius):', self, 90.0, 60.0) ---- ori/48/skeinforge_application/skeinforge_plugins/craft_plugins/clip.py -+++ target/SF48/skeinforge_application/skeinforge_plugins/craft_plugins/clip.py -@@ -92,7 +92,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.clip.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Clip', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Clip') -- self.activateClip = settings.BooleanSetting().getFromValue('Activate Clip', self, True ) -+ self.activateClip = settings.BooleanSetting().getFromValue('Activate Clip', self, False ) - self.clipOverEdgeWidth = settings.FloatSpin().getFromValue( 0.1, 'Clip Over Perimeter Width (ratio):', self, 0.8, 0.5 ) - self.maximumConnectionDistanceOverEdgeWidth = settings.FloatSpin().getFromValue( 1.0, 'Maximum Connection Distance Over Perimeter Width (ratio):', self, 20.0, 10.0 ) - self.executeTitle = 'Clip' ---- ori/48/skeinforge_application/skeinforge_plugins/craft_plugins/comb.py -+++ target/SF48/skeinforge_application/skeinforge_plugins/craft_plugins/comb.py -@@ -160,7 +160,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.comb.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Comb', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Comb') -- self.activateComb = settings.BooleanSetting().getFromValue('Activate Comb', self, False ) -+ self.activateComb = settings.BooleanSetting().getFromValue('Activate Comb', self, True ) - self.runningJumpSpace = settings.FloatSpin().getFromValue(0.0, 'Running Jump Space (mm):', self, 5.0, 2.0) - self.executeTitle = 'Comb' - ---- ori/48/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py -+++ target/SF48/skeinforge_application/skeinforge_plugins/craft_plugins/cool.py -@@ -149,7 +149,7 @@ - self.orbit = settings.MenuRadio().getFromMenuButtonDisplay(self.coolType, 'Orbit', self, False) - self.slowDown = settings.MenuRadio().getFromMenuButtonDisplay(self.coolType, 'Slow Down', self, True) - self.maximumCool = settings.FloatSpin().getFromValue(0.0, 'Maximum Cool (Celcius):', self, 10.0, 2.0) -- self.minimumLayerTime = settings.FloatSpin().getFromValue(0.0, 'Minimum Layer Time (seconds):', self, 120.0, 60.0) -+ self.minimumLayerTime = settings.FloatSpin().getFromValue(0.0, 'Minimum Layer Time (seconds):', self, 120.0, 10.0) - self.minimumOrbitalRadius = settings.FloatSpin().getFromValue( - 0.0, 'Minimum Orbital Radius (millimeters):', self, 20.0, 10.0) - settings.LabelSeparator().getFromRepository(self) ---- ori/48/skeinforge_application/skeinforge_plugins/craft_plugins/dimension.py -+++ target/SF48/skeinforge_application/skeinforge_plugins/craft_plugins/dimension.py -@@ -148,7 +148,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.dimension.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Dimension', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Dimension') -- self.activateDimension = settings.BooleanSetting().getFromValue('Activate Dimension', self, False ) -+ self.activateDimension = settings.BooleanSetting().getFromValue('Activate Dimension', self, True ) - extrusionDistanceFormatLatentStringVar = settings.LatentStringVar() - self.extrusionDistanceFormatChoiceLabel = settings.LabelDisplay().getFromName('Extrusion Distance Format Choice: ', self ) - settings.Radio().getFromRadio( extrusionDistanceFormatLatentStringVar, 'Absolute Extrusion Distance', self, True ) -@@ -156,7 +156,7 @@ - self.extruderRetractionSpeed = settings.FloatSpin().getFromValue( 4.0, 'Extruder Retraction Speed (mm/s):', self, 34.0, 13.3 ) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Filament -', self ) -- self.filamentDiameter = settings.FloatSpin().getFromValue(1.0, 'Filament Diameter (mm):', self, 6.0, 2.8) -+ self.filamentDiameter = settings.FloatSpin().getFromValue(1.0, 'Filament Diameter (mm):', self, 6.0, 2.89) - self.filamentPackingDensity = settings.FloatSpin().getFromValue(0.7, 'Filament Packing Density (ratio):', self, 1.0, 0.85) - settings.LabelSeparator().getFromRepository(self) - self.maximumEValueBeforeReset = settings.FloatSpin().getFromValue(0.0, 'Maximum E Value before Reset (float):', self, 999999.9, 91234.0) ---- ori/48/skeinforge_application/skeinforge_plugins/craft_plugins/export.py -+++ target/SF48/skeinforge_application/skeinforge_plugins/craft_plugins/export.py -@@ -338,17 +338,20 @@ - self.exportLabel = settings.LabelDisplay().getFromName('Export Operations: ', self) - self.exportPlugins = [] - exportLatentStringVar = settings.LatentStringVar() -- self.doNotChangeOutput = settings.RadioCapitalized().getFromRadio(exportLatentStringVar, 'Do Not Change Output', self, True) -+ self.doNotChangeOutput = settings.RadioCapitalized().getFromRadio(exportLatentStringVar, 'Do Not Change Output', self, False) - self.doNotChangeOutput.directoryPath = None - allExportPluginFileNames = exportPluginFileNames + exportStaticPluginFileNames - for exportPluginFileName in allExportPluginFileNames: - exportPlugin = None -+ default = False -+ if exportPluginFileName == "gcode_small": -+ default = True - if exportPluginFileName in exportPluginFileNames: - path = os.path.join(exportPluginsFolderPath, exportPluginFileName) -- exportPlugin = settings.RadioCapitalizedButton().getFromPath(exportLatentStringVar, exportPluginFileName, path, self, False) -+ exportPlugin = settings.RadioCapitalizedButton().getFromPath(exportLatentStringVar, exportPluginFileName, path, self, default) - exportPlugin.directoryPath = exportPluginsFolderPath - else: -- exportPlugin = settings.RadioCapitalized().getFromRadio(exportLatentStringVar, exportPluginFileName, self, False) -+ exportPlugin = settings.RadioCapitalized().getFromRadio(exportLatentStringVar, exportPluginFileName, self, default) - exportPlugin.directoryPath = exportStaticDirectoryPath - self.exportPlugins.append(exportPlugin) - self.fileExtension = settings.StringSetting().getFromValue('File Extension:', self, 'gcode') ---- ori/48/skeinforge_application/skeinforge_plugins/craft_plugins/fill.py -+++ target/SF48/skeinforge_application/skeinforge_plugins/craft_plugins/fill.py -@@ -798,7 +798,7 @@ - self.infillPatternLine = settings.Radio().getFromRadio( infillLatentStringVar, 'Line', self, True ) - self.infillPerimeterOverlap = settings.FloatSpin().getFromValue( 0.0, 'Infill Perimeter Overlap (ratio):', self, 0.4, 0.15 ) - self.infillSolidity = settings.FloatSpin().getFromValue( 0.04, 'Infill Solidity (ratio):', self, 0.3, 0.2 ) -- self.infillWidthOverThickness = settings.FloatSpin().getFromValue(1.3, 'Infill Width over Thickness (ratio):', self, 1.7, 1.5) -+ self.infillWidth = settings.FloatSpin().getFromValue( 0.1, 'Infill Width:', self, 1.7, 0.4 ) - settings.LabelSeparator().getFromRepository(self) - self.solidSurfaceThickness = settings.IntSpin().getFromValue(0, 'Solid Surface Thickness (layers):', self, 5, 3) - self.startFromChoice = settings.MenuButtonDisplay().getFromName('Start From Choice:', self) -@@ -1247,7 +1247,7 @@ - return - elif firstWord == '(': - self.layerHeight = float(splitLine[1]) -- self.infillWidth = self.repository.infillWidthOverThickness.value * self.layerHeight -+ self.infillWidth = self.repository.infillWidth.value - self.surroundingSlope = math.tan(math.radians(min(self.repository.surroundingAngle.value, 80.0))) - self.distanceFeedRate.addTagRoundedLine('infillPerimeterOverlap', self.repository.infillPerimeterOverlap.value) - self.distanceFeedRate.addTagRoundedLine('infillWidth', self.infillWidth) ---- ori/48/skeinforge_application/skeinforge_plugins/craft_plugins/home.py -+++ target/SF48/skeinforge_application/skeinforge_plugins/craft_plugins/home.py -@@ -82,10 +82,10 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.home.html', self) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Home', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Home') -- self.activateHome = settings.BooleanSetting().getFromValue('Activate Home', self, True ) -+ self.activateHome = settings.BooleanSetting().getFromValue('Activate Home', self, False ) - self.nameOfHomeFile = settings.StringSetting().getFromValue('Name of Home File:', self, 'home.gcode') - self.executeTitle = 'Home' -- -+ - def execute(self): - "Home button has been clicked." - fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) ---- ori/48/skeinforge_application/skeinforge_plugins/craft_plugins/jitter.py -+++ target/SF48/skeinforge_application/skeinforge_plugins/craft_plugins/jitter.py -@@ -116,7 +116,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.jitter.html', self) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Jitter', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Jitter') -- self.activateJitter = settings.BooleanSetting().getFromValue('Activate Jitter', self, True) -+ self.activateJitter = settings.BooleanSetting().getFromValue('Activate Jitter', self, False) - self.jitterOverEdgeWidth = settings.FloatSpin().getFromValue(1.0, 'Jitter Over Perimeter Width (ratio):', self, 3.0, 2.0) - self.executeTitle = 'Jitter' - ---- ori/48/skeinforge_application/skeinforge_plugins/craft_plugins/limit.py -+++ target/SF48/skeinforge_application/skeinforge_plugins/craft_plugins/limit.py -@@ -86,7 +86,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.limit.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Limit', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Limit') -- self.activateLimit = settings.BooleanSetting().getFromValue('Activate Limit', self, True) -+ self.activateLimit = settings.BooleanSetting().getFromValue('Activate Limit', self, False) - self.maximumInitialFeedRate = settings.FloatSpin().getFromValue(0.5, 'Maximum Initial Feed Rate (mm/s):', self, 10.0, 1.0) - self.executeTitle = 'Limit' - ---- ori/48/skeinforge_application/skeinforge_plugins/craft_plugins/multiply.py -+++ target/SF48/skeinforge_application/skeinforge_plugins/craft_plugins/multiply.py -@@ -112,11 +112,11 @@ - self.fileNameInput = settings.FileNameInput().getFromFileName( - fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Multiply', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Multiply') -- self.activateMultiply = settings.BooleanSetting().getFromValue('Activate Multiply', self, False) -+ self.activateMultiply = settings.BooleanSetting().getFromValue('Activate Multiply', self, True) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Center -', self ) -- self.centerX = settings.FloatSpin().getFromValue(-100.0, 'Center X (mm):', self, 100.0, 0.0) -- self.centerY = settings.FloatSpin().getFromValue(-100.0, 'Center Y (mm):', self, 100.0, 0.0) -+ self.centerX = settings.FloatSpin().getFromValue(-100.0, 'Center X (mm):', self, 100.0, 105.0) -+ self.centerY = settings.FloatSpin().getFromValue(-100.0, 'Center Y (mm):', self, 100.0, 105.0) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Number of Cells -', self) - self.numberOfColumns = settings.IntSpin().getFromValue(1, 'Number of Columns (integer):', self, 10, 1) ---- ori/48/skeinforge_application/skeinforge_plugins/craft_plugins/raft.py -+++ target/SF48/skeinforge_application/skeinforge_plugins/craft_plugins/raft.py -@@ -351,7 +351,7 @@ - self.baseInfillDensity = settings.FloatSpin().getFromValue(0.3, 'Base Infill Density (ratio):', self, 0.9, 0.5) - self.baseLayerThicknessOverLayerThickness = settings.FloatSpin().getFromValue( - 1.0, 'Base Layer Thickness over Layer Thickness:', self, 3.0, 2.0) -- self.baseLayers = settings.IntSpin().getFromValue(0, 'Base Layers (integer):', self, 3, 1) -+ self.baseLayers = settings.IntSpin().getFromValue(0, 'Base Layers (integer):', self, 3, 0) - self.baseNozzleLiftOverBaseLayerThickness = settings.FloatSpin().getFromValue( - 0.2, 'Base Nozzle Lift over Base Layer Thickness (ratio):', self, 0.8, 0.4) - settings.LabelSeparator().getFromRepository(self) -@@ -369,7 +369,7 @@ - self.interfaceLayerThicknessOverLayerThickness = settings.FloatSpin().getFromValue( - 1.0, 'Interface Layer Thickness over Layer Thickness:', self, 3.0, 1.0) - self.interfaceLayers = settings.IntSpin().getFromValue( -- 0, 'Interface Layers (integer):', self, 3, 2) -+ 0, 'Interface Layers (integer):', self, 3, 0) - self.interfaceNozzleLiftOverInterfaceLayerThickness = settings.FloatSpin().getFromValue( - 0.25, 'Interface Nozzle Lift over Interface Layer Thickness (ratio):', self, 0.85, 0.45) - settings.LabelSeparator().getFromRepository(self) ---- ori/48/skeinforge_application/skeinforge_plugins/craft_plugins/speed.py -+++ target/SF48/skeinforge_application/skeinforge_plugins/craft_plugins/speed.py -@@ -184,18 +184,22 @@ - self.dutyCycleAtBeginning = settings.FloatSpin().getFromValue( 0.0, 'Duty Cyle at Beginning (portion):', self, 1.0, 1.0 ) - self.dutyCycleAtEnding = settings.FloatSpin().getFromValue( 0.0, 'Duty Cyle at Ending (portion):', self, 1.0, 0.0 ) - settings.LabelSeparator().getFromRepository(self) -- self.feedRatePerSecond = settings.FloatSpin().getFromValue( 2.0, 'Feed Rate (mm/s):', self, 50.0, 16.0 ) -- self.flowRateSetting = settings.FloatSpin().getFromValue( 50.0, 'Flow Rate Setting (float):', self, 250.0, 210.0 ) -+ self.feedRatePerSecond = settings.FloatSpin().getFromValue( 2.0, 'Feed Rate (mm/s):', self, 250.0, 50.0 ) -+ self.flowRateSetting = settings.FloatSpin().getFromValue( 50.0, 'Flow Rate Setting (float):', self, 250.0, 50.0 ) - settings.LabelSeparator().getFromRepository(self) -- settings.LabelDisplay().getFromName('- Object First Layer -', self) -+ settings.LabelDisplay().getFromName('- Object First Layers -', self) - self.objectFirstLayerFeedRateInfillMultiplier = settings.FloatSpin().getFromValue( - 0.2, 'Object First Layer Feed Rate Infill Multiplier (ratio):', self, 1.0, 0.4) - self.objectFirstLayerFeedRatePerimeterMultiplier = settings.FloatSpin().getFromValue( - 0.2, 'Object First Layer Feed Rate Perimeter Multiplier (ratio):', self, 1.0, 0.4) -+ self.objectFirstLayerFeedRateTravelMultiplier = settings.FloatSpin().getFromValue( -+ 0.2, 'Object First Layer Feed Rate Travel Multiplier (ratio):', self, 1.0, 0.4) - self.objectFirstLayerFlowRateInfillMultiplier = settings.FloatSpin().getFromValue( - 0.2, 'Object First Layer Flow Rate Infill Multiplier (ratio):', self, 1.0, 0.4) - self.objectFirstLayerFlowRatePerimeterMultiplier = settings.FloatSpin().getFromValue( - 0.2, 'Object First Layer Flow Rate Perimeter Multiplier (ratio):', self, 1.0, 0.4) -+ self.objectFirstLayersLayerAmount = settings.IntSpin().getFromValue( -+ 1, 'Object First Layers Amount Of Layers For Speed Change:', self, 10, 3) - settings.LabelSeparator().getFromRepository(self) - self.orbitalFeedRateOverOperatingFeedRate = settings.FloatSpin().getFromValue( 0.1, 'Orbital Feed Rate over Operating Feed Rate (ratio):', self, 0.9, 0.5 ) - self.maximumZFeedRatePerSecond = settings.FloatSpin().getFromValue(0.5, 'Maximum Z Feed Rate (mm/s):', self, 10.0, 1.0) -@@ -204,7 +208,7 @@ - self.perimeterFeedRateMultiplier = settings.FloatSpin().getFromValue(0.5, 'Perimeter Feed Rate Multiplier (ratio):', self, 1.0, 1.0) - self.perimeterFlowRateMultiplier = settings.FloatSpin().getFromValue(0.5, 'Perimeter Flow Rate Multiplier (ratio):', self, 1.0, 1.0) - settings.LabelSeparator().getFromRepository(self) -- self.travelFeedRatePerSecond = settings.FloatSpin().getFromValue( 2.0, 'Travel Feed Rate (mm/s):', self, 50.0, 16.0 ) -+ self.travelFeedRatePerSecond = settings.FloatSpin().getFromValue( 2.0, 'Travel Feed Rate (mm/s):', self, 350.0, 250.0 ) - self.executeTitle = 'Speed' - - def execute(self): -@@ -237,11 +241,11 @@ - flowRate *= self.repository.bridgeFlowRateMultiplier.value - if self.isEdgePath: - flowRate *= self.repository.perimeterFlowRateMultiplier.value -- if self.layerIndex == 0: -+ if self.layerIndex < self.repository.objectFirstLayersLayerAmount.value: - if self.isEdgePath: -- flowRate *= self.repository.objectFirstLayerFlowRatePerimeterMultiplier.value -+ flowRate *= ((self.repository.objectFirstLayerFlowRatePerimeterMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value - else: -- flowRate *= self.repository.objectFirstLayerFlowRateInfillMultiplier.value -+ flowRate *= ((self.repository.objectFirstLayerFlowRateInfillMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value - if flowRate != self.oldFlowRate: - self.distanceFeedRate.addLine('M108 S' + euclidean.getFourSignificantFigures(flowRate)) - self.oldFlowRate = flowRate -@@ -274,14 +278,16 @@ - feedRateMinute *= self.repository.bridgeFeedRateMultiplier.value - if self.isEdgePath: - feedRateMinute *= self.repository.perimeterFeedRateMultiplier.value -- if self.layerIndex == 0: -+ if self.layerIndex < self.repository.objectFirstLayersLayerAmount.value: - if self.isEdgePath: -- feedRateMinute *= self.repository.objectFirstLayerFeedRatePerimeterMultiplier.value -+ feedRateMinute *= ((self.repository.objectFirstLayerFeedRatePerimeterMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value - else: -- feedRateMinute *= self.repository.objectFirstLayerFeedRateInfillMultiplier.value -+ feedRateMinute *= ((self.repository.objectFirstLayerFeedRateInfillMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value - self.addFlowRateLine() - if not self.isExtruderActive: - feedRateMinute = self.travelFeedRateMinute -+ if self.layerIndex < self.repository.objectFirstLayersLayerAmount.value: -+ feedRateMinute *= ((self.repository.objectFirstLayerFeedRateTravelMultiplier.value * (self.repository.objectFirstLayersLayerAmount.value - self.layerIndex)) + self.layerIndex) / self.repository.objectFirstLayersLayerAmount.value - return self.distanceFeedRate.getLineWithFeedRate(feedRateMinute, line, splitLine) - - def parseInitialization(self): ---- ori/48/skeinforge_application/skeinforge_plugins/craft_plugins/temperature.py -+++ target/SF48/skeinforge_application/skeinforge_plugins/craft_plugins/temperature.py -@@ -126,7 +126,7 @@ - skeinforge_profile.addListsToCraftTypeRepository('skeinforge_application.skeinforge_plugins.craft_plugins.temperature.html', self ) - self.fileNameInput = settings.FileNameInput().getFromFileName( fabmetheus_interpret.getGNUTranslatorGcodeFileTypeTuples(), 'Open File for Temperature', self, '') - self.openWikiManualHelpPage = settings.HelpPage().getOpenFromAbsolute('http://fabmetheus.crsndoo.com/wiki/index.php/Skeinforge_Temperature') -- self.activateTemperature = settings.BooleanSetting().getFromValue('Activate Temperature', self, True ) -+ self.activateTemperature = settings.BooleanSetting().getFromValue('Activate Temperature', self, False ) - settings.LabelSeparator().getFromRepository(self) - settings.LabelDisplay().getFromName('- Rate -', self ) - self.coolingRate = settings.FloatSpin().getFromValue( 1.0, 'Cooling Rate (Celcius/second):', self, 20.0, 3.0 ) ---- ori/48/skeinforge_application/skeinforge.py -+++ target/SF48/skeinforge_application/skeinforge.py -@@ -228,6 +228,8 @@ - from skeinforge_application.skeinforge_utilities import skeinforge_profile - import os - import sys -+import platform -+import subprocess - - - # document raft, stretch, then carve, comb, fill, inset, oozebane, splodge, temperature, speed once they are updated -@@ -561,7 +563,6 @@ - repository = getNewRepository() - repository.fileNameInput.value = fileName - repository.execute() -- settings.startMainLoopFromConstructor(repository) - - - class SkeinforgeRepository: -@@ -578,13 +579,35 @@ - settings.LabelDisplay().getFromName('', self) - importantFileNames = ['craft', 'profile'] - getRadioPluginsAddPluginGroupFrame(archive.getSkeinforgePluginsPath(), importantFileNames, getPluginFileNames(), self) -- self.executeTitle = 'Skeinforge' -+ self.executeTitle = 'Skeinforge a file...' -+ -+ def getPyPyExe(self): -+ pypyExe = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../pypy-1.7/pypy.exe")); -+ if os.path.exists(pypyExe): -+ return pypyExe -+ pypyExe = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../pypy-1.7/bin/pypy")); -+ if os.path.exists(pypyExe): -+ return pypyExe -+ pypyExe = "/bin/pypy"; -+ if os.path.exists(pypyExe): -+ return pypyExe -+ pypyExe = "/usr/bin/pypy"; -+ if os.path.exists(pypyExe): -+ return pypyExe -+ pypyExe = "/usr/local/bin/pypy"; -+ if os.path.exists(pypyExe): -+ return pypyExe -+ return False - - def execute(self): - 'Skeinforge button has been clicked.' - fileNames = skeinforge_polyfile.getFileOrDirectoryTypesUnmodifiedGcode(self.fileNameInput.value, fabmetheus_interpret.getImportPluginFileNames(), self.fileNameInput.wasCancelled) -+ pypyExe = self.getPyPyExe() - for fileName in fileNames: -- skeinforge_craft.writeOutput(fileName) -+ if pypyExe == False or platform.python_implementation() == "PyPy": -+ skeinforge_craft.writeOutput(fileName) -+ else: -+ subprocess.call([pypyExe, __file__, fileName]) - - def save(self): - 'Profile has been saved and profile menu should be updated.'