Merge branch 'review-openscad'

This commit is contained in:
jriegel 2012-09-05 19:49:39 +02:00
commit 1de8259968
41 changed files with 21170 additions and 1 deletions

View File

@ -46,3 +46,6 @@ endif(FREECAD_BUILD_SANDBOX)
add_subdirectory(Surfaces)
add_subdirectory(Ship)
add_subdirectory(OpenSCAD)

View File

@ -1,5 +1,5 @@
#SUBDIRS=Part Mesh Points Raytracing Image Drawing Complete Draft Test TemplatePyMod
SUBDIRS=Points Complete Draft Test TemplatePyMod Web Start Idf Arch Surfaces Ship
SUBDIRS=Points Complete Draft Test TemplatePyMod Web Start Idf Arch Surfaces Ship OpenSCAD
#if HAVE_OPENCV
SUBDIRS += Image

View File

@ -0,0 +1,45 @@
SET(OpenSCAD_SRCS
Init.py
InitGui.py
OpenSCAD_rc.py
OpenSCAD2Dgeom.py
OpenSCADFeatures.py
OpenSCADUtils.py
OpenSCADCommands.py
exportCSG.py
importCSG.py
prototype.py
tokrules.py
colorcodeshapes.py
expandplacements.py
replaceobj.py
)
SOURCE_GROUP("" FILES ${OpenSCAD_SRCS})
SET(ply_SRCS
ply/lex.py
ply/README
ply/yacc.py
ply/__init__.py
)
SOURCE_GROUP("ply" FILES ${ply_SRCS})
set(all_files ${OpenSCAD_SRCS} ${ply_SRCS})
ADD_CUSTOM_TARGET(OpenSCAD ALL
SOURCES ${allfiles}
)
fc_copy_sources(OpenSCAD "${CMAKE_BINARY_DIR}/Mod/OpenSCAD" ${all_files})
INSTALL(
FILES
${ply_SRCS}
DESTINATION
Mod/OpenSCAD/ply
)
INSTALL(
FILES
${OpenSCAD_SRCS}
DESTINATION
Mod/OpenSCAD
)

26
src/Mod/OpenSCAD/Init.py Normal file
View File

@ -0,0 +1,26 @@
# FreeCAD init script of the OpenSCAD module
# (c) 2001 Juergen Riegel
#***************************************************************************
#* (c) Juergen Riegel (juergen.riegel@web.de) 2002 *
#* *
#* This file is part of the FreeCAD CAx development system. *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* FreeCAD 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 Lesser General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with FreeCAD; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#* Juergen Riegel 2002 *
#***************************************************************************/

129
src/Mod/OpenSCAD/InitGui.py Normal file
View File

@ -0,0 +1,129 @@
# OpenSCAD gui init module
#
# Gathering all the information to start FreeCAD
# This is the second one of three init scripts, the third one
# runs when the gui is up
#***************************************************************************
#* (c) Juergen Riegel (juergen.riegel@web.de) 2002
#* *
#* This file is part of the FreeCAD CAx development system. *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU General Public License (GPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* FreeCAD 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 Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with FreeCAD; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#* Juergen Riegel 2002 *
#***************************************************************************/
import FreeCAD
param = FreeCAD.ParamGet(\
"User parameter:BaseApp/Preferences/Mod/OpenSCAD")
openscadfilename = param.GetString('openscadexecutable')
class OpenSCADWorkbench ( Workbench ):
"OpenSCAD workbench object"
Icon = """
/* XPM */
static char * openscadlogo_xpm[] = {
"16 16 33 1",
" c None",
". c #61320B",
"+ c #5D420B",
"@ c #4F4C09",
"# c #564930",
"$ c #754513",
"% c #815106",
"& c #666509",
"* c #875F55",
"= c #6E7000",
"- c #756A53",
"; c #717037",
"> c #946637",
", c #92710E",
"' c #797A0A",
") c #7C7720",
"! c #8A8603",
"~ c #88886F",
"{ c #AF8181",
"] c #999908",
"^ c #BB8D8D",
"/ c #AAAA00",
"( c #A9A880",
"_ c #B5B419",
": c #C1A9A9",
"< c #B1B19A",
"[ c #BEBE00",
"} c #B9B8B4",
"| c #CACC00",
"1 c #D4D4BC",
"2 c #DBD2D0",
"3 c #EEEEED",
"4 c None",
"4444444444444444",
"4444443113444444",
"4444<;']]!;<^^24",
"444(&@!]]]=&#^{3",
"44<']')@++)!&*{^",
"44)]/[|//[/]'@{{",
"42=/_|||||[]!&*{",
"4(&][|||||[/'@#}",
"3-..,|||||[)&&~4",
"^*$%.!|||[!+/](4",
"^{%%%._[[_&/[_14",
":{>%%.!//])_[_44",
"2{{%%+!]!!)]]344",
"4:{{#@&=&&@#3444",
"44224}~--~}44444",
"4444444444444444"};
"""
MenuText = "OpenSCAD"
ToolTip = "OpenSCAD workbench"
def Initialize(self):
import OpenSCAD_rc,OpenSCADCommands
commands=["ColorCodeShape",'RefineShapeFeature','ReplaceObject',"Edgestofaces",'ExpandPlacements','RemoveSubtree']
import FreeCAD
param = FreeCAD.ParamGet(\
"User parameter:BaseApp/Preferences/Mod/OpenSCAD")
openscadfilename = param.GetString('openscadexecutable')
if openscadfilename:
commands.extend(['AddOpenSCADElement'])
self.appendToolbar("OpenSCADTools",["ColorCodeShape",'RefineShapeFeature','ReplaceObject','RemoveSubtree'])
self.appendMenu('OpenSCAD',commands)
#self.appendMenu('OpenSCAD',["AddOpenSCADElement"])
###self.appendCommandbar("&Generic Tools",["ColorCodeShape"])
FreeCADGui.addIconPath(":/icons")
#FreeCADGui.addLanguagePath(":/translations")
FreeCADGui.addPreferencePage(":/ui/openscadprefs-base.ui","OpenSCAD")
def GetClassName(self):
#return "OpenSCADGui::Workbench"
return "Gui::PythonWorkbench"
Gui.addWorkbench(OpenSCADWorkbench())
App.addImportType("OpenSCAD CSG Format (*.csg)","importCSG")
App.addExportType("OpenSCAD CSG Format (*.csg)","exportCSG")
App.addExportType("OpenSCAD Format (*.scad)","exportCSG")
import os
openscadbin = openscadfilename and os.path.isfile(openscadfilename)
if openscadbin:
App.addImportType("OpenSCAD Format (*.scad)","importCSG")
if param.GetBool('debugRegisterPrototype'):
App.addImportType("OpenSCAD CSG prototype (*.csg)","prototype") #prototype
if openscadbin:
App.addImportType("OpenSCAD prototype (*.scad)","prototype") #prototype

View File

@ -0,0 +1,32 @@
#SUBDIRS=App Gui
# Change data dir from default ($(prefix)/share) to $(prefix)
datadir = $(prefix)/Mod/OpenSCAD
data_DATA = \
Init.py InitGui.py \
OpenSCAD_rc.py \
OpenSCAD2Dgeom.py \
OpenSCADFeatures.py \
OpenSCADUtils.py \
OpenSCADCommands.py \
exportCSG.py \
importCSG.py \
prototype.py \
tokrules.py \
colorcodeshapes.py \
expandplacements.py \
replaceobj.py
nobase_data_DATA = \
ply/lex.py \
ply/README \
ply/yacc.py \
ply/__init__.py
EXTRA_DIST = \
$(data_DATA) $(nobase_data_DATA) \
CMakeLists.txt \
OpenSCAD.dox \
exportVersions.txt \
importVersions.txt

View File

@ -0,0 +1,3 @@
/** \defgroup TEMPLATE OpenSCAD
* \ingroup WORKBENCHES */

View File

@ -0,0 +1,460 @@
#***************************************************************************
#* *
#* Copyright (c) 2012 Sebastian Hoogen <github@sebastianhoogen.de> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* 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 Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
__title__="FreeCAD OpenSCAD Workbench - 2D helper fuctions"
__author__ = "Sebastian Hoogen"
__url__ = ["http://free-cad.sourceforge.net"]
'''
This Script includes python functions to convert imported dxf geometry to Faces
'''
class Overlappingfaces():
'''combines overlapping faces together'''
def __init__(self,facelist):
self.sortedfaces = sorted(facelist,key=(lambda shape: shape.Area),reverse=True)
self.builddepdict()
#self.faceindex = {}
#for idx,face in enumerate(self.sortesfaces):
# self.faceindex[face.hashCode()] = idx
# def __len__(self):
# return len(self.sortedfaces)
@staticmethod
def dofacesoverlapboundbox(bigface,smallface):
return bigface.BoundBox.isIntersection(smallface.BoundBox)
@staticmethod
def dofacesoverlapallverts(bigface,smallface):
def vertsinface(f1,verts,tol=0.001,inface=True):
'''check if all given verts are inside shape f1'''
return all([f1.isInside(vert.Point,tol,inface) for vert in verts])
return vertsinface(bigface,smallface.Vertexes)
@staticmethod
def dofacesoverlapboolean(bigface,smallface):
#import FreeCAD,FreeCADGui
#FreeCAD.Console.PrintLog('intersecting %d %d\n'%(bigfacei,smallfacei))
#FreeCADGui.updateGui()
return bigface.common(smallface).Area > 0
def builddepdict(self):
import itertools
#isinsidelist = []
self.isinsidedict = {}
#for bigface, smallface in itertools.combinations(sortedfaces,2):
for bigfacei, smallfacei in itertools.combinations(range(len(self.sortedfaces)),2):
try:
overlap = Overlappingfaces.dofacesoverlapboolean(\
self.sortedfaces[bigfacei],self.sortedfaces[smallfacei])
except:
overlap = Overlappingfaces.dofacesoverlapallverts(\
self.sortedfaces[bigfacei],self.sortedfaces[smallfacei])
if overlap:
#isinsidelist.append((bigfacei,smallfacei))
smallinbig = self.isinsidedict.get(bigfacei,[])
smallinbig.append(smallfacei)
if len(smallinbig) == 1:
self.isinsidedict[bigfacei] = smallinbig
@staticmethod
def finddepth(dict1,faceidx,curdepth=0):
if faceidx not in dict1:
return curdepth+1
else:
#print dict1[faceidx],[(finddepth(dict1,childface,curdepth)) for childface in dict1[faceidx]]
return max([(Overlappingfaces.finddepth(dict1,childface,curdepth+1)) for childface in dict1[faceidx]])
def findrootdepth(self):
return max([Overlappingfaces.finddepth(self.isinsidedict,fi) for fi in range(len(self.sortedfaces))])
def hasnoparent(self,faceindex):
return Overlappingfaces.hasnoparentstatic(self.isinsidedict,faceindex)
@staticmethod
def hasnoparentstatic(isinsidedict,faceindex):
for smalllist in isinsidedict.itervalues():
if faceindex in smalllist:
return False
return True
#@staticmethod
#def subtreedict(rootface,parantdict):
# '''biuld a subtree dictinary'''
# newdict = parantdict.copy()
# del newdict[rootface]
# return newdict
@staticmethod
def directchildren(isinsidedict,parent):
#return [child for child in isinsidedict.get(parent,[]) if child not in isinsidedict]
dchildren=[]
for child in isinsidedict.get(parent,[]):
direct = True
for key, value in isinsidedict.iteritems():
if key != parent and child in value and parent not in value:
direct = False
if direct:
dchildren.append(child)
return dchildren
#@staticmethod
#def indirectchildren(isinsidedict,parent):
# return [child for child in isinsidedict.get(parent,[]) if child in isinsidedict]
@staticmethod
def printtree(isinsidedict,facenum):
def printtreechild(isinsidedict,facenum,parent):
children=Overlappingfaces.directchildren(isinsidedict,parent)
print 'parent %d directchild %s' % (parent,children)
if children:
subdict=isinsidedict.copy()
del subdict[parent]
for child in children:
printtreechild(subdict,facenum,child)
rootitems=[fi for fi in range(facenum) if Overlappingfaces.hasnoparentstatic(isinsidedict,fi)]
for rootitem in rootitems:
printtreechild(isinsidedict,facenum,rootitem)
def makefeatures(self,doc):
import FreeCAD
def addshape(faceindex):
obj=doc.addObject('Part::Feature','facefromedges_%d' % faceindex)
obj.Shape = self.sortedfaces[faceindex]
obj.ViewObject.hide()
return obj
def addfeature(faceindex,isinsidedict):
directchildren = Overlappingfaces.directchildren(isinsidedict,faceindex)
if len(directchildren) == 0:
obj=addshape(faceindex)
else:
subdict=isinsidedict.copy()
del subdict[faceindex]
obj=doc.addObject("Part::Cut","facesfromedges_%d" % faceindex)
obj.Base= addshape(faceindex) #we only do subtraction
if len(directchildren) == 1:
obj.Tool = addfeature(directchildren[0],subdict)
else:
obj.Tool = doc.addObject("Part::MultiFuse","facesfromedges_union")
obj.Tool.Shapes = [addfeature(child,subdict) for child in directchildren]
obj.Tool.ViewObject.hide()
obj.ViewObject.hide()
return obj
rootitems = [fi for fi in range(len(self.sortedfaces)) if self.hasnoparent(fi)]
for rootitem in rootitems:
addfeature(rootitem,self.isinsidedict).ViewObject.show()
def makeshape(self):
def removefaces(rfaces):
for tfi in directchildren[::-1]:
finishedwith.append(tfi)
#del faces[tfi]
if tfi in isinsidedict:
del isinsidedict[tfi]
for key,value in isinsidedict.iteritems():
if tfi in value:
newlist=value[:] #we work on a shallow copy of isinsidedict
newlist.remove(tfi)
isinsidedict[key]=newlist
def hasnoparent(faceindex):
for smalllist in self.isinsidedict.itervalues():
if faceindex in smalllist:
return False
return True
faces=self.sortedfaces[:]
isinsidedict=self.isinsidedict.copy()
finishedwith=[]
while not all([Overlappingfaces.hasnoparentstatic(isinsidedict,fi) for fi in range(len(faces))]):
#print [(Overlappingfaces.hasnoparentstatic(isinsidedict,fi),\
#Overlappingfaces.directchildren(isinsidedict,fi)) for fi in range(len(faces))]
for fi in range(len(faces))[::-1]:
directchildren = Overlappingfaces.directchildren(isinsidedict,fi)
if not directchildren:
continue
elif len(directchildren) == 1:
faces[fi]=faces[fi].cut(faces[directchildren[0]])
#print fi,'-' ,directchildren[0], faces[fi],faces[directchildren[0]]
removefaces(directchildren)
else:
toolface=fusefaces([faces[tfi] for tfi in directchildren])
faces[fi]=faces[fi].cut(toolface)
#print fi, '- ()', directchildren, [faces[tfi] for tfi in directchildren]
removefaces(directchildren)
#print fi,directchildren
faces =[face for index,face in enumerate(faces) if index not in finishedwith]
# return faces
return fusefaces(faces)
def findConnectedEdges(edgelist,eps=1e-6,debug=False):
'''returns a list of list of connected edges'''
def vertequals(v1,v2,eps=1e-6):
'''check two vertices for equality'''
#return all([abs(c1-c2)<eps for c1,c2 in zip(v1.Point,v2.Point)])
return v1.Point.sub(v2.Point).Length<eps
def vertindex(forward):
'''return index of last or first element'''
return -1 if forward else 0
freeedges = edgelist[:]
retlist = []
debuglist = []
while freeedges:
startwire = freeedges.pop(0)
forward = True
newedge = [(startwire,True)]
for forward in (True, False):
found = True
while found:
lastvert = newedge[vertindex(forward)][0].Vertexes[vertindex(forward == newedge[vertindex(forward)][1])]
for ceindex, checkedge in enumerate(freeedges):
found = False
for cvindex, cvert in enumerate([checkedge.Vertexes[0],checkedge.Vertexes[-1]]):
if vertequals(lastvert,cvert,eps):
if forward:
newedge.append((checkedge,cvindex == 0))
else:
newedge.insert(0,(checkedge,cvindex == 1))
del freeedges[ceindex]
found = True
break
else:
found = False
if found:
break
else:
found = False
#we are finished for this edge
debuglist.append(newedge)
retlist.append([item[0] for item in newedge]) #strip off direction
#print debuglist
if debug:
return retlist,debuglist
else:
return retlist
def endpointdistance(edges):
'''return the distance of of vertices in path (list of edges) as
maximum, mininum and distance between start and endpoint
it expects the edges to be traversed forward from starting from Vertex 0'''
numedges=len(edges)
if numedges == 1 and len(edges[0].Vertexes) == 1:
return 0.0,0.0,0.0
outerdistance = edges[0].Vertexes[0].Point.sub(\
edges[-1].Vertexes[-1].Point).Length
if numedges > 1:
innerdistances=[edges[i].Vertexes[-1].Point.sub(edges[i+1].\
Vertexes[0].Point).Length for i in range(numedges-1)]
return max(innerdistances),min(innerdistances),outerdistance
else:
return 0.0,0.0,outerdistance
def endpointdistancedebuglist(debuglist):
'''return the distance of of vertices in path (list of edges) as
maximum, mininum and distance between start and endpoint
it it expects a 'not reversed' flag for every edge'''
numedges=len(debuglist)
if numedges == 1 and len(debuglist[0][0].Vertexes) == 1:
return 0.0,0.0,0.0
outerdistance = debuglist[0][0].Vertexes[(not debuglist[0][1])*-1].\
Point.sub(debuglist[-1][0].Vertexes[(debuglist[-1][1])*-1].\
Point).Length
if numedges > 1:
innerdistances=[debuglist[i][0].Vertexes[debuglist[i][1]*-1].\
Point.sub(debuglist[i+1][0].Vertexes[(not debuglist[i+1][1])*\
-1].Point).Length for i in range(numedges-1)]
return max(innerdistances),min(innerdistances),outerdistance
else:
return 0.0,0.0,outerdistance
def edgestowires(edgelist,eps=0.001):
'''takes list of edges and returns a list of wires'''
import Part, Draft
# todo remove double edges
wirelist=[]
#for path in findConnectedEdges(edgelist,eps=eps):
for path,debug in zip(*findConnectedEdges(edgelist,eps=eps,debug=True)):
maxd,mind,outerd = endpointdistancedebuglist(debug)
assert(maxd <= eps*2) # Assume the input to be broken
if maxd < eps*2 and maxd > 0.000001: #OCC wont like it if maxd > 0.02:
print 'endpointdistance max:%f min:%f, ends:%f' %(maxd,mind,outerd)
if True:
tobeclosed = outerd < eps*2
# OpenSCAD uses 0.001 for corase grid
#from draftlibs import fcvec, fcgeo
#w2=fcgeo.superWire(path,tobeclosed)
w2=superWireReverse(debug,tobeclosed)
wirelist.append(w2)
else:#this locks up FreeCAD
comp=Part.Compound(path)
wirelist.append(comp.connectEdgesToWires(False,eps).Wires[0])
#wirelist.append(comp.connectEdgesToWires(False,0.1).Wires[0])
else:
done = False
try:
wire=Part.Wire(path)
#if not close or wire.isClosed or outerd > 0.0001:
wirelist.append(Part.Wire(path))
done = True
except:
pass
if not done:
comp=Part.Compound(path)
wirelist.append(comp.connectEdgesToWires(False,eps).Wires[0])
return wirelist
def subtractfaces(faces):
'''searches for the biggest face and subtracts all smaller ones from the
first. Only makes sense if all faces overlap.'''
if len(faces)==1:
return faces[0]
else:
facelist=sorted(faces,key=(lambda shape: shape.Area),reverse=True)
base=facelist[0]
tool=reduce(lambda p1,p2: p1.fuse(p2),facelist[1:])
return base.cut(tool)
def fusefaces(faces):
if len(faces)==1:
return faces[0]
else:
return reduce(lambda p1,p2: p1.fuse(p2),faces)
def subtractfaces2(faces):
'''Sort faces, check if they overlap. Subtract overlapping face and fuse
nonoverlapping groups.'''
return fusefaces([subtractfaces(facegroup) for facegroup in findoverlappingfaces(faces)])
def edgestofaces(edges,algo=3,eps=0.001):
#edges=[]
#for shapeobj in (objs):
# edges.extend(shapeobj.Shape.Edges)
#taken from Drafttools
#from draftlibs import fcvec, fcgeo
import Part
#wires = fcgeo.findWires(edges)
wires = edgestowires(edges,eps)
facel=[]
for w in wires:
#assert(len(w.Edges)>1)
if not w.isClosed():
p0 = w.Vertexes[0].Point
p1 = w.Vertexes[-1].Point
edges2 = w.Edges[:]
try:
edges2.append(Part.Line(p1,p0).toShape())
w = Part.Wire(edges2)
#w = Part.Wire(fcgeo.sortEdges(edges2))
except:
comp=Part.Compound(edges2)
w = comp.connectEdgesToWires(False,eps).Wires[0]
facel.append(Part.Face(w))
#if w.isValid: #debuging
# facel.append(Part.Face(w))
#else:
# Part.show(w)
if algo is None:
return facel
elif algo == 1: #stabale behavior
return subtractfaces(facel)
elif algo == 0: #return all faces
return Part.Compound(facel)
elif algo == 2:
return subtractfaces2(facel)
elif algo == 3:
return Overlappingfaces(facel).makeshape()
def superWireReverse(debuglist,closed=False):
'''superWireReverse(debuglist,[closed]): forces a wire between edges
that don't necessarily have coincident endpoints. If closed=True, wire
will always be closed. debuglist has a tuple for every edge.The first
entry is the edge, the second is the flag 'does not nedd to be inverted'
'''
#taken from draftlibs
def median(v1,v2):
vd = v2.sub(v1)
vd.scale(.5,.5,.5)
return v1.add(vd)
try:
from DraftGeomUtils import findMidpoint
except ImportError: #workaround for Version 0.12
from draftlibs.fcgeo import findMidpoint #workaround for Version 0.12
import Part
#edges = sortEdges(edgeslist)
print debuglist
newedges = []
for i in range(len(debuglist)):
curr = debuglist[i]
if i == 0:
if closed:
prev = debuglist[-1]
else:
prev = None
else:
prev = debuglist[i-1]
if i == (len(debuglist)-1):
if closed:
nexte = debuglist[0]
else:
nexte = None
else:
nexte = debuglist[i+1]
print i,prev,curr,nexte
if prev:
if curr[0].Vertexes[-1*(not curr[1])].Point == \
prev[0].Vertexes[-1*prev[1]].Point:
p1 = curr[0].Vertexes[-1*(not curr[1])].Point
else:
p1 = median(curr[0].Vertexes[-1*(not curr[1])].Point,\
prev[0].Vertexes[-1*prev[1]].Point)
else:
p1 = curr[0].Vertexes[-1*(not curr[1])].Point
if nexte:
if curr[0].Vertexes[-1*curr[1]].Point == \
nexte[0].Vertexes[-1*(not nexte[1])].Point:
p2 = nexte[0].Vertexes[-1*(not nexte[1])].Point
else:
p2 = median(curr[0].Vertexes[-1*(curr[1])].Point,\
nexte[0].Vertexes[-1*(not nexte[1])].Point)
else:
p2 = curr[0].Vertexes[-1*(curr[1])].Point
if isinstance(curr[0].Curve,Part.Line):
print "line",p1,p2
newedges.append(Part.Line(p1,p2).toShape())
elif isinstance(curr[0].Curve,Part.Circle):
p3 = findMidpoint(curr[0])
print "arc",p1,p3,p2
newedges.append(Part.Arc(p1,p3,p2).toShape())
else:
print "Cannot superWire edges that are not lines or arcs"
return None
print newedges
return Part.Wire(newedges)

View File

@ -0,0 +1,173 @@
import FreeCAD,FreeCADGui
from PyQt4 import QtGui, QtCore
class ColorCodeShape:
"Change the Color of selected or all Shapes based on their validity"
def Activated(self):
import colorcodeshapes
selection=FreeCADGui.Selection.getSelectionEx()
if len(selection) > 0:
objs=[selobj.Object for selobj in selection]
else:
objs=FreeCAD.ActiveDocument.Objects
colorcodeshapes.colorcodeshapes(objs)
def GetResources(self):
return {'Pixmap' : 'OpenSCAD_ColorCodeShape', 'MenuText': 'Color Shapes', 'ToolTip': 'Color Shapes by validity and type'}
class Edgestofaces:
def Activated(self):
from OpenSCAD2Dgeom import edgestofaces,Overlappingfaces
selection=FreeCADGui.Selection.getSelectionEx()
edges=[]
for selobj in selection:
edges.extend(selobj.Object.Shape.Edges)
Overlappingfaces(edgestofaces(edges,None)).makefeatures(FreeCAD.ActiveDocument)
for selobj in selection:
selobj.Object.ViewObject.hide()
FreeCAD.ActiveDocument.recompute()
def GetResources(self):
return {'Pixmap' : 'python', 'MenuText': 'EdgesToFaces', 'ToolTip': 'Convert Edges to Faces'}
class RefineShapeFeature:
def Activated(self):
import Part,OpenSCADFeatures
selection=FreeCADGui.Selection.getSelectionEx()
for selobj in selection:
#newobj=FreeCAD.ActiveDocument.addObject("Part::FeaturePython",'refine')
newobj=selobj.Document.addObject("Part::FeaturePython",'refine')
OpenSCADFeatures.RefineShape(newobj,selobj.Object)
OpenSCADFeatures.ViewProviderTree(newobj.ViewObject)
newobj.Label='refine_%s' % selobj.Object.Label
selobj.Object.ViewObject.hide()
FreeCAD.ActiveDocument.recompute()
def GetResources(self):
return {'Pixmap' : 'OpenSCAD_RefineShapeFeature', 'MenuText': \
'Refine Shape Feature', 'ToolTip': 'Create Refine Shape Feature'}
class ExpandPlacements:
'''This should aid interactive repair in the future
but currently it breaks extrusions, as axis, base and so on have to be
recalculated'''
def Activated(self):
import expandplacements
selobj=FreeCADGui.Selection.getSelectionEx()[0]
expandplacements.expandplacements(selobj.Object,FreeCAD.Placement())
FreeCAD.ActiveDocument.recompute()
def GetResources(self):
return {'Pixmap' : 'python', 'MenuText': 'Expand Placements', 'ToolTip': 'Expand all placements downwards the FeatureTree'}
class ReplaceObject:
def Activated(self):
import replaceobj
#objs=[selobj.Object for selobj in FreeCADGui.Selection.getSelectionEx()]
objs=FreeCADGui.Selection.getSelection()
if len(objs)==3:
replaceobj.replaceobjfromselection(objs)
else:
FreeCAD.Console.PrintError('please select 3 objects first')
def GetResources(self):
return {'Pixmap' : 'OpenSCAD_ReplaceObject', 'MenuText': \
'Replace Object', 'ToolTip': \
'Replace an object in the Feature Tree select old, new and parent object'}
class RemoveSubtree:
def Activated(self):
def addsubobjs(obj,toremoveset):
toremove.add(obj)
for subobj in obj.OutList:
addsubobjs(subobj,toremoveset)
import FreeCAD,FreeCADGui
objs=FreeCADGui.Selection.getSelection()
toremove=set()
for obj in objs:
addsubobjs(obj,toremove)
checkinlistcomplete =False
while not checkinlistcomplete:
for obj in toremove:
if (obj not in objs) and (frozenset(obj.InList) - toremove):
toremove.remove(obj)
break
else:
checkinlistcomplete = True
for obj in toremove:
obj.Document.removeObject(obj.Name)
def GetResources(self):
return {'Pixmap' : 'OpenSCAD_RemoveSubtree', 'MenuText': \
'Remove Objects and thier Children', 'ToolTip': \
'Removes the selected Objects and all Children that are not referenced from other objects'}
class AddSCADWidget(QtGui.QWidget):
def __init__(self,*args):
QtGui.QWidget.__init__(self,*args)
self.textEdit=QtGui.QTextEdit()
self.buttonadd = QtGui.QPushButton(u'Add')
self.buttonclear = QtGui.QPushButton(u'Clear')
self.checkboxmesh = QtGui.QCheckBox(u'as Mesh')
layouth=QtGui.QHBoxLayout()
layouth.addWidget(self.buttonadd)
layouth.addWidget(self.buttonclear)
layout= QtGui.QVBoxLayout()
layout.addLayout(layouth)
layout.addWidget(self.checkboxmesh)
layout.addWidget(self.textEdit)
self.setLayout(layout)
self.setWindowTitle(u'Add OpenSCAD Element')
self.textEdit.setText(u'cube();')
self.buttonclear.clicked.connect(self.textEdit.clear)
class AddSCADTask:
def __init__(self):
self.form = AddSCADWidget()
self.form.buttonadd.clicked.connect(self.addelement)
def getStandardButtons(self):
return int(QtGui.QDialogButtonBox.Close)
def isAllowedAlterSelection(self):
return True
def isAllowedAlterView(self):
return True
def isAllowedAlterDocument(self):
return True
def addelement(self):
scadstr=unicode(self.form.textEdit.toPlainText())
asmesh=self.form.checkboxmesh.checkState()
import OpenSCADUtils, os
extension= 'stl' if asmesh else 'csg'
tmpfilename=OpenSCADUtils.callopenscadstring(scadstr,extension)
if tmpfilename:
doc=FreeCAD.activeDocument() or FreeCAD.newDocument()
if asmesh:
import Mesh
Mesh.insert(tmpfilename,doc.Name)
else:
import importCSG
importCSG.insert(tmpfilename,doc.Name)
os.unlink(tmpfilename)
else:
FreeCAD.Console.PrintError('Running OpenSCAD failed\n')
class AddOpenSCADElement:
def Activated(self):
panel = AddSCADTask()
FreeCADGui.Control.showDialog(panel)
def GetResources(self):
return {'Pixmap' : 'python', 'MenuText': \
'Add OpenSCAD Element...', 'ToolTip': \
'Add an OpenSCAD Element by entering OpenSCAD Code and executing the OpenSCAD binary'}
FreeCADGui.addCommand('ColorCodeShape',ColorCodeShape())
FreeCADGui.addCommand('Edgestofaces',Edgestofaces())
FreeCADGui.addCommand('RefineShapeFeature',RefineShapeFeature())
FreeCADGui.addCommand('ExpandPlacements',ExpandPlacements())
FreeCADGui.addCommand('ReplaceObject',ReplaceObject())
FreeCADGui.addCommand('RemoveSubtree',RemoveSubtree())
FreeCADGui.addCommand('AddOpenSCADElement',AddOpenSCADElement())

View File

@ -0,0 +1,433 @@
#***************************************************************************
#* *
#* Copyright (c) 2012 Sebastian Hoogen <github@sebastianhoogen.de> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* 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 Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
__title__="FreeCAD OpenSCAD Workbench - Parametric Features"
__author__ = "Sebastian Hoogen"
__url__ = ["http://free-cad.sourceforge.net"]
'''
This Script includes python Features to represent OpenSCAD Operations
'''
class ViewProviderTree:
"A generic View Provider for Elements with Children"
def __init__(self, obj):
obj.Proxy = self
self.Object = obj.Object
def attach(self, obj):
self.Object = obj.Object
return
def updateData(self, fp, prop):
return
def getDisplayModes(self,obj):
modes=[]
return modes
def setDisplayMode(self,mode):
return mode
def onChanged(self, vp, prop):
return
def __getstate__(self):
# return {'ObjectName' : self.Object.Name}
return None
def __setstate__(self,state):
if state is not None:
try:
import FreeCAD
doc = FreeCAD.ActiveDocument #crap
self.Object = doc.getObject(state['ObjectName'])
except:
raise
# return None
def claimChildren(self):
objs = []
if hasattr(self.Object.Proxy,"Base"):
objs.append(self.Object.Proxy.Base)
if hasattr(self.Object,"Base"):
objs.append(self.Object.Base)
if hasattr(self.Object,"Objects"):
objs.extend(self.Object.Objects)
if hasattr(self.Object,"Components"):
objs.extend(self.Object.Components)
return objs
def getIcon(self):
#if self.Object.Proxy.__class__ == MatrixTransform:
if isinstance(self.Object.Proxy,MatrixTransform):
return """/* XPM */
static char * matrix_xpm[] = {
"16 16 3 1",
" c #0079FF",
". c #FFFFFF",
"+ c #000000",
" ......... .",
" ............. .",
" . . . . . .",
" . . . . . .",
" ............. .",
" . . . . . .",
" . . . . . .",
" ............. .",
" . . . . . .",
" . . . . . .",
" ............. .",
" ...........+. .",
" ..+..+..+..+. .",
" ............. .",
" ......... .",
"................"};"""
elif False:
return """/* XPM */
static char * qm_xpm[] = {
"16 16 37 1",
" c None",
". c #FFFFFF",
"+ c #CBE3FF",
"@ c #70B3FF",
"# c #3092FF",
"$ c #0D7FFF",
"% c #047BFF",
"& c #1885FF",
"* c #56A6FF",
"= c #CFE5FF",
"- c #0079FF",
"; c #067CFF",
"> c #B9DAFF",
", c #88C0FF",
"' c #CAE3FF",
") c #F2F8FF",
"! c #FAFCFF",
"~ c #CEE5FF",
"{ c #459DFF",
"] c #2D90FF",
"^ c #EEF6FF",
"/ c #077CFF",
"( c #B1D6FF",
"_ c #3494FF",
": c #90C4FF",
"< c #037AFF",
"[ c #BCDBFF",
"} c #6EB2FF",
"| c #087DFF",
"1 c #A8D1FF",
"2 c #8AC1FF",
"3 c #1C87FF",
"4 c #CCE4FF",
"5 c #1F89FF",
"6 c #CDE4FF",
"7 c #027AFF",
"8 c #FDFDFF",
"....+@#$%&*=....",
"....-------;>...",
"....#,')!~{-]...",
"..........^-/...",
"..........(-_...",
".........:/<[...",
"........}-|1....",
".......2-34.....",
".......5-6......",
".......7-8......",
".......--.......",
"................",
"................",
".......--.......",
".......--.......",
".......--......."};
"""
else:
return """/* XPM */
static char * openscadlogo_xpm[] = {
"16 16 33 1",
" c None",
". c #61320B",
"+ c #5D420B",
"@ c #4F4C09",
"# c #564930",
"$ c #754513",
"% c #815106",
"& c #666509",
"* c #875F55",
"= c #6E7000",
"- c #756A53",
"; c #717037",
"> c #946637",
", c #92710E",
"' c #797A0A",
") c #7C7720",
"! c #8A8603",
"~ c #88886F",
"{ c #AF8181",
"] c #999908",
"^ c #BB8D8D",
"/ c #AAAA00",
"( c #A9A880",
"_ c #B5B419",
": c #C1A9A9",
"< c #B1B19A",
"[ c #BEBE00",
"} c #B9B8B4",
"| c #CACC00",
"1 c #D4D4BC",
"2 c #DBD2D0",
"3 c #EEEEED",
"4 c #FDFFFC",
"4444444444444444",
"4444443113444444",
"4444<;']]!;<^^24",
"444(&@!]]]=&#^{3",
"44<']')@++)!&*{^",
"44)]/[|//[/]'@{{",
"42=/_|||||[]!&*{",
"4(&][|||||[/'@#}",
"3-..,|||||[)&&~4",
"^*$%.!|||[!+/](4",
"^{%%%._[[_&/[_14",
":{>%%.!//])_[_44",
"2{{%%+!]!!)]]344",
"4:{{#@&=&&@#3444",
"44224}~--~}44444",
"4444444444444444"};
"""
class MatrixTransform:
def __init__(self, obj,matrix=None,child=None):
obj.addProperty("App::PropertyLink","Base","Base",
"The base object that must be tranfsformed")
obj.addProperty("App::PropertyMatrix","Matrix","Matrix", "Transformation Matrix")
obj.Proxy = self
obj.Matrix = matrix
obj.Base = child
def onChanged(self, fp, prop):
"Do something when a property has changed"
pass
def updateProperty(self, fp, prop, value):
epsilon = 0.0001
if abs(getattr(fp, prop) - value) > epsilon:
setattr(fp, prop, value)
def execute(self, fp):
pass
if fp.Matrix and fp.Base:
sh=fp.Base.Shape#.copy()
m=sh.Placement.toMatrix().multiply(fp.Matrix)
fp.Shape = sh.transformGeometry(m)
#else:
#FreeCAD.Console.PrintMessage('base %s\nmat %s/n' % (fp.Base,fp.Matrix))
class ImportObject:
def __init__(self, obj,child=None):
obj.addProperty("App::PropertyLink","Base","Base",
"The base object that must be tranfsformed")
obj.Proxy = self
obj.Base = child
def onChanged(self, fp, prop):
"Do something when a property has changed"
pass
def execute(self, fp):
pass
# if fp.Base:
# fp.Shape = fp.Base.Shape.copy()
class RefineShape:
'''return a refined shape'''
def __init__(self, obj,child=None):
obj.addProperty("App::PropertyLink","Base","Base",
"The base object that must be refined")
obj.Proxy = self
obj.Base = child
def onChanged(self, fp, prop):
"Do something when a property has changed"
pass
def execute(self, fp):
if fp.Base and fp.Base.Shape.isValid():
sh=fp.Base.Shape.removeSplitter()
if sh.Placement.isNull():
fp.Shape=sh
else:
fp.Shape=sh.transformGeometry(sh.Placement.toMatrix())
class GetWire:
'''return the first wire from a given shape'''
def __init__(self, obj,child=None):
obj.addProperty("App::PropertyLink","Base","Base",
"The base object that wire must be extracted")
obj.Proxy = self
obj.Base = child
def onChanged(self, fp, prop):
"Do something when a property has changed"
pass
def execute(self, fp):
if fp.Base:
#fp.Shape=fp.Base.Shape.Wires[0]
fp.Shape=fp.Base.Shape.Wires[0].transformGeometry(\
fp.Base.Shape.Placement.toMatrix())
class Frustum:
def __init__(self, obj,r1=1,r2=2,n=3,h=4):
obj.addProperty("App::PropertyInteger","FacesNumber","Base","Number of faces")
obj.addProperty("App::PropertyDistance","Radius1","Base","Radius of lower the inscribed control circle")
obj.addProperty("App::PropertyDistance","Radius2","Base","Radius of upper the inscribed control circle")
obj.addProperty("App::PropertyDistance","Height","Base","Height of the Frustum")
obj.FacesNumber = n
obj.Radius1 = r1
obj.Radius2= r2
obj.Height= h
obj.Proxy = self
def execute(self, fp):
self.createGeometry(fp)
def onChanged(self, fp, prop):
if prop in ["FacesNumber","Radius1","Radius2","Height"]:
self.createGeometry(fp)
def createGeometry(self,fp):
if all((fp.Radius1,fp.Radius2,fp.FacesNumber,fp.Height)):
import math
import FreeCAD,Part
#from draftlibs import fcgeo
plm = fp.Placement
wires=[]
faces=[]
for ir,r in enumerate((fp.Radius1,fp.Radius2)):
angle = (math.pi*2)/fp.FacesNumber
pts = [FreeCAD.Vector(r,0,ir*fp.Height)]
for i in range(fp.FacesNumber-1):
ang = (i+1)*angle
pts.append(FreeCAD.Vector(\
r*math.cos(ang),r*math.sin(ang),ir*fp.Height))
pts.append(pts[0])
shape = Part.makePolygon(pts)
face = Part.Face(shape)
wires.append(shape)
faces.append(face)
#shellperi=Part.makeRuledSurface(*wires)
shellperi=Part.makeLoft(wires)
shell=Part.Shell(shellperi.Faces+faces)
fp.Shape = Part.Solid(shell)
fp.Placement = plm
class Twist:
def __init__(self, obj,child=None,h=1.0,angle=0.0):
obj.addProperty("App::PropertyLink","Base","Base",
"The base object that must be tranfsformed")
obj.addProperty("App::PropertyAngle","Angle","Base","Twist Angle in degrees") #degree or rad
obj.addProperty("App::PropertyDistance","Height","Base","Height of the Extrusion")
obj.Base = child
obj.Angle = angle
obj.Height = h
obj.Proxy = self
def execute(self, fp):
self.createGeometry(fp)
def onChanged(self, fp, prop):
pass
#if prop in ["Angle","Height"]:
# self.createGeometry(fp)
def createGeometry(self,fp):
import FreeCAD,Part,math
#tangle = -twist #openscad uses degrees clockwise
if fp.Base and fp.Angle and fp.Height and \
fp.Base.Shape.isValid():
#wire=fp.Base.Shape.Wires[0].transformGeometry(fp.Base.Placement.toMatrix())
solids=[]
for faceb in fp.Base.Shape.Faces:
#fp.Base.Shape.Faces[0].check()
#faceb=fp.Base.Shape.Faces[0]
#faceb=fp.Base.Shape.removeSplitter().Faces[0]
faceu=faceb.copy()
facetransform=FreeCAD.Matrix()
facetransform.rotateZ(math.radians(fp.Angle))
facetransform.move(FreeCAD.Vector(0,0,fp.Height))
faceu.transformShape(facetransform)
step = 2 + int(fp.Angle // 90) #resolution in z direction
zinc = fp.Height/(step-1.0)
angleinc = math.radians(fp.Angle)/(step-1.0)
spine = Part.makePolygon([(0,0,i*zinc) \
for i in range(step)])
auxspine = Part.makePolygon([(math.cos(i*angleinc),\
math.sin(i*angleinc),i*fp.Height/(step-1)) \
for i in range(step)])
faces=[faceb,faceu]
for wire in faceb.Wires:
pipeshell=Part.BRepOffsetAPI.MakePipeShell(spine)
pipeshell.setSpineSupport(spine)
pipeshell.add(wire)
pipeshell.setAuxiliarySpine(auxspine,True,False)
print pipeshell.getStatus()
assert(pipeshell.isReady())
#fp.Shape=pipeshell.makeSolid()
pipeshell.build()
faces.extend(pipeshell.shape().Faces)
try:
fullshell=Part.Shell(faces)
solid=Part.Solid(fullshell)
if solid.Volume < 0:
solid.reverse()
assert(solid.Volume >= 0)
solids.append(solid)
except:
solids.append(Part.Compound(faces))
fp.Shape=Part.Compound(solids)
class OffsetShape:
def __init__(self, obj,child=None,offset=1.0):
obj.addProperty("App::PropertyLink","Base","Base",
"The base object that must be tranfsformed")
obj.addProperty("App::PropertyDistance","Offset","Base","Offset outwards")
obj.Base = child
obj.Offset = offset
obj.Proxy = self
def execute(self, fp):
self.createGeometry(fp)
def onChanged(self, fp, prop):
pass
#if prop in ["Offset"]:
# self.createGeometry(fp)
def createGeometry(self,fp):
if fp.Base and fp.Offset:
fp.Shape=fp.Base.Shape.makeOffsetShape(self.Offset,1e-6)

View File

@ -0,0 +1,115 @@
#***************************************************************************
#* *
#* Copyright (c) 2012 Sebastian Hoogen <github@sebastianhoogen.de> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* 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 Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
__title__="FreeCAD OpenSCAD Workbench - Utility Fuctions"
__author__ = "Sebastian Hoogen"
__url__ = ["http://free-cad.sourceforge.net"]
'''
This Script includes various pyhton helper functions that are shared across
the module
'''
def callopenscad(inputfilename,outputfilename=None,outputext='csg',keepname=False):
'''call the open scad binary
returns the filename of the result (or None),
please delete the file afterwards'''
import FreeCAD,os,subprocess,tempfile,time
osfilename = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
GetString('openscadexecutable')
if osfilename and os.path.isfile(osfilename):
if not outputfilename:
dir1=tempfile.gettempdir()
if keepname:
outputfilename=os.path.join(dir1,'%s.%s' % (os.path.split(inputfilename)[1].rsplit('.',1)[0],outputext))
else:
outputfilename=os.path.join(dir1,'output-%d.%s' % (int(time.time()*10) % 1000000,outputext))
if subprocess.call([osfilename,'-o',outputfilename,inputfilename]) == 0:
return outputfilename
def callopenscadstring(scadstr,outputext='csg'):
'''create a tempfile and call the open scad binary
returns the filename of the result (or None),
please delete the file afterwards'''
import os,tempfile,time
dir1=tempfile.gettempdir()
inputfilename=os.path.join(dir1,'input-%d.scad' % (int(time.time()*10) % 1000000))
inputfile = open(inputfilename,'w')
inputfile.write(scadstr)
inputfile.close()
outputfilename = callopenscad(inputfilename,outputext=outputext,keepname=True)
os.unlink(inputfilename)
return outputfilename
def reverseimporttypes():
'''allows to search for supported filetypes by module'''
def getsetfromdict(dict1,index):
if index in dict1:
return dict1[index]
else:
set1=set()
dict1[index]=set1
return set1
importtypes={}
import FreeCAD
for key,value in FreeCAD.getImportType().iteritems():
if type(value) is str:
getsetfromdict(importtypes,value).add(key)
else:
for vitem in value:
getsetfromdict(importtypes,vitem).add(key)
return importtypes
def fcsubmatrix(m):
"""Extracts the 3x3 Submatrix from a freecad Matrix Object
as a list of row vectors"""
return [[m.A11,m.A12,m.A13],[m.A21,m.A22,m.A23],[m.A31,m.A32,m.A33]]
def multiplymat(l,r):
"""multiply matrices given as lists of row vectors"""
rt=zip(*r) #transpose r
mat=[]
for y in range(len(rt)):
mline=[]
for x in range(len(l)):
mline.append(sum([le*re for le,re in zip(l[y],rt[x])]))
mat.append(mline)
return mat
def isorthogonal(submatrix,precision=4):
"""checking if 3x3 Matrix is ortogonal (M*Transp(M)==I)"""
prod=multiplymat(submatrix,zip(*submatrix))
return [[round(f,precision) for f in line] for line in prod]==[[1,0,0],[0,1,0],[0,0,1]]
def detsubmatrix(s):
"""get the determinant of a 3x3 Matrix given as list of row vectors"""
return s[0][0]*s[1][1]*s[2][2]+s[0][1]*s[1][2]*s[2][0]+s[0][2]*s[1][0]*s[2][1]\
-s[2][0]*s[1][1]*s[0][2]-s[2][1]*s[1][2]*s[0][0]-s[2][2]*s[1][0]*s[0][1]
def isspecialorthogonalpython(submat,precision=4):
return isorthogonal(submat,precision) and round(detsubmatrix(submat),precision)==1
def isspecialorthogonal(mat,precision=4):
return abs(mat.submatrix(3).isOrthogonal(10**(-precision))-1.0) < 10**(-precision) and \
abs(mat.submatrix(3).determinant()-1.0) < 10**(-precision)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,10 @@
<RCC>
<qresource>
<file>icons/preferences-openscad.svg</file>
<file>icons/OpenSCAD_ColorCodeShape.svg</file>
<file>icons/OpenSCAD_RefineShapeFeature.svg</file>
<file>icons/OpenSCAD_ReplaceObject.svg</file>
<file>icons/OpenSCAD_RemoveSubtree.svg</file>
<file>ui/openscadprefs-base.ui</file>
</qresource>
</RCC>

View File

@ -0,0 +1,136 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2980"
sodipodi:version="0.32"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="Tree_Part.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
version="1.1">
<defs
id="defs2982">
<linearGradient
id="linearGradient3864">
<stop
id="stop3866"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3868"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective2988" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.5"
inkscape:cx="-2.8181818"
inkscape:cy="32"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1366"
inkscape:window-height="706"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata2985">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<g
id="g3845"
transform="matrix(0.58306143,0,0,0.58306143,-12.65478,-77.438442)"
style="fill:#008000;stroke-width:3.45138597;stroke-miterlimit:4;stroke-dasharray:none">
<path
style="fill:#008000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.45138597;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 96.18444,141.44968 -19.81441,7.17921 29.4349,3.97209 -0.72357,36.12665 17.25881,-10.94388 0.57587,-34.38485 -26.7316,-1.94922 z"
id="path3823"
sodipodi:nodetypes="ccccccc"
inkscape:connector-curvature="0" />
<path
style="fill:#008000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.45138597;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 75.57941,148.6589 30.70884,3.36562 0,36.47719 -31.12383,-5.06479 0.41499,-34.77802 z"
id="path3825"
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0" />
<path
style="fill:#008000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.45138597;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 106.05435,152.06948 16.72598,-8.4088"
id="path3827"
inkscape:connector-curvature="0" />
</g>
<path
sodipodi:type="arc"
style="fill:#ffff00;stroke:#000000;stroke-width:1.61374438;stroke-miterlimit:4;stroke-dasharray:none"
id="path3759"
sodipodi:cx="48.090908"
sodipodi:cy="27.636364"
sodipodi:rx="7.5454545"
sodipodi:ry="4.3636365"
d="m 55.636363,27.636364 a 7.5454545,4.3636365 0 1 1 -15.090909,0 7.5454545,4.3636365 0 1 1 15.090909,0 z"
transform="matrix(1.3960176,0.45054535,-0.42447321,1.4817643,-39.495773,-47.031115)" />
<g
style="fill:#ff0000;stroke-width:4.24927664;stroke-miterlimit:4;stroke-dasharray:none"
transform="matrix(0.53484593,0,0,0.54597261,-35.993942,-44.385404)"
id="g3761">
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
id="path3765"
d="m 75.57941,148.6589 30.70884,3.36562 0,36.47719 -31.12383,-5.06479 0.41499,-34.77802 z"
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.24927664;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
inkscape:connector-curvature="0"
id="path3767"
d="m 106.05435,152.06948 16.72598,-8.4088"
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4.24927664;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
<g
style="fill:#008000;stroke-width:3.45138597;stroke-miterlimit:4;stroke-dasharray:none"
transform="matrix(0.58306143,0,0,0.58306143,-13.200235,-47.256624)"
id="g3769">
<path
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc"
id="path3771"
d="m 92.130602,151.42836 -17.943408,16.22238 32.553236,12.70344 9.62711,-18.24526 z"
style="fill:#ffff00;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:3.45138597000000000;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -0,0 +1,402 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:i="http://ns.adobe.com/AdobeIllustrator/10.0/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg2980"
sodipodi:version="0.32"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="Tree_Part.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
version="1.1">
<defs
id="defs2982">
<linearGradient
id="linearGradient3864">
<stop
id="stop3866"
offset="0"
style="stop-color:#71b2f8;stop-opacity:1;" />
<stop
id="stop3868"
offset="1"
style="stop-color:#002795;stop-opacity:1;" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3864"
id="radialGradient3850"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.6028459,1.0471639,-1.9794021,1.1395295,127.9588,-74.456907)"
cx="51.328892"
cy="31.074146"
fx="51.328892"
fy="31.074146"
r="19.571428" />
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective2988" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.6160967"
inkscape:cx="16.57935"
inkscape:cy="-46.748843"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="911"
inkscape:window-height="684"
inkscape:window-x="177"
inkscape:window-y="19"
inkscape:window-maximized="0" />
<metadata
id="metadata2985">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<g
id="g3845"
transform="matrix(0.49611692,0,0,0.49611692,-6.2336536,-40.712112)">
<path
style="fill:#0034ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 96.18444,141.44968 -19.81441,7.17921 29.4349,3.97209 -0.72357,36.12665 17.25881,-10.94388 0.57587,-34.38485 -26.7316,-1.94922 z"
id="path3823"
sodipodi:nodetypes="ccccccc"
inkscape:connector-curvature="0" />
<path
style="fill:#0034ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 75.57941,148.6589 30.70884,3.36562 0,36.47719 -31.12383,-5.06479 0.41499,-34.77802 z"
id="path3825"
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0" />
<path
style="fill:url(#radialGradient3850);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 106.05435,152.06948 16.72598,-8.4088"
id="path3827"
inkscape:connector-curvature="0" />
</g>
<g
style="stroke:#000000;stroke-width:1.97789347;stroke-miterlimit:10;stroke-dasharray:none;overflow:visible"
id="g3981"
transform="matrix(0.34530072,-0.0459555,0.0459555,0.34530072,-43.228956,-7.238134)">
<g
id="Layer_1"
i:rgbTrio="#4F008000FFFF"
i:layer="yes"
i:dimmedPercent="50"
style="stroke:#000000;stroke-width:1.97789347;stroke-miterlimit:10;stroke-dasharray:none">
<g
id="g3891"
transform="matrix(-0.32435514,0.13754467,-0.13754467,-0.32435514,270.902,139.40876)"
style="stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none">
<path
id="path3893"
stroke-miterlimit="10"
i:knockout="Off"
d="m 348.426,49.113 c 15.516,-4.14 30.348,-8.028 45.469,-11.664 15.084,-3.852 34.055,-8.46 44.135,-10.332 9.938,-1.872 10.621,-1.116 15.084,-0.36 4.32,0.648 8.244,3.492 10.801,4.932 2.34,1.296 3.527,0.648 3.564,3.744 -0.18,3.096 -1.477,11.34 -3.889,15.156 -2.447,3.708 -6.553,5.364 -10.367,7.488 -3.889,2.088 -7.668,3.132 -12.602,5.076 -5.039,1.872 -4.248,0.72 -17.207,5.976 -13.213,5.148 -44.461,18.072 -60.264,24.876 -15.732,6.66 -22.932,9.36 -33.553,15.192 -10.764,5.795 -21.889,15.588 -30.313,19.26 -8.459,3.492 -13.859,1.477 -19.943,2.16 -6.084,0.611 -8.711,-0.145 -16.775,1.764 -8.316,1.871 -22.248,5.939 -32.221,9.432 -10.116,3.492 -20.988,8.172 -27.216,10.98 -6.192,2.629 -9.432,4.213 -9.72,4.896 -0.18,3.131 -2.016,6.119 -5.508,9.107 -3.6,2.988 -6.516,4.176 -15.552,8.713 -9.216,4.428 -26.964,12.096 -38.952,17.855 -12.168,5.725 -23.544,11.557 -32.76,16.127 -9.144,4.43 -16.236,7.957 -21.816,10.584 -5.688,2.592 -7.596,4.43 -11.952,4.969 -4.392,0.359 -8.1,0.324 -14.616,-2.629 -6.732,-3.131 -17.388,-10.367 -25.02,-15.443 -7.704,-5.111 -16.2,-10.512 -20.484,-14.832 -4.248,-4.32 -3.852,-6.588 -4.932,-11.088 -1.224,-4.68 -2.52,-11.988 -1.8,-16.236 0.612,-4.355 2.556,-6.984 5.976,-9.395 3.492,-2.412 8.064,-4.141 14.4,-5.041 0.432,-9.791 0.18,-19.477 -0.612,-29.34 -0.972,-9.972 -3.24,-21.636 -4.428,-29.736 -1.152,-8.172 -2.052,-14.184 -2.556,-18.576 1.8,-4.104 4.068,-6.804 6.804,-8.532 2.664,-1.764 6.336,-1.656 9.36,-1.62 2.916,-0.072 5.724,0.468 8.28,1.476 1.8,-4.788 4.608,-7.56 8.568,-8.316 3.852,-0.756 8.856,0.396 14.868,3.6 1.836,-3.96 4.5,-6.084 7.992,-6.516 3.492,-0.432 7.704,0.828 12.78,3.924 1.548,-3.024 3.708,-4.788 6.624,-5.148 2.88,-0.396 6.48,0.468 10.8,2.736 1.908,-2.952 4.428,-4.536 7.56,-4.896 3.132,-0.432 6.732,0.504 11.088,2.664 0.18,-1.44 1.224,-2.412 3.672,-2.52 2.268,-0.216 7.128,0.612 10.296,1.368 3.096,0.612 4.644,1.368 8.388,2.664 3.6,1.26 9.252,3.096 13.5,4.932 4.248,1.728 9.288,2.556 11.628,5.4 2.196,2.772 1.764,4.788 1.764,11.34 -0.036,6.588 -1.8,20.268 -1.836,27.576 -0.18,7.236 0.18,12.204 0.936,15.444 13.068,-4.32 25.056,-8.748 36.504,-13.824 11.412,-5.148 23.688,-11.556 31.536,-16.308 7.596,-4.896 9.035,-9.432 14.473,-12.348 5.398,-2.916 10.295,-3.24 17.748,-5.148 7.379,-2.052 16.416,-4.212 26.279,-6.768 9.684,-2.772 22.393,-6.372 31.859,-8.856 9.398,-2.483 17.318,-4.463 24.158,-5.939 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3895"
stroke-miterlimit="10"
i:knockout="Off"
d="m 259.326,74.709 c 13.104,-2.7 26.855,-5.94 42.084,-10.008 15.191,-4.212 31.68,-9.684 48.348,-14.184 16.633,-4.536 34.92,-8.712 50.4,-12.384 15.264,-3.816 31.463,-8.496 40.824,-9.72 9.18,-1.26 10.764,0.936 14.471,2.412 3.637,1.368 9.721,3.996 7.416,6.084 -2.447,1.944 -9.684,3.096 -21.852,6.372 -12.348,3.24 -34.523,8.856 -51.119,13.536 -16.705,4.68 -31.393,9.108 -47.953,14.4 -16.631,5.22 -38.195,11.988 -50.939,16.812 -12.744,4.752 -12.707,6.084 -25.164,11.772 -12.707,5.58 -35.82,15.768 -49.752,21.816 -13.932,6.047 -25.38,12.24 -32.832,14.004 -7.488,1.477 -8.928,-2.305 -11.664,-4.393 -2.916,-2.123 -4.536,-4.68 -5.256,-7.955 11.484,-4.141 22.572,-8.605 33.66,-13.537 11.052,-5.004 24.3,-10.944 32.364,-15.876 7.884,-5.04 10.621,-10.404 15.156,-13.608 4.5,-3.203 8.388,-5.003 11.808,-5.543 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#0034ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3897"
i:knockout="Off"
d="m 170.154,126.621 c 15.156,-6.553 27.72,-12.133 38.376,-17.028 10.548,-4.824 18.432,-8.46 24.768,-12.024 6.155,-3.816 8.243,-9.036 12.384,-9.972 4.141,-0.9 9.9,2.916 12.023,4.644 1.98,1.62 2.305,3.708 0,5.292 -2.52,1.332 -7.848,1.584 -14.328,3.528 -6.551,2.052 -16.307,5.616 -24.263,8.604 -7.992,2.772 -15.336,5.292 -23.292,8.677 -8.1,3.42 -16.092,7.199 -24.516,11.627 -0.36,-1.115 -0.756,-2.233 -1.152,-3.348 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#0034ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3899"
stroke-miterlimit="10"
i:knockout="Off"
d="m 463.158,40.797 c 0.828,3.924 -0.396,7.236 -4.068,10.224 -3.744,2.844 -10.332,4.356 -17.676,7.308 -7.488,2.916 -16.057,6.048 -26.244,10.08 -10.332,3.888 -24.191,9.072 -34.633,13.464 -10.439,4.32 -17.711,7.524 -27.646,12.384 -10.152,4.716 -24.084,10.836 -32.365,15.876 -8.279,4.896 -11.736,10.908 -17.172,13.465 -5.652,2.412 -9.287,0.863 -15.768,1.584 -6.553,0.684 -12.527,0.252 -22.787,2.592 -10.514,2.268 -28.945,7.523 -38.917,10.908 -9.972,3.311 -13.824,6.119 -20.16,8.818 -6.336,2.594 -12.024,4.896 -17.46,6.912 0.756,4.141 -0.216,7.668 -3.312,10.729 -3.096,2.953 -8.1,3.996 -15.012,7.309 -7.128,3.203 -16.416,7.236 -27.036,12.24 -10.764,4.967 -25.92,12.492 -36.576,17.568 -10.764,5.074 -19.692,9.287 -27.072,12.348 -7.416,2.879 -9.756,6.443 -16.884,5.146 -7.236,-1.367 -17.568,-8.1 -25.992,-13.104 -8.676,-5.111 -19.296,-10.332 -24.624,-17.027 -5.22,-6.805 -8.604,-20.34 -6.66,-23.111 1.908,-2.773 10.512,2.916 17.748,6.516 7.056,3.42 17.496,11.592 24.66,14.4 7.056,2.844 10.548,3.6 17.712,2.268 7.056,-1.549 13.788,-6.336 24.66,-11.016 10.944,-4.789 27.108,-11.412 40.032,-17.102 12.816,-5.76 26.748,-13.428 36.108,-16.775 9.216,-3.313 15.336,-4.248 18.756,-2.951 4.068,-3.133 12.384,-7.561 25.092,-13.428 12.744,-6.086 36.792,-15.408 50.759,-21.709 13.896,-6.264 19.477,-10.728 32.148,-15.48 12.529,-4.896 24.625,-7.632 42.949,-13.212 18.359,-5.688 44.818,-14.004 66.311,-20.232 21.385,-6.336 41.438,-11.772 61.129,-16.992 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#0034ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3901"
i:knockout="Off"
d="m 52.758,212.877 c -4.104,-0.973 -20.844,-10.656 -27.432,-15.049 -6.624,-4.391 -9,-6.912 -11.7,-11.303 -2.844,-4.609 -6.228,-13.861 -4.464,-15.408 1.908,-1.404 11.52,2.592 15.516,6.732 3.888,4.066 3.348,12.887 7.56,17.279 4.068,4.176 13.284,5.111 16.812,8.1 3.384,3.024 7.632,10.405 3.708,9.649 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#0034ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3903"
i:knockout="Off"
d="m 263.395,124.82 c -8.137,1.477 -17.354,4.105 -28.08,7.885 -10.765,3.816 -27.792,11.195 -35.964,14.795 -8.28,3.422 -12.456,5.365 -13.068,5.832 1.08,2.484 0.684,4.752 -1.188,7.236 -1.944,2.34 -3.708,3.961 -10.152,7.092 -6.696,3.168 -27.36,12.709 -28.8,11.701 -1.296,-1.332 16.992,-14.076 20.844,-18.648 3.636,-4.535 -0.756,-6.48 1.26,-8.207 1.872,-1.729 8.712,-0.9 10.368,-2.232 1.62,-1.477 1.44,-3.385 -0.54,-6.121 1.08,-0.143 2.16,-0.072 3.672,0.072 1.404,0.145 2.952,0.359 4.788,0.793 1.908,-2.844 6.048,-5.904 12.564,-9.324 6.552,-3.457 17.64,-7.525 26.316,-11.088 8.568,-3.637 20.916,-10.477 24.84,-10.404 3.779,0.035 -4.32,8.496 -2.088,10.404 2.231,1.726 7.163,1.798 15.228,0.214 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#0034ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3905"
i:knockout="Off"
d="m 461.43,42.777 c 2.447,-0.288 -0.576,3.636 -2.268,5.508 -1.801,1.692 -1.656,2.52 -7.74,4.86 -6.229,2.268 -20.557,5.76 -28.549,8.676 -7.955,2.772 -8.891,4.032 -18.756,8.244 -10.043,4.176 -34.02,15.156 -40.391,16.848 -6.264,1.476 -3.564,-3.744 2.807,-7.308 6.373,-3.672 25.453,-10.224 34.885,-13.824 9.145,-3.6 13.535,-5.148 20.557,-7.704 7.02,-2.736 15.156,-5.4 21.779,-7.992 6.623,-2.628 15.012,-7.056 17.676,-7.308 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#0034ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3907"
i:knockout="Off"
d="m 442.781,39.465 c 6.625,-1.368 7.525,-0.756 9.109,-1.908 1.512,-1.296 2.916,-4.536 0.035,-5.292 -3.096,-0.792 -6.912,-1.368 -17.965,0.756 -11.268,2.088 -44.314,9.684 -48.311,11.556 -3.744,1.62 20.447,-1.332 25.092,-1.224 4.428,0.072 -3.961,2.52 1.477,1.944 5.471,-0.792 23.761,-4.5 30.563,-5.832 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#0034ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3909"
stroke-miterlimit="10"
i:knockout="Off"
d="m 77.454,83.061 c 0.36,1.512 -7.776,3.168 -10.656,3.312 -2.988,0.144 -6.228,-0.756 -6.66,-2.304 -0.432,-1.656 0.936,-7.38 3.96,-7.452 2.916,-0.18 12.78,4.752 13.356,6.444 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3911"
stroke-miterlimit="10"
i:knockout="Off"
d="m 38.43,67.185 c -0.108,2.016 -6.588,9.072 -10.152,9.648 -3.564,0.432 -10.476,-4.788 -10.584,-6.768 -0.144,-2.16 6.48,-4.572 10.008,-5.148 3.456,-0.504 10.548,0.252 10.728,2.268 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3913"
stroke-miterlimit="10"
i:knockout="Off"
d="m 57.33,75.429 c 0.432,2.304 -3.132,9.036 -6.372,9.108 -3.42,0 -12.708,-6.696 -13.284,-8.964 -0.504,-2.448 6.588,-5.184 10.008,-5.148 3.348,-0.036 8.964,2.556 9.648,5.004 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3915"
stroke-miterlimit="10"
i:knockout="Off"
d="m 58.122,61.281 c 0.972,1.944 -0.756,7.2 -3.384,7.992 -2.736,0.612 -11.772,-2.088 -12.672,-3.924 -1.044,-1.944 4.104,-6.768 6.84,-7.38 2.664,-0.648 8.136,1.332 9.216,3.312 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3917"
stroke-miterlimit="10"
i:knockout="Off"
d="m 93.654,79.605 c 0.828,1.476 -2.664,3.24 -4.932,3.348 -2.448,-0.036 -7.992,-1.692 -8.856,-3.168 -0.864,-1.512 1.224,-5.796 3.636,-5.76 2.34,0.072 9.108,3.924 10.152,5.58 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3919"
stroke-miterlimit="10"
i:knockout="Off"
d="m 110.43,75.681 c 0.072,1.548 -5.4,4.752 -7.812,4.752 -2.7,-0.288 -6.804,-4.176 -7.02,-5.688 -0.144,-1.548 3.888,-3.528 6.408,-3.312 2.412,0.144 8.28,2.664 8.424,4.248 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3921"
stroke-miterlimit="10"
i:knockout="Off"
d="m 125.91,73.809 c 0,1.116 -4.716,3.852 -6.948,3.888 -2.376,-0.108 -6.66,-2.628 -6.732,-3.816 -0.108,-1.26 4.104,-3.276 6.408,-3.276 2.304,0.072 7.092,1.836 7.272,3.204 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3923"
stroke-miterlimit="10"
i:knockout="Off"
d="m 142.434,70.317 c -0.036,1.26 -4.14,4.824 -6.48,5.004 -2.448,0.036 -7.092,-3.096 -7.272,-4.392 -0.18,-1.404 4.104,-3.204 6.444,-3.312 2.304,-0.108 7.056,1.296 7.308,2.7 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3925"
stroke-miterlimit="10"
i:knockout="Off"
d="m 162.918,67.689 c 0.18,1.476 -4.716,3.6 -7.452,3.888 -2.844,0.108 -8.424,-1.26 -8.82,-2.736 -0.36,-1.584 4.032,-6.084 6.804,-6.264 2.808,-0.144 8.928,3.456 9.468,5.112 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3927"
stroke-miterlimit="10"
i:knockout="Off"
d="m 164.538,70.389 c -3.744,2.304 -6.948,3.492 -10.008,3.708 -3.024,0.144 -5.796,-0.684 -8.172,-2.628 0.18,9.504 0.468,19.44 0.72,30.636 0.252,11.088 0.54,22.825 0.864,35.639 3.816,1.908 6.912,2.377 9.684,1.549 2.7,-0.863 4.788,-3.168 6.408,-6.768 -0.828,-11.736 -1.188,-22.428 -1.08,-32.904 0.036,-10.512 0.612,-19.98 1.584,-29.232 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3929"
stroke-miterlimit="10"
i:knockout="Off"
d="m 144.09,72.801 c -1.98,2.376 -4.248,3.816 -6.66,4.32 -2.448,0.468 -5.184,0 -8.064,-1.404 -1.044,9.36 -1.62,19.368 -1.728,30.456 -0.18,11.052 0.108,22.788 0.864,35.604 3.816,1.871 6.912,2.447 9.684,1.584 2.7,-0.936 4.788,-3.061 6.408,-6.768 0.432,-9.721 0.612,-19.656 0.54,-30.349 -0.144,-10.799 -0.396,-21.671 -1.044,-33.443 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3931"
stroke-miterlimit="10"
i:knockout="Off"
d="m 126.306,77.013 c -1.44,1.512 -3.132,2.448 -5.076,2.664 -2.016,0.216 -4.176,-0.252 -6.624,-1.404 -1.584,11.664 -2.772,23.076 -3.42,35.064 -0.72,11.952 -0.972,23.651 -0.756,35.929 3.744,1.979 6.984,2.375 9.72,1.584 2.7,-0.9 4.788,-3.168 6.408,-6.805 -0.648,-10.871 -1.152,-21.6 -1.116,-32.868 -0.072,-11.304 0.216,-22.464 0.864,-34.164 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3933"
stroke-miterlimit="10"
i:knockout="Off"
d="m 111.834,79.461 c -2.268,1.692 -4.536,2.556 -6.948,2.988 -2.448,0.36 -4.968,0.036 -7.56,-0.828 -1.8,14.076 -2.772,27.288 -3.06,40.176 -0.36,12.889 0.108,24.805 1.26,36.432 2.592,1.08 4.86,1.008 7.236,-0.072 2.268,-1.15 4.356,-3.455 6.408,-6.768 -1.188,-10.008 -1.548,-20.879 -1.08,-32.904 0.468,-12.096 1.62,-24.912 3.744,-39.024 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3935"
stroke-miterlimit="10"
i:knockout="Off"
d="m 95.13,81.441 c -0.648,2.34 -2.124,3.528 -4.428,3.96 -2.376,0.36 -5.508,-0.288 -9.54,-1.8 -0.684,11.016 -1.188,22.896 -1.512,36.215 -0.36,13.357 -0.54,27.469 -0.54,43.164 2.988,1.908 5.76,2.449 8.172,1.764 2.376,-0.756 4.536,-2.879 6.336,-6.227 -0.828,-10.117 -1.044,-21.42 -0.792,-34.309 0.252,-13.031 0.972,-26.963 2.304,-42.767 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3937"
stroke-miterlimit="10"
i:knockout="Off"
d="m 78.714,86.193 c -3.96,1.908 -7.488,2.7 -11.016,2.52 -3.528,-0.216 -6.768,-1.44 -9.864,-3.672 0.468,18.792 0.792,34.848 1.116,48.996 0.324,14.111 0.504,25.488 0.684,34.955 4.32,2.773 7.848,3.744 10.728,3.205 2.88,-0.648 4.968,-2.809 6.408,-6.768 -1.08,-8.893 -1.44,-19.584 -1.08,-32.904 0.288,-13.32 1.296,-28.584 3.024,-46.332 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3939"
stroke-miterlimit="10"
i:knockout="Off"
d="m 53.082,87.633 c -3.852,0.468 -7.092,0.252 -9.864,-0.792 -2.808,-1.152 -5.004,-2.844 -6.732,-5.508 1.404,17.64 2.484,32.761 3.096,46.188 0.612,13.355 0.9,24.336 0.72,33.516 2.412,4.031 4.86,6.408 7.488,7.127 2.628,0.648 5.184,-0.215 8.064,-2.879 C 55.458,152.9 55.026,140.59 54.558,127.521 54.09,114.488 53.55,101.385 53.082,87.633 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3941"
stroke-miterlimit="10"
i:knockout="Off"
d="m 32.202,79.857 c -2.844,1.8 -5.472,2.304 -8.172,1.116 -2.736,-1.188 -5.256,-3.78 -7.848,-7.92 2.808,18.576 4.536,34.272 5.724,48.061 1.08,13.68 1.26,24.66 0.684,33.516 2.412,3.961 4.86,6.191 7.488,7.129 2.556,0.826 5.148,0.07 7.92,-2.125 C 37.602,148.222 36.81,136.054 35.91,122.698 34.974,109.269 33.642,95.265 32.202,79.857 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3943"
stroke-miterlimit="10"
i:knockout="Off"
d="m 80.01,59.841 c 0.72,1.332 -2.124,3.78 -4.788,4.572 -2.844,0.72 -10.44,0.684 -11.268,-0.504 -0.648,-1.404 3.996,-6.732 6.84,-7.38 2.736,-0.684 8.352,1.908 9.216,3.312 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3945"
stroke-miterlimit="10"
i:knockout="Off"
d="m 98.082,56.097 c 0.612,1.584 -2.232,5.472 -4.932,6.228 -2.808,0.612 -10.404,-0.612 -11.124,-2.16 -0.648,-1.656 3.996,-6.696 6.84,-7.344 2.736,-0.72 8.352,1.62 9.216,3.276 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3947"
stroke-miterlimit="10"
i:knockout="Off"
d="m 116.298,55.053 c 0.396,1.656 -4.104,5.472 -6.696,6.3 -2.808,0.648 -8.892,-0.648 -9.36,-2.232 -0.396,-1.728 3.96,-6.732 6.804,-7.38 2.7,-0.684 8.748,1.584 9.252,3.312 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3949"
stroke-miterlimit="10"
i:knockout="Off"
d="m 135.126,54.369 c 0.54,1.224 -2.628,4.104 -5.472,4.752 -2.844,0.36 -10.8,-0.54 -11.376,-1.728 -0.54,-1.332 4.644,-4.896 7.632,-5.436 2.772,-0.576 8.568,1.152 9.216,2.412 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3951"
stroke-miterlimit="10"
i:knockout="Off"
d="m 77.742,70.353 c 1.008,1.728 -3.564,5.76 -5.94,6.264 -2.484,0.288 -7.884,-2.232 -8.856,-3.96 -1.044,-1.836 0,-6.156 2.556,-6.552 2.448,-0.324 11.16,2.34 12.24,4.248 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3953"
stroke-miterlimit="10"
i:knockout="Off"
d="m 95.85,67.401 c 1.008,1.548 -2.052,5.256 -4.32,5.724 -2.304,0.252 -7.848,-2.232 -9,-3.708 -1.116,-1.512 -0.288,-5.04 2.088,-5.328 2.196,-0.36 10.008,1.764 11.232,3.312 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3955"
stroke-miterlimit="10"
i:knockout="Off"
d="m 113.13,66.933 c 0.972,1.404 -3.096,3.492 -5.4,3.636 -2.448,0.036 -7.344,-1.692 -8.46,-3.096 -1.116,-1.476 -0.288,-5.256 2.088,-5.364 2.232,-0.18 10.692,3.42 11.772,4.824 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3957"
stroke-miterlimit="10"
i:knockout="Off"
d="m 128.286,63.729 c 0.792,1.476 -2.808,4.716 -4.824,5.148 -2.16,0.252 -6.336,-1.8 -7.272,-3.24 -0.936,-1.548 0.036,-5.004 2.088,-5.364 1.98,-0.324 9.072,1.944 10.008,3.456 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3959"
stroke-miterlimit="10"
i:knockout="Off"
d="m 148.338,60.057 c 0.864,1.476 -2.916,4.716 -4.824,5.148 -2.16,0.252 -6.372,-1.8 -7.236,-3.24 -0.828,-1.548 -0.036,-5.004 2.088,-5.364 2.016,-0.324 9,1.944 9.972,3.456 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#edecf1;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
<path
id="path3961"
i:knockout="Off"
d="m 168.282,133.102 c -1.116,3.455 -3.312,5.76 -6.876,7.055 -3.564,1.225 -8.352,1.441 -14.364,0.504 -2.664,2.197 -5.4,3.744 -8.208,4.5 -2.844,0.756 -5.76,0.865 -8.748,0.182 -0.396,3.059 -2.124,5.184 -5.184,6.658 -3.168,1.441 -7.416,1.945 -13.176,1.801 -1.296,2.916 -3.024,5.111 -5.724,6.588 -2.628,1.369 -6.048,2.016 -10.116,1.943 -1.728,3.602 -3.924,5.725 -6.588,6.66 -2.7,0.9 -5.868,0.324 -9.504,-1.439 -4.392,5.148 -8.532,7.848 -12.276,8.389 -3.888,0.432 -7.236,-1.297 -10.476,-5.473 -4.86,1.297 -8.64,1.439 -11.88,0.576 -3.204,-0.936 -5.616,-3.023 -7.236,-6.119 -5.22,1.402 -9.396,1.295 -12.672,-0.469 -3.312,-1.764 -5.616,-5.076 -6.912,-9.936 -3.132,-0.252 -5.364,0.072 -6.696,1.115 -1.44,0.936 -3.744,2.771 -1.512,4.824 2.268,1.871 7.38,3.24 14.652,7.164 7.092,3.959 18.216,14.615 27.828,16.271 9.396,1.439 15.984,-2.303 28.944,-6.912 13.068,-4.643 35.676,-15.084 48.384,-20.484 12.384,-5.398 18.432,-9.252 26.064,-12.131 7.56,-3.096 13.968,-4.896 19.368,-5.832 -2.268,-0.793 -3.924,-1.584 -5.112,-2.52 -1.224,-0.937 -1.836,-1.908 -1.98,-2.915 z"
clip-rule="evenodd"
inkscape:connector-curvature="0"
style="fill:#000080;fill-rule:evenodd;stroke:#000000;stroke-width:5.61401606;stroke-miterlimit:10;stroke-dasharray:none" />
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1,461 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg3612"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="OpenSCAD_ReplaceObject.svg">
<defs
id="defs3614">
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Mend"
style="overflow:visible;">
<path
id="path3835"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;"
transform="scale(0.4) rotate(180) translate(10,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mstart"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Mstart"
style="overflow:visible">
<path
id="path3832"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt"
transform="scale(0.4) translate(10,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Lstart"
style="overflow:visible">
<path
id="path3826"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt"
transform="scale(0.8) translate(12.5,0)" />
</marker>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective3620" />
<inkscape:perspective
id="perspective3588"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3692"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
id="linearGradient3144-6">
<stop
id="stop3146-9"
style="stop-color:#ffffff;stop-opacity:1"
offset="0" />
<stop
id="stop3148-2"
style="stop-color:#ffffff;stop-opacity:0"
offset="1" />
</linearGradient>
<linearGradient
id="linearGradient3701">
<stop
id="stop3703"
style="stop-color:#ffffff;stop-opacity:1"
offset="0" />
<stop
id="stop3705"
style="stop-color:#ffffff;stop-opacity:0"
offset="1" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3144-6"
id="radialGradient3688"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.6985294,0,202.82863)"
cx="225.26402"
cy="672.79736"
fx="225.26402"
fy="672.79736"
r="34.345188" />
<linearGradient
id="linearGradient3708">
<stop
id="stop3710"
style="stop-color:#ffffff;stop-opacity:1"
offset="0" />
<stop
id="stop3712"
style="stop-color:#ffffff;stop-opacity:0"
offset="1" />
</linearGradient>
<inkscape:perspective
id="perspective3805"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
id="linearGradient3864-0-0">
<stop
id="stop3866-5-7"
offset="0"
style="stop-color:#0619c0;stop-opacity:1;" />
<stop
id="stop3868-7-6"
offset="1"
style="stop-color:#379cfb;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient3377">
<stop
id="stop3379"
offset="0"
style="stop-color:#ffaa00;stop-opacity:1;" />
<stop
id="stop3381"
offset="1"
style="stop-color:#faff2b;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient3864-0">
<stop
id="stop3866-5"
offset="0"
style="stop-color:#0619c0;stop-opacity:1;" />
<stop
id="stop3868-7"
offset="1"
style="stop-color:#379cfb;stop-opacity:1;" />
</linearGradient>
<inkscape:perspective
id="perspective3902"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
id="linearGradient5048">
<stop
style="stop-color:black;stop-opacity:0;"
offset="0"
id="stop5050" />
<stop
id="stop5056"
offset="0.5"
style="stop-color:black;stop-opacity:1;" />
<stop
style="stop-color:black;stop-opacity:0;"
offset="1"
id="stop5052" />
</linearGradient>
<linearGradient
id="linearGradient3841-0-3">
<stop
id="stop3843-1-3"
offset="0"
style="stop-color:#0619c0;stop-opacity:1;" />
<stop
id="stop3845-0-8"
offset="1"
style="stop-color:#379cfb;stop-opacity:1;" />
</linearGradient>
<radialGradient
gradientUnits="userSpaceOnUse"
fy="114.5684"
fx="20.892099"
r="5.256"
cy="114.5684"
cx="20.892099"
id="aigrd2">
<stop
id="stop15566"
style="stop-color:#F0F0F0"
offset="0" />
<stop
id="stop15568"
style="stop-color:#9a9a9a;stop-opacity:1.0000000;"
offset="1.0000000" />
</radialGradient>
<radialGradient
gradientUnits="userSpaceOnUse"
fy="64.567902"
fx="20.892099"
r="5.257"
cy="64.567902"
cx="20.892099"
id="aigrd3">
<stop
id="stop15573"
style="stop-color:#F0F0F0"
offset="0" />
<stop
id="stop15575"
style="stop-color:#9a9a9a;stop-opacity:1.0000000;"
offset="1.0000000" />
</radialGradient>
<linearGradient
id="linearGradient15662">
<stop
style="stop-color:#ffffff;stop-opacity:1.0000000;"
offset="0.0000000"
id="stop15664" />
<stop
style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
offset="1.0000000"
id="stop15666" />
</linearGradient>
<radialGradient
r="86.70845"
fy="35.736916"
fx="33.966679"
cy="35.736916"
cx="33.966679"
gradientTransform="matrix(0.96049297,0,0,1.041132,-52.144249,-702.33158)"
gradientUnits="userSpaceOnUse"
id="radialGradient4452"
xlink:href="#linearGradient259"
inkscape:collect="always" />
<linearGradient
id="linearGradient259">
<stop
style="stop-color:#fafafa;stop-opacity:1.0000000;"
offset="0.0000000"
id="stop260" />
<stop
style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
offset="1.0000000"
id="stop261" />
</linearGradient>
<radialGradient
r="37.751713"
fy="3.7561285"
fx="8.824419"
cy="3.7561285"
cx="8.824419"
gradientTransform="matrix(0.96827297,0,0,1.032767,-48.790699,-701.68513)"
gradientUnits="userSpaceOnUse"
id="radialGradient4454"
xlink:href="#linearGradient269"
inkscape:collect="always" />
<linearGradient
id="linearGradient269">
<stop
style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
offset="0.0000000"
id="stop270" />
<stop
style="stop-color:#4c4c4c;stop-opacity:1.0000000;"
offset="1.0000000"
id="stop271" />
</linearGradient>
<inkscape:perspective
id="perspective4947"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
id="linearGradient4095">
<stop
style="stop-color:#005bff;stop-opacity:1;"
offset="0"
id="stop4097" />
<stop
style="stop-color:#c1e3f7;stop-opacity:1;"
offset="1"
id="stop4099" />
</linearGradient>
<inkscape:perspective
id="perspective5027"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5076"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4247"
id="linearGradient4253"
x1="394.15784"
y1="185.1304"
x2="434.73947"
y2="140.22731"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.94231826,0,0,0.94231826,23.727549,8.8262536)" />
<linearGradient
id="linearGradient4247">
<stop
style="stop-color:#2e8207;stop-opacity:1;"
offset="0"
id="stop4249" />
<stop
style="stop-color:#52ff00;stop-opacity:1;"
offset="1"
id="stop4251" />
</linearGradient>
<linearGradient
y2="140.22731"
x2="434.73947"
y1="185.1304"
x1="394.15784"
gradientTransform="matrix(0.94231826,0,0,0.94231826,23.727549,8.8262536)"
gradientUnits="userSpaceOnUse"
id="linearGradient5087"
xlink:href="#linearGradient4247"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5141"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.5"
inkscape:cx="13.473807"
inkscape:cy="32"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1280"
inkscape:window-height="962"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata3617">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<g
transform="translate(-259.85207,-132.78349)"
id="g4670">
<rect
ry="0"
rx="0"
y="179.57144"
x="280.71429"
height="9.2792215"
width="24.688311"
id="rect4258"
style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
ry="0"
rx="0"
y="151.57144"
x="280.71429"
height="9.6428576"
width="24.688311"
id="rect4258-1-7"
style="color:#000000;fill:#0042ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
ry="0"
rx="0"
y="165.38962"
x="285.80521"
height="9.6428576"
width="24.324675"
id="rect4258-1"
style="color:#000000;fill:#9fb7ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
ry="0"
rx="0"
y="137.57144"
x="266.71429"
height="9.6428576"
width="32.142857"
id="rect4258-1-7-4"
style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
sodipodi:nodetypes="ccc"
id="path5098"
d="m 271.07143,207.42857 0,37.14286 9.28571,0"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
transform="translate(0,-60)" />
<path
transform="translate(0,-60)"
id="path5100"
d="m 280.89286,216.71429 -9.64286,0"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 275.61688,156.15584 0,14.23377 9.28571,0"
id="path3050"
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0" />
</g>
<path
style="fill:none;stroke:#d30000;stroke-width:2.39999999999999990;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M 53.090909,45.090909 24.454545,16.454545"
id="path3061"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path3065"
d="M 53.090909,16.454545 24.454545,45.090909"
style="fill:none;stroke:#d30000;stroke-width:2.39999999999999990;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -0,0 +1,457 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="64px"
height="64px"
id="svg3612"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="Draft_AddToGroup.svg">
<defs
id="defs3614">
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Mend"
style="overflow:visible;">
<path
id="path3835"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;"
transform="scale(0.4) rotate(180) translate(10,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mstart"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Mstart"
style="overflow:visible">
<path
id="path3832"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt"
transform="scale(0.4) translate(10,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Lstart"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Lstart"
style="overflow:visible">
<path
id="path3826"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt"
transform="scale(0.8) translate(12.5,0)" />
</marker>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 32 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="64 : 32 : 1"
inkscape:persp3d-origin="32 : 21.333333 : 1"
id="perspective3620" />
<inkscape:perspective
id="perspective3588"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective3692"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
id="linearGradient3144-6">
<stop
id="stop3146-9"
style="stop-color:#ffffff;stop-opacity:1"
offset="0" />
<stop
id="stop3148-2"
style="stop-color:#ffffff;stop-opacity:0"
offset="1" />
</linearGradient>
<linearGradient
id="linearGradient3701">
<stop
id="stop3703"
style="stop-color:#ffffff;stop-opacity:1"
offset="0" />
<stop
id="stop3705"
style="stop-color:#ffffff;stop-opacity:0"
offset="1" />
</linearGradient>
<radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3144-6"
id="radialGradient3688"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1,0,0,0.6985294,0,202.82863)"
cx="225.26402"
cy="672.79736"
fx="225.26402"
fy="672.79736"
r="34.345188" />
<linearGradient
id="linearGradient3708">
<stop
id="stop3710"
style="stop-color:#ffffff;stop-opacity:1"
offset="0" />
<stop
id="stop3712"
style="stop-color:#ffffff;stop-opacity:0"
offset="1" />
</linearGradient>
<inkscape:perspective
id="perspective3805"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
id="linearGradient3864-0-0">
<stop
id="stop3866-5-7"
offset="0"
style="stop-color:#0619c0;stop-opacity:1;" />
<stop
id="stop3868-7-6"
offset="1"
style="stop-color:#379cfb;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient3377">
<stop
id="stop3379"
offset="0"
style="stop-color:#ffaa00;stop-opacity:1;" />
<stop
id="stop3381"
offset="1"
style="stop-color:#faff2b;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient3864-0">
<stop
id="stop3866-5"
offset="0"
style="stop-color:#0619c0;stop-opacity:1;" />
<stop
id="stop3868-7"
offset="1"
style="stop-color:#379cfb;stop-opacity:1;" />
</linearGradient>
<inkscape:perspective
id="perspective3902"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
id="linearGradient5048">
<stop
style="stop-color:black;stop-opacity:0;"
offset="0"
id="stop5050" />
<stop
id="stop5056"
offset="0.5"
style="stop-color:black;stop-opacity:1;" />
<stop
style="stop-color:black;stop-opacity:0;"
offset="1"
id="stop5052" />
</linearGradient>
<linearGradient
id="linearGradient3841-0-3">
<stop
id="stop3843-1-3"
offset="0"
style="stop-color:#0619c0;stop-opacity:1;" />
<stop
id="stop3845-0-8"
offset="1"
style="stop-color:#379cfb;stop-opacity:1;" />
</linearGradient>
<radialGradient
gradientUnits="userSpaceOnUse"
fy="114.5684"
fx="20.892099"
r="5.256"
cy="114.5684"
cx="20.892099"
id="aigrd2">
<stop
id="stop15566"
style="stop-color:#F0F0F0"
offset="0" />
<stop
id="stop15568"
style="stop-color:#9a9a9a;stop-opacity:1.0000000;"
offset="1.0000000" />
</radialGradient>
<radialGradient
gradientUnits="userSpaceOnUse"
fy="64.567902"
fx="20.892099"
r="5.257"
cy="64.567902"
cx="20.892099"
id="aigrd3">
<stop
id="stop15573"
style="stop-color:#F0F0F0"
offset="0" />
<stop
id="stop15575"
style="stop-color:#9a9a9a;stop-opacity:1.0000000;"
offset="1.0000000" />
</radialGradient>
<linearGradient
id="linearGradient15662">
<stop
style="stop-color:#ffffff;stop-opacity:1.0000000;"
offset="0.0000000"
id="stop15664" />
<stop
style="stop-color:#f8f8f8;stop-opacity:1.0000000;"
offset="1.0000000"
id="stop15666" />
</linearGradient>
<radialGradient
r="86.70845"
fy="35.736916"
fx="33.966679"
cy="35.736916"
cx="33.966679"
gradientTransform="matrix(0.96049297,0,0,1.041132,-52.144249,-702.33158)"
gradientUnits="userSpaceOnUse"
id="radialGradient4452"
xlink:href="#linearGradient259"
inkscape:collect="always" />
<linearGradient
id="linearGradient259">
<stop
style="stop-color:#fafafa;stop-opacity:1.0000000;"
offset="0.0000000"
id="stop260" />
<stop
style="stop-color:#bbbbbb;stop-opacity:1.0000000;"
offset="1.0000000"
id="stop261" />
</linearGradient>
<radialGradient
r="37.751713"
fy="3.7561285"
fx="8.824419"
cy="3.7561285"
cx="8.824419"
gradientTransform="matrix(0.96827297,0,0,1.032767,-48.790699,-701.68513)"
gradientUnits="userSpaceOnUse"
id="radialGradient4454"
xlink:href="#linearGradient269"
inkscape:collect="always" />
<linearGradient
id="linearGradient269">
<stop
style="stop-color:#a3a3a3;stop-opacity:1.0000000;"
offset="0.0000000"
id="stop270" />
<stop
style="stop-color:#4c4c4c;stop-opacity:1.0000000;"
offset="1.0000000"
id="stop271" />
</linearGradient>
<inkscape:perspective
id="perspective4947"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
id="linearGradient4095">
<stop
style="stop-color:#005bff;stop-opacity:1;"
offset="0"
id="stop4097" />
<stop
style="stop-color:#c1e3f7;stop-opacity:1;"
offset="1"
id="stop4099" />
</linearGradient>
<inkscape:perspective
id="perspective5027"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective5076"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4247"
id="linearGradient4253"
x1="394.15784"
y1="185.1304"
x2="434.73947"
y2="140.22731"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.94231826,0,0,0.94231826,23.727549,8.8262536)" />
<linearGradient
id="linearGradient4247">
<stop
style="stop-color:#2e8207;stop-opacity:1;"
offset="0"
id="stop4249" />
<stop
style="stop-color:#52ff00;stop-opacity:1;"
offset="1"
id="stop4251" />
</linearGradient>
<linearGradient
y2="140.22731"
x2="434.73947"
y1="185.1304"
x1="394.15784"
gradientTransform="matrix(0.94231826,0,0,0.94231826,23.727549,8.8262536)"
gradientUnits="userSpaceOnUse"
id="linearGradient5087"
xlink:href="#linearGradient4247"
inkscape:collect="always" />
<inkscape:perspective
id="perspective5141"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.5"
inkscape:cx="-2.8181818"
inkscape:cy="32"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1366"
inkscape:window-height="706"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata3617">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<g
transform="translate(-259.85207,-132.78349)"
id="g4670">
<rect
ry="0"
rx="0"
y="179.57144"
x="280.71429"
height="9.2792215"
width="24.688311"
id="rect4258"
style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
ry="0"
rx="0"
y="151.57144"
x="280.71429"
height="9.6428576"
width="24.688311"
id="rect4258-1-7"
style="color:#000000;fill:#0042ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
ry="0"
rx="0"
y="165.38962"
x="285.80521"
height="9.6428576"
width="24.324675"
id="rect4258-1"
style="color:#000000;fill:#0042ff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
ry="0"
rx="0"
y="137.57144"
x="266.71429"
height="9.6428576"
width="32.142857"
id="rect4258-1-7-4"
style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
sodipodi:nodetypes="ccc"
id="path5098"
d="m 271.07143,207.42857 0,37.14286 9.28571,0"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
transform="translate(0,-60)" />
<path
transform="translate(0,-60)"
id="path5100"
d="m 280.89286,216.71429 -9.64286,0"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 275.61688,156.15584 0,14.23377 9.28571,0"
id="path3050"
sodipodi:nodetypes="ccc"
inkscape:connector-curvature="0" />
</g>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow1Mstart);marker-end:url(#Arrow1Mend)"
d="m 52.363636,24 c 7.198529,0.420502 8.668453,-1.886663 8.727273,6.181818 0.05882,8.068481 -0.801204,6.872738 -7.818182,6.727273"
id="path3820"
inkscape:connector-curvature="0"
sodipodi:nodetypes="czc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -0,0 +1,547 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Gui::Dialog::DlgSettingsOpenSCAD</class>
<widget class="QWidget" name="Gui::Dialog::DlgSettingsOpenSCAD">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>575</width>
<height>629</height>
</rect>
</property>
<property name="windowTitle">
<string>General settings</string>
</property>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<number>9</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>General OpenSCAD Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="fclabel">
<property name="text">
<string>OpenSCAD executable</string>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefFileChooser" name="gui::preffilechooser" native="true">
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>The path to the OpenSCAD executeable</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>openscadexecutable</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/OpenSCAD</cstring>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>OpenSCAD import</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="Gui::PrefCheckBox" name="gui::prefcheckboxviewtreeprovider">
<property name="toolTip">
<string>If this is checked, Features will claim thier children in the tree view</string>
</property>
<property name="text">
<string>Use ViewProvider in Tree View</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>useViewProviderTree</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/OpenSCAD</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="Gui::PrefCheckBox" name="gui::prefcheckboxmultmatrixfeature">
<property name="toolTip">
<string>If this is checked, Multmatrix Object will be Parametric</string>
</property>
<property name="text">
<string>Use Multmatrix Feature</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>useMultmatrixFeature</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/OpenSCAD</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="label_12">
<property name="toolTip">
<string>The maximum number of faces of a polygon, prism or frustum. If fn is greater than this value the object is considered to be a circular. Set to 0 for no limit</string>
</property>
<property name="text">
<string>Maximum number of faces for polygons (fn)</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Gui::PrefSpinBox" name="gui::prefmaxfnsp">
<property name="toolTip">
<string>The maximum number of faces of a polygon, prism or frustum. If fn is greater than this value the object is considered to be a circular. Set to 0 for no limit</string>
</property>
<property name="value">
<number>0</number>
</property>
<property name="prefEntry" stdset="0">
<cstring>useMaxFN</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/OpenSCAD</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="Gui::PrefCheckBox" name="gui::prefcheckboxmultmatrixfeature">
<property name="toolTip">
<string>Debug: Register filetype to prototype importer</string>
</property>
<property name="text">
<string>Debug: Register filetype to prototype importer</string>
</property>
<property name="prefEntry" stdset="0">
<cstring>debugRegisterPrototype</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/OpenSCAD</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>OpenSCAD export</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_21">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_21">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>maximum fragment size</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="toolTip">
<string>minimum angle for a fragment</string>
</property>
<property name="text">
<string>angular (fa)</string>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefDoubleSpinBox" name="doubleSpinBox_2">
<property name="toolTip">
<string>minimum angle for a fragment</string>
</property>
<property name="suffix">
<string>°</string>
</property>
<property name="minimum">
<double>0.010000000000000</double>
</property>
<property name="maximum">
<double>360.000000000000000</double>
</property>
<property name="value">
<double>12.000000000000000</double>
</property>
<property name="prefEntry" stdset="0">
<cstring>exportFa</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/OpenSCAD</cstring>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="toolTip">
<string>minimum size of a fragment</string>
</property>
<property name="text">
<string>size (fs)</string>
</property>
</widget>
</item>
<item>
<widget class="Gui::PrefDoubleSpinBox" name="doubleSpinBox_3">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>minimum size of a fragment</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="suffix">
<string>mm</string>
</property>
<property name="minimum">
<double>0.010000000000000</double>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="value">
<double>2.000000000000000</double>
</property>
<property name="prefEntry" stdset="0">
<cstring>exportFs</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/OpenSCAD</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>convexity</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Gui::PrefSpinBox" name="spinBox">
<property name="value">
<number>10</number>
</property>
<property name="prefEntry" stdset="0">
<cstring>exportConvexity</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/OpenSCAD</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Mesh fallback</string>
</property>
</widget>
</item>
<item row="0" column="1">
<spacer name="horizontalSpacer_21">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_3m">
<property name="toolTip">
<string>Maxium Length</string>
</property>
<property name="text">
<string>MaxLength</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="Gui::PrefDoubleSpinBox" name="doubleSpinBox_2l">
<property name="toolTip">
<string>Maximum Length</string>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>1000.000000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
<property name="prefEntry" stdset="0">
<cstring>meshmaxlength</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/OpenSCAD</cstring>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QLabel" name="label_4">
<property name="toolTip">
<string>Maximum Area</string>
</property>
<property name="text">
<string>maxArea</string>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="Gui::PrefDoubleSpinBox" name="doubleSpinBox_2a">
<property name="toolTip">
<string>Maximum Area</string>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>1000.000000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
</property>
<property name="prefEntry" stdset="0">
<cstring>meshmaxarea</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/OpenSCAD</cstring>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="Gui::PrefDoubleSpinBox" name="doubleSpinBox_ll">
<property name="toolTip">
<string>Local Length</string>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>1000.000000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
</property>
<property name="prefEntry" stdset="0">
<cstring>meshlocallen</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/OpenSCAD</cstring>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QLabel" name="label_4">
<property name="toolTip">
<string>Local Length</string>
</property>
<property name="text">
<string>localLen</string>
</property>
</widget>
</item>
<item row="1" column="5">
<widget class="Gui::PrefDoubleSpinBox" name="doubleSpinBox_2a">
<property name="toolTip">
<string>Deflection</string>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>1000.000000000000000</double>
</property>
<property name="value">
<double>0.000000000000000</double>
</property>
<property name="prefEntry" stdset="0">
<cstring>meshdeflection</cstring>
</property>
<property name="prefPath" stdset="0">
<cstring>Mod/OpenSCAD</cstring>
</property>
</widget>
</item>
<item row="0" column="5">
<widget class="QLabel" name="label_4">
<property name="toolTip">
<string>Deflection</string>
</property>
<property name="text">
<string>deflection</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Triangulation settings</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
<customwidgets>
<customwidget>
<class>Gui::FileChooser</class>
<extends>QWidget</extends>
<header>Gui/FileDialog.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefFileChooser</class>
<extends>Gui::FileChooser</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefSpinBox</class>
<extends>QSpinBox</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefCheckBox</class>
<extends>QCheckBox</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
<customwidget>
<class>Gui::PrefDoubleSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>Gui/PrefWidgets.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

5
src/Mod/OpenSCAD/TODO Normal file
View File

@ -0,0 +1,5 @@
Makefiles (get the python files included)
Copyright notices (Ply is BSD, everything Keith did is GPL, most of the stuff from freecad is LGPL)
Make filenames unique, e.g. prepepend module name
Add a gui command for the Refine Shape feature.
Add a gui command for subtractfaces2.

View File

@ -0,0 +1,81 @@
import FreeCAD
def shapedict(shapelst):
return dict([(shape.hashCode(),shape) for shape in shapelst])
def shapeset(shapelst):
return set([shape.hashCode() for shape in shapelst])
def mostbasiccompound(comp):
'''searches fo the most basic shape in a Compound'''
solids=shapeset(comp.Solids)
shells=shapeset(comp.Shells)
faces=shapeset(comp.Faces)
wires=shapeset(comp.Wires)
edges=shapeset(comp.Edges)
vertexes=shapeset(comp.Vertexes)
#FreeCAD.Console.PrintMessage('%s\n' % (str((len(solids),len(shells),len(faces),len(wires),len(edges),len(vertexes)))))
for shape in comp.Solids:
shells -= shapeset(shape.Shells)
faces -= shapeset(shape.Faces)
wires -= shapeset(shape.Wires)
edges -= shapeset(shape.Edges)
vertexes -= shapeset(shape.Vertexes)
for shape in comp.Shells:
faces -= shapeset(shape.Faces)
wires -= shapeset(shape.Wires)
edges -= shapeset(shape.Edges)
vertexes -= shapeset(shape.Vertexes)
for shape in comp.Faces:
wires -= shapeset(shape.Wires)
edges -= shapeset(shape.Edges)
vertexes -= shapeset(shape.Vertexes)
for shape in comp.Wires:
edges -= shapeset(shape.Edges)
vertexes -= shapeset(shape.Vertexes)
for shape in comp.Edges:
vertexes -= shapeset(shape.Vertexes)
#FreeCAD.Console.PrintMessage('%s\n' % (str((len(solids),len(shells),len(faces),len(wires),len(edges),len(vertexes)))))
#return len(solids),len(shells),len(faces),len(wires),len(edges),len(vertexes)
if vertexes:
return "Vertex"
elif edges:
return "Edge"
elif wires:
return "Wire"
elif faces:
return "Face"
elif shells:
return "Shell"
elif solids:
return "Solid"
def colorcodeshapes(objs):
shapecolors={
"Compound":(0.3,0.3,0.4),
"CompSolid":(0.1,0.5,0.0),
"Solid":(0.0,0.8,0.0),
"Shell":(0.8,0.0,0.0),
"Face":(0.6,0.6,0.0),
"Wire":(0.1,0.1,0.1),
"Edge":(1.0,1.0,1.0),
"Vertex":(8.0,8.0,8.0),
"Shape":(0.0,0.0,1.0),
None:(0.0,0.0,0.0)}
for obj in objs:
if hasattr(obj,'Shape'):
try:
if obj.Shape.isNull():
continue
if not obj.Shape.isValid():
color=(1.0,0.4,0.4)
else:
st=obj.Shape.ShapeType
if st in ["Compound","CompSolid"]:
st = mostbasiccompound(obj.Shape)
color=shapecolors[st]
obj.ViewObject.ShapeColor = color
except:
raise
#colorcodeshapes(App.ActiveDocument.Objects)

View File

@ -0,0 +1,78 @@
import FreeCAD
from OpenSCADFeatures import *
from OpenSCADUtils import isspecialorthogonalpython,isspecialorthogonal
import replaceobj
def likeprimitive(obj,extrusion=False):
'''we can't push the matrix transformation further down'''
return not obj.OutList or obj.isDerivedFrom('Part::Extrusion')\
or extrusion and (obj.isDerivedFrom('Part::Revolution') \
or obj.isDerivedFrom('Part::FeaturePython')) or \
not obj.isDerivedFrom('Part::Feature')
def expandplacementsmatrix(obj,matrix):
'''expand afine transformation down the feature tree'''
ownmatrix=matrix.multiply(obj.Placement.toMatrix())
if obj.isDerivedFrom('Part::Feature') and \
isinstance(obj.Proxy,MatrixTransform):
innermatrix=ownmatrix.multiply(obj.Matrix)
if likeprimitive(obj.Base,True): #this matrix is needed
obj.Placement=FreeCAD.Placement()
obj.Matrix = innermatrix
else: #the inner object is not a primitive
expandplacementsmatrix(obj.Base,innermatrix)
#remove the matrix object
for parent in obj.Base.InList:
replaceobj.replaceobj(parent,obj,obj.Base)
out.Document.removeObject(obj.Name)
elif likeprimitive(obj,True):
#if isspecialorthogonalpython(fcsubmatrix(ownmatrix)):
if isspecialorthogonal(ownmatrix):
obj.Placement=FreeCAD.Placement()
#this should never happen unless matrices cancel out
obj.Placement=FreeCAD.Placement(ownmatrix)
else:
newobj=doc.addObject("Part::FeaturePython",'exp_trans')
MatrixTransform(newobj,ownmatrix,obj) #This object is not mutable GUI
ViewProviderTree(newobj.ViewObject)
for parent in obj.InList:
replaceobj.replaceobj(parent,obj,newobj) # register the new object in the feature tree
obj.Placement=FreeCAD.Placement()
else: #not a primitive
for outobj in obj.OutList:
if outobj.isDerivedFrom('Part::Feature') and \
isinstance(obj.Proxy,MatrixTransform):
newmatrix = ownmatrix.multiply(obj.Matrix).multiply(\
outobj.Base.Placement.toMatrix())
if likeprimitive(outobj.Base,True): #child of is like primtitive
outobj.Matrix = newmatrix
outobj.Base.Placement=FreeCAD.Placement()
else: #remove the MatrixTranformation
plainobj=outobj.Base
for parent in outobj.InList:
replaceobj.replaceobj(parent,outobj,plainobj)
outobj.Document.removeObject(outobj.Name)
expandplacementsmatrix(outobj,newmatrix)
else:
expandplacementsmatrix(outobj,ownmatrix)
obj.Placement=FreeCAD.Placement()
def expandplacements(obj,placement):
ownplacement=placement.multiply(obj.Placement)
if obj.isDerivedFrom('Part::FeaturePython') and isinstance(obj.Proxy,MatrixTransform):
#expandplacementsmatrix(obj,ownplacement.toMatrix())
expandplacementsmatrix(obj,placement.toMatrix())
elif likeprimitive(obj,False):
obj.Placement=ownplacement
else:
for outobj in obj.OutList:
if obj.isDerivedFrom('Part::Extrusion'):
obj.Dir=ownplacement.Rotation.multVec(obj.Dir)
elif obj.isDerivedFrom('Part::Revolution'):
obj.Axis=ownplacement.Rotation.multVec(obj.Axis)
#obj.Base=ownplacement.Rotation.multVec(obj.Base)
expandplacements(outobj,ownplacement)
obj.Placement=FreeCAD.Placement()
#expandplacements(rootobj,FreeCAD.Placement())

View File

@ -0,0 +1,260 @@
#***************************************************************************
#* *
#* Copyright (c) 2012 Keith Sloan <keith@sloan-home.co.uk> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU General Public License (GPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* 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 Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#* Acknowledgements : *
#* *
#* Thanks to shoogen on the FreeCAD forum for programming advice *
#* and some code. *
#* *
#***************************************************************************
__title__="FreeCAD OpenSCAD Workbench - CSG exporter Version 0.01c"
__author__ = "Keith Sloan <keith@sloan-home.co.uk>"
__url__ = ["http://www.sloan-home.co.uk/Export/Export.html"]
import FreeCAD, os, Part, math
from FreeCAD import Vector
try: import FreeCADGui
except ValueError: gui = False
else: gui = True
#***************************************************************************
# Tailor following to your requirements ( Should all be strings ) *
#fafs = '$fa = 12, $fs = 2'
#convexity = 'convexity = 10'
params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD")
fa = params.GetFloat('exportFa',12.0)
fs = params.GetFloat('exportFs',2.0)
conv = params.GetInt('exportConvexity',10)
fafs = '$fa = %f, $fs = %f' % (fa,fs)
convexity = 'convexity = %d' % conv
#***************************************************************************
if open.__module__ == '__builtin__':
pythonopen = open
def check_center(ob):
# Only say center = false if no rotation and no displacement
if ob.Placement.isNull():
return 'false'
return 'true'
def center(b):
if b == 0 :
return 'false'
return 'true'
def check_multmatrix(csg,ob,x,y,z):
v = FreeCAD.Vector(0,0,1)
b = FreeCAD.Vector(x,y,z)
if ( ob.Placement.Base == FreeCAD.Vector(0,0,0)):
return 0 # center = false no mm
elif not ob.Placement.isNull():
print "Output Multmatrix"
m = ob.Placement.toMatrix()
# adjust position for center displacments
csg.write("multmatrix([["+str(m.A11)+", "+str(m.A12)+", "+str(m.A13)+", "+str(m.A14)+"], ["\
+str(m.A21)+", "+str(m.A22)+", "+str(m.A23)+", "+str(m.A24)+"], ["\
+str(m.A31)+", "+str(m.A32)+", "+str(m.A33)+", "+str(m.A34)+"], [ 0, 0, 0, 1]]){\n")
return 1 # center = true and mm
return 2 # center = true and no mm
def mesh2polyhedron(mesh):
pointstr=','.join(['[%f,%f,%f]' % tuple(vec) for vec in mesh.Topology[0]])
trianglestr=','.join(['[%d,%d,%d]' % tuple(tri) for tri in mesh.Topology[1]])
return 'polyhedron ( points = [%s], triangles = [%s]);' % (pointstr,trianglestr)
def vector2d(v):
return [v[0],v[1]]
def vertexs2polygon(vertex):
pointstr=','.join(['[%f, %f]' % tuple(vector2d(v.Point)) for v in vertex])
return 'polygon ( points = [%s], paths = undef, convexity = 1);}' % pointstr
def shape2polyhedron(shape):
import MeshPart
fa = params.GetFloat('exportFa',12.0)
return mesh2polyhedron(MeshPart.meshFromShape(shape,params.GetFloat(\
'meshmaxlength',1.0), params.GetFloat('meshmaxarea',0.0),\
params.GetFloat('meshlocallen',0.0),\
params.GetFloat('meshdeflection',0.0)))
def process_object(csg,ob):
print "Placement"
print "Pos : "+str(ob.Placement.Base)
print "axis : "+str(ob.Placement.Rotation.Axis)
print "angle : "+str(ob.Placement.Rotation.Angle)
if ob.Type == "Part::Sphere" :
print "Sphere Radius : "+str(ob.Radius)
check_multmatrix(csg,ob,0,0,0)
global fafs
csg.write("sphere($fn = 0, "+fafs+", r = "+str(ob.Radius)+");\n")
elif ob.Type == "Part::Box" :
print "cube : ("+ str(ob.Length)+","+str(ob.Width)+","+str(ob.Height)+")"
mm = check_multmatrix(csg,ob,-ob.Length/2,-ob.Width/2,-ob.Height/2)
csg.write("cube (size = ["+str(ob.Length)+", "+str(ob.Width)+", "+str(ob.Height)+"], center = "+center(mm)+");\n")
if mm == 1 : csg.write("}\n")
elif ob.Type == "Part::Cylinder" :
print "cylinder : Height "+str(ob.Height)+ " Radius "+str(ob.Radius)
mm = check_multmatrix(csg,ob,0,0,-ob.Height/2)
global fafs
csg.write("cylinder($fn = 0, "+fafs+", h = "+str(ob.Height)+ ", r1 = "+str(ob.Radius)+\
", r2 = " + str(ob.Radius) + ", center = "+center(mm)+");\n")
if mm == 1 : csg.write("}\n")
elif ob.Type == "Part::Cone" :
print "cone : Height "+str(ob.Height)+ " Radius1 "+str(ob.Radius1)+" Radius2 "+str(ob.Radius2)
mm = check_multmatrix(csg,ob,0,0,-ob.Height/2)
global fafs
csg.write("cylinder($fn = 0, "+fafs+", h = "+str(ob.Height)+ ", r1 = "+str(ob.Radius1)+\
", r2 = "+str(ob.Radius2)+", center = "+center(mm)+");\n")
if mm == 1 : csg.write("}\n")
elif ob.Type == "Part::Torus" :
print "Torus"
print ob.Radius1
print ob.Radius2
if ob.Angle3 == 360.00 :
mm = check_multmatrix(csg,ob,0,0,0)
global fafs
csg.write("rotate_extrude("+convexity+", $fn = 0, "+fafs+")\n")
csg.write("multmatrix([[1, 0, 0, "+str(ob.Radius1)+"], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])\n")
csg.write("circle($fn = 0, "+fafs+", r = "+str(ob.Radius2)+");\n")
if mm == 1 : csg.write("}\n")
else : # Cannot convert to rotate extrude so best effort is polyhedron
csg.write('%s\n' % shape2polyhedron(ob.Shape))
elif ob.Type == "Part::Extrusion" :
print "Extrusion"
print ob.Base
print ob.Base.Name
#if ( ob.Base == "Part::FeaturePython" and ob.Base.Name == "Polygon") :
if ob.Base.Name == "Polygon" :
f = str(ob.Base.FacesNumber)
r = str(ob.Base.Radius)
h = str(ob.Dir[2])
print "Faces : " + f
print "Radius : " + r
print "Height : " + h
mm = check_multmatrix(csg,ob,0,0,-float(h)/2)
global fafs
csg.write("cylinder($fn = "+f+", "+fafs+", h = "+h+", r1 = "+r+\
", r2 = "+r+", center = "+center(mm)+");\n")
if mm == 1: csg.write("}\n")
elif ob.Base.Name == "circle" :
r = str(ob.Base.Radius)
h = str(ob.Dir[2])
print "Radius : " + r
print "Height : " + h
mm = check_multmatrix(csg,ob,0,0,-float(h)/2)
global fafs
csg.write("cylinder($fn = 0, "+fafs+", h = "+h+", r1 = "+r+\
", r2 = "+r+", center = "+center(mm)+");\n")
if mm == 1: csg.write("}\n")
elif ob.Base.Name == "Wire" :
print "Wire extrusion"
print ob.Base
mm = check_multmatrix(csg,ob,0,0,0)
global fafs
csg.write("linear_extrude(height = "+str(ob.Dir[2])+", center = "+center(mm)+", "+convexity+", twist = 0, slices = 2, $fn = 0, "+fafs+")\n{\n")
csg.write(vertexs2polygon(ob.Base.Shape.Vertexes))
if mm == 1: csg.write("}\n")
elif ob.Base.Name == "square" :
mm = check_multmatrix(csg,ob,0,0,0)
global fafs
csg.write("linear_extrude(height = "+str(ob.Dir[2])+", center = true, "+convexity+", twist = 0, slices = 2, $fn = 0, "+fafs+")\n{\n")
csg.write("square (size = ["+str(ob.Base.Length)+", "+str(ob.Base.Width)+"],center = "+center(mm)+";\n}\n")
if mm == 1: csg.write("}\n")
elif ob.Type == "Part::Cut" :
print "Cut"
csg.write("difference() {\n")
process_object(csg,ob.Base)
process_object(csg,ob.Tool)
csg.write("}\n")
elif ob.Type == "Part::Fuse" :
print "union"
csg.write("union() {\n")
process_object(csg,ob.Base)
process_object(csg,ob.Tool)
csg.write("}\n")
elif ob.Type == "Part::Common" :
print "intersection"
csg.write("intersection() {\n")
process_object(csg,ob.Base)
process_object(csg,ob.Tool)
csg.write("}\n")
elif ob.Type == "Part::MultiFuse" :
print "Multi Fuse / union"
csg.write("union() {\n")
for subobj in ob.Shapes:
process_object(csg,subobj)
csg.write("}\n")
elif ob.Type == "Part::Common" :
print "Multi Common / intersection"
csg.write("intersection() {\n")
for subobj in ob.Shapes:
process_object(csg,subobj)
csg.write("}\n")
elif ob.isDerivedFrom('Part::Feature') :
print "Part::Feature"
mm = check_multmatrix(csg,ob,0,0,0)
csg.write('%s\n' % shape2polyhedron(ob.Shape))
if mm == 1 : csg.write("}\n")
def export(exportList,filename):
"called when freecad exports a file"
# process Objects
print "\nStart Export 0.1c\n"
print "Open Output File"
csg = pythonopen(filename,'w')
print "Write Inital Output"
# Not sure if comments as per scad are allowed in csg file
csg.write("// CSG file generated from FreeCAD Export 0.1c\n")
#write initial group statements - not sure if required
csg.write("group() {\n group(){\n")
for ob in exportList:
print ob
print "Name : "+ob.Name
print "Type : "+ob.Type
print "Shape : "
print ob.Shape
process_object(csg,ob)
# write closing group braces
csg.write("}\n}\n")
# close file
csg.close()
FreeCAD.Console.PrintMessage("successfully exported "+filename)

View File

@ -0,0 +1,18 @@
Version History
===============
0.01a - First Version
0.01b - Added support for polyhedron ( Thanks shoogen )
Some support for Torus
Some support for extruded regular polygon
0.01c - Support for extruded polygon, square, circle
bug fix for polyhydron floating point - Thanks shoogen
Thanks to shoogen and Peter Li for their advice, code, support and expertise.
Keith Sloan
keith@sloan-home.co.uk

View File

@ -0,0 +1,885 @@
# -*- coding: utf8 -*-
#***************************************************************************
#* *
#* Copyright (c) 2012 Keith Sloan <keith@sloan-home.co.uk> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU General Public License (GPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* 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 Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#* Acknowledgements : *
#* *
#* Thanks to shoogen on the FreeCAD forum and Peter Li *
#* for programming advice and some code. *
#* *
#* *
#***************************************************************************
__title__="FreeCAD OpenSCAD Workbench - CSG importer Version 0.05d"
__author__ = "Keith Sloan <keith@sloan-home.co.uk>"
__url__ = ["http://www.sloan-home.co.uk/ImportCSG"]
import FreeCAD, os, sys
import ply.lex as lex
import ply.yacc as yacc
import Part
from OpenSCADFeatures import RefineShape
from OpenSCAD2Dgeom import *
from OpenSCADUtils import *
isspecialorthogonaldeterminant = isspecialorthogonalpython
from OpenSCADFeatures import Twist
if open.__module__ == '__builtin__':
pythonopen = open # to distinguish python built-in open function from the one declared here
# Get the token map from the lexer. This is required.
import tokrules
from tokrules import tokens
#Globals
dxfcache = {}
def open(filename):
"called when freecad opens a file."
global doc
global pathName
docname = os.path.splitext(os.path.basename(filename))[0]
doc = FreeCAD.newDocument(docname)
if filename.lower().endswith('.scad'):
tmpfile=callopenscad(filename)
pathName = '' #https://github.com/openscad/openscad/issues/128
#pathName = os.getcwd() #https://github.com/openscad/openscad/issues/128
processcsg(tmpfile)
os.unlink(tmpfile)
else:
pathName = os.path.dirname(os.path.normpath(filename))
processcsg(filename)
return doc
def insert(filename,docname):
"called when freecad imports a file"
global doc
global pathName
groupname = os.path.splitext(os.path.basename(filename))[0]
try:
doc=FreeCAD.getDocument(docname)
except:
doc=FreeCAD.newDocument(docname)
importgroup = doc.addObject("App::DocumentObjectGroup",groupname)
if filename.lower().endswith('.scad'):
tmpfile=callopenscad(filename)
pathName = '' #https://github.com/openscad/openscad/issues/128
#pathName = os.getcwd() #https://github.com/openscad/openscad/issues/128
processcsg(tmpfile)
os.unlink(tmpfile)
else:
pathName = os.path.dirname(os.path.normpath(filename))
processcsg(filename)
def processcsg(filename):
global doc
print 'ImportCSG Version 0.5d'
# Build the lexer
print 'Start Lex'
lex.lex(module=tokrules)
print 'End Lex'
# Build the parser
print 'Load Parser'
# No debug out otherwise Linux has protection exception
#parser = yacc.yacc(debug=0)
parser = yacc.yacc(debug=0)
print 'Parser Loaded'
# Give the lexer some input
#f=open('test.scad', 'r')
f = pythonopen(filename, 'r')
#lexer.input(f.read())
print 'Start Parser'
# Swap statements to enable Parser debugging
#result = parser.parse(f.read(),debug=1)
result = parser.parse(f.read())
print 'End Parser'
print result
FreeCAD.Console.PrintMessage('End processing CSG file')
doc.recompute()
#import colorcodeshapes
#colorcodeshapes.colorcodeshapes(doc.Objects)
def p_block_list_(p):
'''
block_list : statement
| block_list statement
'''
print "Block List"
print p[1]
if(len(p) > 2) :
print p[2]
p[0] = p[1] + p[2]
else :
p[0] = p[1]
print "End Block List"
def p_group_action1(p):
'group_action1 : group LPAREN RPAREN OBRACE block_list EBRACE'
print "Group"
p[0] = p[5]
def p_group_action2(p) :
'group_action2 : group LPAREN RPAREN SEMICOL'
print "Group2"
p[0] = []
def p_boolean(p) :
'''
boolean : true
| false
'''
p[0] = p[1]
#def p_string(p):
# 'string : QUOTE ID QUOTE'
# p[0] = p[2]
def p_stripped_string(p):
'stripped_string : STRING'
p[0] = p[1].strip('"')
def p_statement(p):
'''statement : part
| operation
| multmatrix_action
| group_action1
| group_action2
| color_action
| not_supported
'''
p[0] = p[1]
def p_part(p):
'''
part : sphere_action
| cylinder_action
| cube_action
| circle_action
| square_action
| polygon_action_nopath
| polygon_action_plus_path
| polyhedron_action
'''
p[0] = p[1]
def p_2d_point(p):
'2d_point : OSQUARE NUMBER COMMA NUMBER ESQUARE'
global points_list
print "2d Point"
p[0] = [float(p[2]),float(p[4])]
def p_points_list_2d(p):
'''
points_list_2d : 2d_point COMMA
| points_list_2d 2d_point COMMA
| points_list_2d 2d_point
'''
if p[2] == ',' :
print "Start List"
print p[1]
p[0] = [p[1]]
else :
print p[1]
print p[2]
p[1].append(p[2])
p[0] = p[1]
print p[0]
def p_3d_point(p):
'3d_point : OSQUARE NUMBER COMMA NUMBER COMMA NUMBER ESQUARE'
global points_list
print "3d point"
p[0] = [p[2],p[4],p[6]]
def p_points_list_3d(p):
'''
points_list_3d : 3d_point COMMA
| points_list_3d 3d_point COMMA
| points_list_3d 3d_point
'''
if p[2] == ',' :
print "Start List"
print p[1]
p[0] = [p[1]]
else :
print p[1]
print p[2]
p[1].append(p[2])
p[0] = p[1]
print p[0]
def p_path_points(p):
'''
path_points : NUMBER COMMA
| path_points NUMBER COMMA
| path_points NUMBER
'''
print "Path point"
if p[2] == ',' :
print 'Start list'
print p[1]
p[0] = [int(p[1])]
else :
print p[1]
print len(p[1])
print p[2]
p[1].append(int(p[2]))
p[0] = p[1]
print p[0]
def p_path_list(p):
'path_list : OSQUARE path_points ESQUARE'
print 'Path List '
print p[2]
p[0] = p[2]
def p_path_set(p) :
'''
path_set : path_list
| path_set COMMA path_list
'''
print 'Path Set'
print len(p)
if len(p) == 2 :
p[0] = [p[1]]
else :
p[1].append(p[3])
p[0] = p[1]
print p[0]
def p_operation(p):
'''
operation : difference_action
| intersection_action
| union_action
| rotate_extrude_action
| linear_extrude_with_twist
| linear_extrude_action2
| rotate_extrude_file
| import_file1
| projection_action
'''
p[0] = p[1]
def p_not_supported(p):
'''
not_supported : hull
| minkowski
'''
from PyQt4 import QtGui
QtGui.QMessageBox.critical(None, "Unsupported Function : "+p[1], "Press OK")
def p_size_vector(p):
'size_vector : OSQUARE NUMBER COMMA NUMBER COMMA NUMBER ESQUARE'
print "size vector"
p[0] = [p[2],p[4],p[6]]
def p_assign(p):
'assign : ID EQ NUMBER'
print "Assignment"
print p[1] + ' : ' + p[3]
p[0] = p[3]
def p_color_action(p):
'color_action : color LPAREN vector RPAREN OBRACE block_list EBRACE'
import math
print "Color"
color = tuple([float(f) for f in p[3][:3]]) #RGB
transp = 100 - int(math.floor(100*float(p[3][3]))) #Alpha
for obj in p[6]:
obj.ViewObject.ShapeColor =color
obj.ViewObject.Transparency = transp
p[0] = p[6]
# Error rule for syntax errors
def p_error(p):
print "Syntax error in input!"
print p
def fuse(list,name):
global doc
print "Fuse"
print list
# Is this Multi Fuse
if ( len(list) > 2):
print "Multi Fuse"
myfuse = doc.addObject('Part::MultiFuse',name)
myfuse.Shapes = list
for subobj in myfuse.Shapes:
subobj.ViewObject.hide()
else :
print "Single Fuse"
myfuse = doc.addObject('Part::Fuse',name)
myfuse.Base = list[0]
myfuse.Tool = list[1]
myfuse.Base.ViewObject.hide()
myfuse.Tool.ViewObject.hide()
return(myfuse)
def p_union_action(p):
'union_action : union LPAREN RPAREN OBRACE block_list EBRACE'
print "union"
newpart = fuse(p[5],p[1])
print "Push Union Result"
p[0] = [newpart]
print "End Union"
def p_difference_action(p):
'difference_action : difference LPAREN RPAREN OBRACE block_list EBRACE'
print "difference"
print len(p[5])
print p[5]
mycut = doc.addObject('Part::Cut',p[1])
# Cut using Fuse
mycut.Base = p[5][0]
# Can only Cut two objects do we need to fuse extras
if (len(p[5]) > 2 ):
print "Need to Fuse Extra First"
mycut.Tool = fuse(p[5][1:],'union')
else :
mycut.Tool = p[5][1]
mycut.Base.ViewObject.hide()
mycut.Tool.ViewObject.hide()
print "Push Resulting Cut"
p[0] = [mycut]
print "End Cut"
def p_intersection_action(p):
'intersection_action : intersection LPAREN RPAREN OBRACE block_list EBRACE'
print "intersection"
# Is this Multi Common
if (len(p[5]) > 2):
print "Multi Common"
mycommon = doc.addObject('Part::MultiCommon',p[1])
mycommon.Shapes = p[5]
for subobj in mycommon.Shapes:
subobj.ViewObject.hide()
else :
print "Single Common"
mycommon = doc.addObject('Part::Common',p[1])
mycommon.Base = p[5][0]
mycommon.Tool = p[5][1]
mycommon.Base.ViewObject.hide()
mycommon.Tool.ViewObject.hide()
p[0] = [mycommon]
print "End Intersection"
def process_rotate_extrude(obj):
myrev = doc.addObject("Part::Revolution","RotateExtrude")
myrev.Source = obj
myrev.Axis = (0.00,1.00,0.00)
myrev.Base = (0.00,0.00,0.00)
myrev.Angle = 360.00
myrev.Placement=FreeCAD.Placement(FreeCAD.Vector(),FreeCAD.Rotation(0,0,90))
obj.ViewObject.hide()
newobj=doc.addObject("Part::FeaturePython",'RefineRotateExtrude')
RefineShape(newobj,myrev)
if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
GetBool('useViewProviderTree'):
from OpenSCADFeatures import ViewProviderTree
ViewProviderTree(newobj.ViewObject)
else:
newobj.ViewObject.Proxy = 0
myrev.ViewObject.hide()
return(newobj)
def p_rotate_extrude_action(p):
'rotate_extrude_action : rotate_extrude LPAREN assign COMMA assign COMMA assign COMMA assign RPAREN OBRACE block_list EBRACE'
print "Rotate Extrude"
if (len(p[12]) > 1) :
part = fuse(p[12],"Rotate Extrude Union")
else :
part = p[12][0]
p[0] = [process_rotate_extrude(part)]
print "End Rotate Extrude"
def p_rotate_extrude_file(p):
'rotate_extrude_file : rotate_extrude LPAREN file EQ stripped_string COMMA layer EQ stripped_string COMMA origin EQ 2d_point COMMA assign \
COMMA assign COMMA assign COMMA assign COMMA assign RPAREN SEMICOL'
print "Rotate Extrude File"
filen,ext =p[5] .rsplit('.',1)
obj = process_import_file(filen,ext,p[9])
p[0] = [process_rotate_extrude(obj)]
print "End Rotate Extrude File"
def process_linear_extrude(obj,h) :
mylinear = doc.addObject("Part::Extrusion","LinearExtrude")
mylinear.Base = obj
mylinear.Dir = (0,0,h)
mylinear.Placement=FreeCAD.Placement()
try:
mylinear.Solid = True
except:
a = 1 # Any old null statement
obj.ViewObject.hide()
newobj=doc.addObject("Part::FeaturePython",'RefineLinearExtrude')
RefineShape(newobj,mylinear)
if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
GetBool('useViewProviderTree'):
from OpenSCADFeatures import ViewProviderTree
ViewProviderTree(newobj.ViewObject)
else:
newobj.ViewObject.Proxy = 0
mylinear.ViewObject.hide()
return(newobj)
def process_linear_extrude_with_twist(base,height,twist) :
newobj=doc.addObject("Part::FeaturePython",'twist_extrude')
Twist(newobj,base,height,-twist) #base is an FreeCAD Object, heigth and twist are floats
if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
GetBool('useViewProviderTree'):
from OpenSCADFeatures import ViewProviderTree
ViewProviderTree(newobj.ViewObject)
else:
newobj.ViewObject.Proxy = 0
#import ViewProviderTree from OpenSCADFeatures
#ViewProviderTree(obj.ViewObject)
return(newobj)
def p_linear_extrude_with_twist(p):
'linear_extrude_with_twist : linear_extrude LPAREN assign COMMA center EQ boolean COMMA assign COMMA assign COMMA assign COMMA \
assign COMMA assign COMMA assign RPAREN OBRACE block_list EBRACE'
print "Linear Extrude With Twist"
h = float(p[3])
print "Twist : "+p[11]
t = float(p[11])
s = int(p[13])
if (len(p[22]) > 1) :
obj = fuse(p[22],"Linear Extrude Union")
else :
obj = p[22][0]
if t:
p[0] = [process_linear_extrude_with_twist(obj,h,t)]
else:
p[0] = [process_linear_extrude(obj,h)]
if p[7]=='true' :
center(obj,0,0,h)
print "End Linear Extrude with twist"
def p_linear_extrude_action2(p):
'linear_extrude_action2 : linear_extrude LPAREN assign COMMA center EQ boolean COMMA assign COMMA assign COMMA assign COMMA \
assign RPAREN OBRACE block_list EBRACE'
print "Linear Extrude 2"
h = float(p[3])
if (len(p[18]) > 1) :
obj = fuse(p[18],"Linear Extrude Union")
else :
obj = p[18][0]
p[0] = [process_linear_extrude(obj,h)]
if p[7]=='true' :
center(obj,0,0,h)
print "End Linear Extrude 2"
def p_import_file1(p):
'import_file1 : import LPAREN file EQ stripped_string COMMA layer EQ stripped_string COMMA origin EQ 2d_point COMMA assign COMMA assign COMMA \
assign COMMA assign COMMA assign RPAREN SEMICOL'
print "Import File"
filen,ext =p[5] .rsplit('.',1)
p[0] = [process_import_file(filen,ext,p[9])]
print "End Import File"
def process_import_file(fname,ext,layer):
print "Importing : "+fname+"."+ext+" Layer : "+layer
if ext.lower() in reverseimporttypes()['Mesh']:
obj=process_mesh_file(fname,ext)
elif ext=='dxf' :
obj=processDXF(fname,layer)
else :
print "Unsupported file extension"
return(obj)
def process_mesh_file(fname,ext):
import Mesh
fullname = fname+'.'+ext
filename = os.path.join(pathName,fullname)
mesh1 = doc.getObject(fname) #reuse imported object
if not mesh1:
Mesh.insert(filename)
mesh1=doc.getObject(fname)
mesh1.ViewObject.hide()
sh=Part.Shape()
sh.makeShapeFromMesh(mesh1.Mesh.Topology,0.1)
solid = Part.Solid(sh)
obj=doc.addObject('Part::Feature',"Mesh")
#ImportObject(obj,mesh1) #This object is not mutable from the GUI
#ViewProviderTree(obj.ViewObject)
solid=solid.removeSplitter()
if solid.Volume < 0:
#sh.reverse()
#sh = sh.copy()
solid.complement()
obj.Shape=solid#.removeSplitter()
return(obj)
def processDXF(fname,layer):
global doc
global pathName
print "Process DXF file"
print "File Name : "+fname
print "Layer : "+layer
print "PathName : "+pathName
dxfname = fname+'.dxf'
filename = os.path.join(pathName,dxfname)
print "DXF Full path : "+filename
#featname='import_dxf_%s_%s'%(objname,layera)
# reusing an allready imported object does not work if the
#shape in not yet calculated
import importDXF
global dxfcache
layers=dxfcache.get(id(doc),[])
print "Layers : "+str(layers)
if layers:
try:
groupobj=[go for go in layers if (not layer) or go.Label == layer]
except:
groupobj= None
else:
groupobj= None
if not groupobj:
print "Importing Layer"
layers = importDXF.processdxf(doc,filename) or importDXF.layers
dxfcache[id(doc)] = layers[:]
for l in layers:
for o in l.Group:
o.ViewObject.hide()
l.ViewObject.hide()
groupobj=[go for go in layers if (not layer) or go.Label == layer]
edges=[]
if not groupobj:
print 'import of layer %s failed' % layer
for shapeobj in groupobj[0].Group:
edges.extend(shapeobj.Shape.Edges)
f=edgestofaces(edges)
#obj=doc.addObject("Part::FeaturePython",'import_dxf_%s_%s'%(objname,layera))
obj=doc.addObject('Part::Feature',"dxf")
#ImportObject(obj,groupobj[0]) #This object is not mutable from the GUI
#ViewProviderTree(obj.ViewObject)
obj.Shape=f
print "DXF Diagnostics"
print obj.Shape.ShapeType
print "Closed : "+str(f.isClosed())
print f.check()
print [w.isClosed() for w in obj.Shape.Wires]
return(obj)
def processSTL(fname):
print "Process STL file"
def p_multmatrix_action(p):
'multmatrix_action : multmatrix LPAREN matrix RPAREN OBRACE block_list EBRACE'
print "MultMatrix"
transform_matrix = FreeCAD.Matrix()
print "Multmatrix"
print p[3]
transform_matrix.A11 = round(float(p[3][0][0]),12)
transform_matrix.A12 = round(float(p[3][0][1]),12)
transform_matrix.A13 = round(float(p[3][0][2]),12)
transform_matrix.A14 = round(float(p[3][0][3]),12)
transform_matrix.A21 = round(float(p[3][1][0]),12)
transform_matrix.A22 = round(float(p[3][1][1]),12)
transform_matrix.A23 = round(float(p[3][1][2]),12)
transform_matrix.A24 = round(float(p[3][1][3]),12)
transform_matrix.A31 = round(float(p[3][2][0]),12)
transform_matrix.A32 = round(float(p[3][2][1]),12)
transform_matrix.A33 = round(float(p[3][2][2]),12)
transform_matrix.A34 = round(float(p[3][2][3]),12)
print transform_matrix
print "Apply Multmatrix"
# If more than one object on the stack for multmatrix fuse first
if (len(p[6]) > 1) :
part = fuse(p[6],"Matrix Union")
else :
part = p[6][0]
# part = new_part.transformGeometry(transform_matrix)
# part = new_part.copy()
# part.transformShape(transform_matrix)
if (isspecialorthogonaldeterminant(fcsubmatrix(transform_matrix))) :
print "Orthogonal"
part.Placement=FreeCAD.Placement(transform_matrix).multiply(part.Placement)
new_part = part
elif FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
GetBool('useMultmatrixFeature'):
from OpenSCADFeatures import MatrixTransform
new_part=doc.addObject("Part::FeaturePython",'Matrix Deformation')
MatrixTransform(new_part,transform_matrix,part)
if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
GetBool('useViewProviderTree'):
from OpenSCADFeatures import ViewProviderTree
ViewProviderTree(new_part.ViewObject)
else:
new_part.ViewObject.Proxy = 0
part.ViewObject.hide()
else :
print "Transform Geometry"
# Need to recompute to stop transformGeometry causing a crash
doc.recompute()
new_part = doc.addObject("Part::Feature","Matrix Deformation")
# new_part.Shape = part.Base.Shape.transformGeometry(transform_matrix)
new_part.Shape = part.Shape.transformGeometry(transform_matrix)
part.ViewObject.hide()
if False :
# Does not fix problemfile or beltTighener although later is closer
newobj=doc.addObject("Part::FeaturePython",'RefineMultMatrix')
RefineShape(newobj,new_part)
newobj.ViewObject.Proxy = 0
new_part.ViewObject.hide()
p[0] = [newobj]
else :
p[0] = [new_part]
print "Multmatrix applied"
def p_matrix(p):
'matrix : OSQUARE vector COMMA vector COMMA vector COMMA vector ESQUARE'
print "Matrix"
p[0] = [p[2],p[4],p[6],p[8]]
def p_vector(p):
'vector : OSQUARE NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER ESQUARE'
print "Vector"
p[0] = [p[2],p[4],p[6],p[8]]
def center(obj,x,y,z):
obj.Placement = FreeCAD.Placement(\
FreeCAD.Vector(-x/2.0,-y/2.0,-z/2.0),\
FreeCAD.Rotation(0,0,0,1))
def p_sphere_action(p):
'sphere_action : sphere LPAREN assign COMMA assign COMMA assign COMMA assign RPAREN SEMICOL'
print "Sphere : "+p[9]
r = float(p[9])
mysphere = doc.addObject("Part::Sphere",p[1])
mysphere.Radius = r
print "Push Sphere"
p[0] = [mysphere]
print "End Sphere"
def myPolygon(n,r1):
# Adapted from Draft::_Polygon
import math
print "My Polygon"
angle = math.pi*2/n
nodes = [FreeCAD.Vector(r1,0,0)]
for i in range(n-1) :
th = (i+1) * angle
nodes.append(FreeCAD.Vector(r1*math.cos(th),r1*math.sin(th),0))
nodes.append(nodes[0])
polygonwire = Part.makePolygon(nodes)
polygon = doc.addObject("Part::Feature","Polygon")
polygon.Shape = Part.Face(polygonwire)
return(polygon)
def p_cylinder_action(p):
'cylinder_action : cylinder LPAREN assign COMMA assign COMMA assign COMMA assign COMMA assign COMMA assign COMMA center EQ boolean RPAREN SEMICOL'
print "Cylinder"
h = float(p[9])
r1 = float(p[11])
r2 = float(p[13])
print p[9] + ' : ' + p[11] + ' : ' + p[13]
if ( r1 == r2 ):
print "Make Cylinder"
n = int(p[3])
fnmax = FreeCAD.ParamGet(\
"User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
GetInt('useMaxFN')
if n < 3 or fnmax != 0 and n > fnmax:
mycyl=doc.addObject("Part::Cylinder",p[1])
mycyl.Height = h
mycyl.Radius = r1
else :
print "Make Prism"
mycyl=doc.addObject("Part::Extrusion","prism")
mycyl.Dir = (0,0,h)
try :
import Draft
mycyl.Base = Draft.makePolygon(n,r1)
except :
# If Draft can't import (probably due to lack of Pivy on Mac and
# Linux builds of FreeCAD), this is a fallback.
# or old level of FreeCAD
print "Draft makePolygon Failed, falling back on manual polygon"
mycyl.Base = myPolygon(n,r1)
else :
pass
mycyl.Base.ViewObject.hide()
# mycyl.Solid = True
else:
print "Make Cone"
mycyl=doc.addObject("Part::Cone",p[1])
mycyl.Height = h
mycyl.Radius1 = r1
mycyl.Radius2 = r2
print "Center = "+str(p[17])
if p[17]=='true' :
center(mycyl,0,0,h)
if False :
# Does not fix problemfile or beltTighener although later is closer
newobj=doc.addObject("Part::FeaturePython",'RefineCylinder')
RefineShape(newobj,mycyl)
if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
GetBool('useViewProviderTree'):
from OpenSCADFeatures import ViewProviderTree
ViewProviderTree(newobj.ViewObject)
else:
newobj.ViewObject.Proxy = 0
mycyl.ViewObject.hide()
p[0] = [newobj]
else :
p[0] = [mycyl]
print "End Cylinder"
def p_cube_action(p):
'cube_action : cube LPAREN size EQ size_vector COMMA center EQ boolean RPAREN SEMICOL'
global doc
l = float(p[5][0])
w = float(p[5][1])
h = float(p[5][2])
print "cube : "+p[5][0] + ' : ' + p[5][1] +' : '+ p[5][2]
mycube=doc.addObject('Part::Box',p[1])
mycube.Length=l
mycube.Width=w
mycube.Height=h
print "Center = "+str(p[9])
if p[9]=='true' :
center(mycube,l,w,h);
p[0] = [mycube]
print "End Cube"
def p_circle_action(p) :
'circle_action : circle LPAREN assign COMMA assign COMMA assign COMMA assign RPAREN SEMICOL'
print "Circle : "+str(p[9])
r = float(p[9])
n = int(p[3])
fnmax = FreeCAD.ParamGet(\
"User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
GetInt('useMaxFN')
import Draft
if n == 0 or fnmax != 0 and n > fnmax:
mycircle = Draft.makeCircle(r)
#mycircle = doc.addObject('Part::Circle',p[1])
#mycircle.Radius = r
else :
mycircle = Draft.makePolygon(n,r)
print "Push Circle"
p[0] = [mycircle]
def p_square_action(p) :
'square_action : square LPAREN size EQ 2d_point COMMA center EQ boolean RPAREN SEMICOL'
print "Square"
x = float(p[5][0])
y = float(p[5][1])
mysquare = doc.addObject('Part::Plane',p[1])
mysquare.Length=x
mysquare.Width=y
if p[9]=='true' :
center(mysquare,x,y,0)
p[0] = [mysquare]
def convert_points_list_to_vector(l):
v = []
for i in l :
print i
v.append(FreeCAD.Vector(i[0],i[1]))
print v
return(v)
def p_polygon_action_nopath(p) :
'polygon_action_nopath : polygon LPAREN points EQ OSQUARE points_list_2d ESQUARE COMMA paths EQ undef COMMA assign RPAREN SEMICOL'
print "Polygon"
print p[6]
v = convert_points_list_to_vector(p[6])
mypolygon = doc.addObject('Part::Feature',p[1])
print "Make Parts"
# Close Polygon
v.append(v[0])
parts = Part.makePolygon(v)
print "update object"
mypolygon.Shape = Part.Face(parts)
p[0] = [mypolygon]
def p_polygon_action_plus_path(p) :
'polygon_action_plus_path : polygon LPAREN points EQ OSQUARE points_list_2d ESQUARE COMMA paths EQ OSQUARE path_set ESQUARE COMMA assign RPAREN SEMICOL'
print "Polygon with Path"
print p[6]
v = convert_points_list_to_vector(p[6])
print "Path Set List"
print p[12]
for i in p[12] :
print i
mypolygon = doc.addObject('Part::Feature','wire')
path_list = []
for j in i :
j = int(j)
print j
path_list.append(v[j])
# Close path
path_list.append(v[int(i[0])])
print 'Path List'
print path_list
wire = Part.makePolygon(path_list)
mypolygon.Shape = Part.Face(wire)
p[0] = [mypolygon]
# This only pushes last polygon
def make_face(v1,v2,v3):
wire = Part.makePolygon([v1,v2,v3,v1])
face = Part.Face(wire)
return face
def p_polyhedron_action(p) :
'polyhedron_action : polyhedron LPAREN points EQ OSQUARE points_list_3d ESQUARE COMMA triangles EQ OSQUARE points_list_3d ESQUARE COMMA assign RPAREN SEMICOL'
print "Polyhedron Points"
v = []
for i in p[6] :
print i
v.append(FreeCAD.Vector(float(i[0]),float(i[1]),float(i[2])))
print v
print "Polyhedron triangles"
print p[12]
faces_list = []
mypolyhed = doc.addObject('Part::Feature',p[1])
for i in p[12] :
print i
f = make_face(v[int(i[0])],v[int(i[1])],v[int(i[2])])
faces_list.append(f)
shell=Part.makeShell(faces_list)
mypolyhed.Shape=Part.Solid(shell)
p[0] = [mypolyhed]
def p_projection_action(p) :
'projection_action : projection LPAREN cut EQ boolean COMMA assign RPAREN OBRACE block_list EBRACE'
print 'Projection'
from PyQt4 import QtGui
QtGui.QMessageBox.critical(None, "Projection Not yet Coded waiting for Peter Li"," Press OK")

View File

@ -0,0 +1,58 @@
Version History
===============
0.01a - First Version
0.01b - Fixed
Reset Global Variables
Fix Multmatrix use of Shape
Added import Function (Untested)
0.02b - Rewrite to use Bojects rather than shapes.
Objects now editible in FreeCad
0.02c - disable debug output on Yacc to avoid permissions problem with Linux
0.03a - Added support for
Linear Extrusion
Rotational Extrusion
circle
square
polygon
polyhedron
0.04a - Change to use parser stack rather than own stack
to fix a parsing problem
makes code cleaner and less global variables
parsers out SCAD comments ( Not sure if the are allowed in CSG file anyway
other minor fixes
0.04b - Fixes where Multmatrix has more than one object neeed to fuse first
Correct import function call
0.04c - Support for cylinders made with regular polygon ( Needs FreeCAD with fixes to Draft )
Attempt at transformGeometry for non orthoganal Multmatrix
0.04d - Fix to multmatrix transformGeometry to avoid crash
0.04e - If problems with Draft or Draft's makePolygon because not on latest FreeCAD falls back
to non editable polygon
0.04f - Fix bug with multiple intersections
0.05a - Attempt to add shogens's import dxf function code.
0.05b - Fix introduced bug with centre=true/false, ViewObject Proxy
0.05c - Fix for polygon with path - Changes to Polygon and polyhdron - parse projection
There appear to be problems with some files and these maybe as a result of bugs in FreeCAD that corrupt
the model. If you find problem files please email them to keith@sloan-home.co.uk
Thanks to shoogen and Peter Li for their advice, support and expertise.
Keith Sloan
keith@sloan-home.co.uk

View File

@ -0,0 +1,40 @@
February 17, 2011
Announcing : PLY-3.4 (Python Lex-Yacc)
http://www.dabeaz.com/ply
I'm pleased to announce PLY-3.4--a pure Python implementation of the
common parsing tools lex and yacc. PLY-3.4 is a minor bug fix
release. It supports both Python 2 and Python 3.
If you are new to PLY, here are a few highlights:
- PLY is closely modeled after traditional lex/yacc. If you know how
to use these or similar tools in other languages, you will find
PLY to be comparable.
- PLY provides very extensive error reporting and diagnostic
information to assist in parser construction. The original
implementation was developed for instructional purposes. As
a result, the system tries to identify the most common types
of errors made by novice users.
- PLY provides full support for empty productions, error recovery,
precedence rules, and ambiguous grammars.
- Parsing is based on LR-parsing which is fast, memory efficient,
better suited to large grammars, and which has a number of nice
properties when dealing with syntax errors and other parsing
problems. Currently, PLY can build its parsing tables using
either SLR or LALR(1) algorithms.
More information about PLY can be obtained on the PLY webpage at:
http://www.dabeaz.com/ply
PLY is freely available.
Cheers,
David Beazley (http://www.dabeaz.com)

1093
src/Mod/OpenSCAD/ply/CHANGES Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,22 @@
Metadata-Version: 1.0
Name: ply
Version: 3.4
Summary: Python Lex & Yacc
Home-page: http://www.dabeaz.com/ply/
Author: David Beazley
Author-email: dave@dabeaz.com
License: BSD
Description:
PLY is yet another implementation of lex and yacc for Python. Some notable
features include the fact that its implemented entirely in Python and it
uses LALR(1) parsing which is efficient and well suited for larger grammars.
PLY provides most of the standard lex/yacc features including support for empty
productions, precedence rules, error recovery, and support for ambiguous grammars.
PLY is extremely easy to use and provides very extensive error checking.
It is compatible with both Python 2 and Python 3.
Platform: UNKNOWN
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 2

271
src/Mod/OpenSCAD/ply/README Normal file
View File

@ -0,0 +1,271 @@
PLY (Python Lex-Yacc) Version 3.4
Copyright (C) 2001-2011,
David M. Beazley (Dabeaz LLC)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the David Beazley or Dabeaz LLC may be used to
endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Introduction
============
PLY is a 100% Python implementation of the common parsing tools lex
and yacc. Here are a few highlights:
- PLY is very closely modeled after traditional lex/yacc.
If you know how to use these tools in C, you will find PLY
to be similar.
- PLY provides *very* extensive error reporting and diagnostic
information to assist in parser construction. The original
implementation was developed for instructional purposes. As
a result, the system tries to identify the most common types
of errors made by novice users.
- PLY provides full support for empty productions, error recovery,
precedence specifiers, and moderately ambiguous grammars.
- Parsing is based on LR-parsing which is fast, memory efficient,
better suited to large grammars, and which has a number of nice
properties when dealing with syntax errors and other parsing problems.
Currently, PLY builds its parsing tables using the LALR(1)
algorithm used in yacc.
- PLY uses Python introspection features to build lexers and parsers.
This greatly simplifies the task of parser construction since it reduces
the number of files and eliminates the need to run a separate lex/yacc
tool before running your program.
- PLY can be used to build parsers for "real" programming languages.
Although it is not ultra-fast due to its Python implementation,
PLY can be used to parse grammars consisting of several hundred
rules (as might be found for a language like C). The lexer and LR
parser are also reasonably efficient when parsing typically
sized programs. People have used PLY to build parsers for
C, C++, ADA, and other real programming languages.
How to Use
==========
PLY consists of two files : lex.py and yacc.py. These are contained
within the 'ply' directory which may also be used as a Python package.
To use PLY, simply copy the 'ply' directory to your project and import
lex and yacc from the associated 'ply' package. For example:
import ply.lex as lex
import ply.yacc as yacc
Alternatively, you can copy just the files lex.py and yacc.py
individually and use them as modules. For example:
import lex
import yacc
The file setup.py can be used to install ply using distutils.
The file doc/ply.html contains complete documentation on how to use
the system.
The example directory contains several different examples including a
PLY specification for ANSI C as given in K&R 2nd Ed.
A simple example is found at the end of this document
Requirements
============
PLY requires the use of Python 2.2 or greater. However, you should
use the latest Python release if possible. It should work on just
about any platform. PLY has been tested with both CPython and Jython.
It also seems to work with IronPython.
Resources
=========
More information about PLY can be obtained on the PLY webpage at:
http://www.dabeaz.com/ply
For a detailed overview of parsing theory, consult the excellent
book "Compilers : Principles, Techniques, and Tools" by Aho, Sethi, and
Ullman. The topics found in "Lex & Yacc" by Levine, Mason, and Brown
may also be useful.
A Google group for PLY can be found at
http://groups.google.com/group/ply-hack
Acknowledgments
===============
A special thanks is in order for all of the students in CS326 who
suffered through about 25 different versions of these tools :-).
The CHANGES file acknowledges those who have contributed patches.
Elias Ioup did the first implementation of LALR(1) parsing in PLY-1.x.
Andrew Waters and Markus Schoepflin were instrumental in reporting bugs
and testing a revised LALR(1) implementation for PLY-2.0.
Special Note for PLY-3.0
========================
PLY-3.0 the first PLY release to support Python 3. However, backwards
compatibility with Python 2.2 is still preserved. PLY provides dual
Python 2/3 compatibility by restricting its implementation to a common
subset of basic language features. You should not convert PLY using
2to3--it is not necessary and may in fact break the implementation.
Example
=======
Here is a simple example showing a PLY implementation of a calculator
with variables.
# -----------------------------------------------------------------------------
# calc.py
#
# A simple calculator with variables.
# -----------------------------------------------------------------------------
tokens = (
'NAME','NUMBER',
'PLUS','MINUS','TIMES','DIVIDE','EQUALS',
'LPAREN','RPAREN',
)
# Tokens
t_PLUS = r'\+'
t_MINUS = r'-'
t_TIMES = r'\*'
t_DIVIDE = r'/'
t_EQUALS = r'='
t_LPAREN = r'\('
t_RPAREN = r'\)'
t_NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'
def t_NUMBER(t):
r'\d+'
t.value = int(t.value)
return t
# Ignored characters
t_ignore = " \t"
def t_newline(t):
r'\n+'
t.lexer.lineno += t.value.count("\n")
def t_error(t):
print("Illegal character '%s'" % t.value[0])
t.lexer.skip(1)
# Build the lexer
import ply.lex as lex
lex.lex()
# Precedence rules for the arithmetic operators
precedence = (
('left','PLUS','MINUS'),
('left','TIMES','DIVIDE'),
('right','UMINUS'),
)
# dictionary of names (for storing variables)
names = { }
def p_statement_assign(p):
'statement : NAME EQUALS expression'
names[p[1]] = p[3]
def p_statement_expr(p):
'statement : expression'
print(p[1])
def p_expression_binop(p):
'''expression : expression PLUS expression
| expression MINUS expression
| expression TIMES expression
| expression DIVIDE expression'''
if p[2] == '+' : p[0] = p[1] + p[3]
elif p[2] == '-': p[0] = p[1] - p[3]
elif p[2] == '*': p[0] = p[1] * p[3]
elif p[2] == '/': p[0] = p[1] / p[3]
def p_expression_uminus(p):
'expression : MINUS expression %prec UMINUS'
p[0] = -p[2]
def p_expression_group(p):
'expression : LPAREN expression RPAREN'
p[0] = p[2]
def p_expression_number(p):
'expression : NUMBER'
p[0] = p[1]
def p_expression_name(p):
'expression : NAME'
try:
p[0] = names[p[1]]
except LookupError:
print("Undefined name '%s'" % p[1])
p[0] = 0
def p_error(p):
print("Syntax error at '%s'" % p.value)
import ply.yacc as yacc
yacc.yacc()
while 1:
try:
s = raw_input('calc > ') # use input() on Python 3
except EOFError:
break
yacc.parse(s)
Bug Reports and Patches
=======================
My goal with PLY is to simply have a decent lex/yacc implementation
for Python. As a general rule, I don't spend huge amounts of time
working on it unless I receive very specific bug reports and/or
patches to fix problems. I also try to incorporate submitted feature
requests and enhancements into each new version. To contact me about
bugs and/or new features, please send email to dave@dabeaz.com.
In addition there is a Google group for discussing PLY related issues at
http://groups.google.com/group/ply-hack
-- Dave

16
src/Mod/OpenSCAD/ply/TODO Normal file
View File

@ -0,0 +1,16 @@
The PLY to-do list:
1. Finish writing the C Preprocessor module. Started in the
file ply/cpp.py
2. Create and document libraries of useful tokens.
3. Expand the examples/yply tool that parses bison/yacc
files.
4. Think of various diabolical things to do with the
new yacc internals. For example, it is now possible
to specify grammrs using completely different schemes
than the reflection approach used by PLY.

View File

@ -0,0 +1,4 @@
# PLY package
# Author: David Beazley (dave@dabeaz.com)
__all__ = ['lex','yacc']

1058
src/Mod/OpenSCAD/ply/lex.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,31 @@
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
setup(name = "ply",
description="Python Lex & Yacc",
long_description = """
PLY is yet another implementation of lex and yacc for Python. Some notable
features include the fact that its implemented entirely in Python and it
uses LALR(1) parsing which is efficient and well suited for larger grammars.
PLY provides most of the standard lex/yacc features including support for empty
productions, precedence rules, error recovery, and support for ambiguous grammars.
PLY is extremely easy to use and provides very extensive error checking.
It is compatible with both Python 2 and Python 3.
""",
license="""BSD""",
version = "3.4",
author = "David Beazley",
author_email = "dave@dabeaz.com",
maintainer = "David Beazley",
maintainer_email = "dave@dabeaz.com",
url = "http://www.dabeaz.com/ply/",
packages = ['ply'],
classifiers = [
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 2',
]
)

3276
src/Mod/OpenSCAD/ply/yacc.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,688 @@
#!/usr/bin/env python
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License (LGPL)
# as published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
import FreeCAD
import re,math
from OpenSCADFeatures import *
from OpenSCAD2Dgeom import *
from OpenSCADUtils import *
if open.__module__ == '__builtin__':
pythonopen = open # to distinguish python built-in open function from the one declared here
def openscadmesh(doc,scadstr,objname):
import Part,Mesh,os,OpenSCADUtils
tmpfilename=OpenSCADUtils.callopenscadstring(scadstr,'stl')
if tmpfilename:
#mesh1 = doc.getObject(objname) #reuse imported object
Mesh.insert(tmpfilename)
os.unlink(tmpfilename)
mesh1=doc.getObject(objname) #blog
mesh1.ViewObject.hide()
sh=Part.Shape()
sh.makeShapeFromMesh(mesh1.Mesh.Topology,0.1)
solid = Part.Solid(sh)
obj=doc.addObject("Part::FeaturePython",objname)
ImportObject(obj,mesh1) #This object is not mutable from the GUI
ViewProviderTree(obj.ViewObject)
solid=solid.removeSplitter()
if solid.Volume < 0:
solid.complement()
obj.Shape=solid#.removeSplitter()
return obj
else:
print scadstr
class Node:
#fnmin=12 # maximal fn for implicit polygon renderfing
fnmin= FreeCAD.ParamGet(\
"User parameter:BaseApp/Preferences/Mod/OpenSCAD").\
GetInt('useMaxFN')
planedim=1e10 #size of the sqaure used as x-y-plane
def __init__(self,name,arguments=None,children=None,):
pass
self.name=name
self.arguments=arguments or {}
self.children=children or []
def __repr__(self):
str1 ='Node(name=%s' % self.name
if self.arguments:
str1 += ',arguments=%s' % self.arguments
if self.children:
str1 += ',children=%s' % self.children
return str1+')'
def __nonzero__(self):
'''a Node is not obsolent if doesn't have children. Only if as neither name children or
arguments'''
return bool(self.name or self.arguments or self.children)
def __len__(self):
'''return the numer of children'''
return len(self.children)
def __getitem__(self,key):
'''dirct access to the children'''
return self.children.__getitem__(key)
def rlen(self,checkmultmarix=False):
'''Total number of nodes'''
if self.children:
return 1+sum([ch.rlen() for ch in self.children])
else:
return 1
def addtofreecad(self,doc=None,fcpar=None):
def center(obj,x,y,z):
obj.Placement = FreeCAD.Placement(\
FreeCAD.Vector(-x/2.0,-y/2.0,-z/2.0),\
FreeCAD.Rotation(0,0,0,1))
import FreeCAD,Part
if not doc:
doc=FreeCAD.newDocument()
namel=self.name.lower()
multifeature={'union':"Part::MultiFuse",'imp_union':"Part::MultiFuse",
'intersection':"Part::MultiCommon"}
if namel in multifeature:
if len(self.children)>1:
obj=doc.addObject(multifeature[namel],namel)
subobjs = [child.addtofreecad(doc,obj) for child in self.children]
obj.Shapes = subobjs
for subobj in subobjs:
subobj.ViewObject.hide()
elif len(self.children)==1:
obj = self.children[0].addtofreecad(doc,fcpar or True)
else:
obj = fcpar
elif namel == 'difference':
if len(self.children)==1:
obj = self.children[0].addtofreecad(doc,fcpar or True)
else:
obj=doc.addObject("Part::Cut",namel)
base = self.children[0].addtofreecad(doc,obj)
if len(self.children)==2:
tool = self.children[1].addtofreecad(doc,obj)
else:
tool = Node(name='imp_union',\
children=self.children[1:]).addtofreecad(doc,obj)
obj.Base = base
obj.Tool = tool
base.ViewObject.hide()
tool.ViewObject.hide()
elif namel == 'cube':
obj=doc.addObject('Part::Box',namel)
x,y,z=self.arguments['size']
obj.Length=x
obj.Width=y
obj.Height=z
if self.arguments['center']:
center(obj,x,y,z)
elif namel == 'sphere':
obj=doc.addObject("Part::Sphere",namel)
obj.Radius = self.arguments['r']
elif namel == 'cylinder':
h = self.arguments['h']
r1 ,r2 = self.arguments['r1'], self.arguments['r2']
if '$fn' in self.arguments and self.arguments['$fn'] > 2 \
and self.arguments['$fn']<=Node.fnmin:
if r1 == r2:
import Draft
base = Draft.makePolygon(int(self.arguments['$fn']),r1)
obj = doc.addObject("Part::Extrusion",'prism')
obj.Base= base
obj.Dir = (0,0,h)
if self.arguments['center']:
center(obj,0,0,h)
base.ViewObject.hide()
elif True: #use Frustum Feature with makeRuledSurface
obj=doc.addObject("Part::FeaturePython",'frustum')
Frustum(obj,r1,r2,int(self.arguments['$fn']),h)
ViewProviderTree(obj.ViewObject)
if self.arguments['center']:
center(obj,0,0,h)
else: #Use Part::Loft and GetWire Feature
obj=doc.addObject('Part::Loft','frustum')
import Draft
p1 = Draft.makePolygon(int(self.arguments['$fn']),r1)
p2 = Draft.makePolygon(int(self.arguments['$fn']),r2)
if self.arguments['center']:
p1.Placement = FreeCAD.Placement(\
FreeCAD.Vector(0.0,0.0,-h/2.0),FreeCAD.Rotation())
p2.Placement = FreeCAD.Placement(\
FreeCAD.Vector(0.0,0.0,h/2.0),FreeCAD.Rotation())
else:
p2.Placement = FreeCAD.Placement(\
FreeCAD.Vector(0.0,0.0,h),FreeCAD.Rotation())
w1=doc.addObject("Part::FeaturePython",'polygonwire1')
w2=doc.addObject("Part::FeaturePython",'polygonwire2')
GetWire(w1,p1)
GetWire(w2,p2)
ViewProviderTree(w1.ViewObject)
ViewProviderTree(w2.ViewObject)
obj.Sections=[w1,w2]
obj.Solid=True
obj.Ruled=True
p1.ViewObject.hide()
p2.ViewObject.hide()
w1.ViewObject.hide()
w2.ViewObject.hide()
else:
if r1 == r2:
obj=doc.addObject("Part::Cylinder",namel)
obj.Height = h
obj.Radius = r1
else:
obj=doc.addObject("Part::Cone",'cone')
obj.Height = h
obj.Radius1, obj.Radius2 = r1, r2
if self.arguments['center']:
center(obj,0,0,h)
elif namel == 'polyhedron':
obj = doc.addObject("Part::Feature",namel)
points=self.arguments['points']
faces=self.arguments['triangles']
shell=Part.Shell([Part.Face(Part.makePolygon(\
[tuple(points[pointindex]) for pointindex in \
(face+face[0:1])])) for face in faces])
# obj.Shape=Part.Solid(shell).removeSplitter()
solid=Part.Solid(shell).removeSplitter()
if solid.Volume < 0:
# solid.complement()
solid.reverse()
obj.Shape=solid#.removeSplitter()
elif namel == 'polygon':
obj = doc.addObject("Part::Feature",namel)
points=self.arguments['points']
paths = self.arguments.get('paths')
if not paths:
faces=[Part.Face(Part.makePolygon([(x,y,0) for x,y in points+points[0:1]]))]
else:
faces= [Part.Face(Part.makePolygon([(points[pointindex][0],points[pointindex][1],0) for \
pointindex in (path+path[0:1])])) for path in paths]
obj.Shape=subtractfaces(faces)
elif namel == 'square':
obj = doc.addObject("Part::Plane",namel)
x,y = self.arguments['size']
obj.Length = x
obj.Width = y
if self.arguments['center']:
center(obj,x,y,0)
elif namel == 'circle':
r = self.arguments['r']
import Draft
if '$fn' in self.arguments and self.arguments['$fn'] != 0 \
and self.arguments['$fn']<=Node.fnmin:
obj=Draft.makePolygon(int(self.arguments['$fn']),r)
else:
obj=Draft.makeCircle(r) # create a Face
#obj = doc.addObject("Part::Circle",namel);obj.Radius = r
elif namel == 'color':
if len(self.children) == 1:
obj = self.children[0].addtofreecad(doc,fcpar or True)
else:
obj = Node(name='imp_union',\
children=self.children).addtofreecad(doc,fcpar or True)
obj.ViewObject.ShapeColor = tuple([float(p) for p in self.arguments[:3]]) #RGB
transp = 100 - int(math.floor(100*self.arguments[3])) #Alpha
obj.ViewObject.Transparency = transp
elif namel == 'multmatrix':
assert(len(self.children)>0)
m1l=[round(f,12) for f in sum(self.arguments,[])] #Thats the original matrix
m1=FreeCAD.Matrix(*tuple(m1l)) #Thats the original matrix
if isspecialorthogonalpython(fcsubmatrix(m1)): #a Placement can represent the transformation
if len(self.children) == 1:
obj = self.children[0].addtofreecad(doc,fcpar or True)
else:
obj = Node(name='imp_union',\
children=self.children).addtofreecad(doc,fcpar or True)
#FreeCAD.Console.PrintMessage('obj %s\nmat %s/n' % (obj.Placement,m1))
obj.Placement=FreeCAD.Placement(m1).multiply(obj.Placement)
else: #we need to apply the matrix transformation to the Shape using a custom PythonFeature
obj=doc.addObject("Part::FeaturePython",namel)
if len(self.children) == 1:
child = self.children[0].addtofreecad(doc,obj)
else:
child = Node(name='imp_union',\
children=self.children).addtofreecad(doc,obj)
MatrixTransform(obj,m1,child) #This object is not mutable from the GUI
ViewProviderTree(obj.ViewObject)
#elif namel == 'import': pass #Custom Feature
elif namel == 'linear_extrude':
height = self.arguments['height']
twist = self.arguments.get('twist')
if not twist:
obj = doc.addObject("Part::Extrusion",namel)
else: #twist
obj=doc.addObject("Part::FeaturePython",'twist_extrude')
if len(self.children)==0:
base= Node('import',self.arguments).addtofreecad(doc,obj)
elif len(self.children)==1:
base = self.children[0].addtofreecad(doc,obj)
else:
base = Node(name='imp_union',\
children=self.children).addtofreecad(doc,obj)
if False and base.isDerivedFrom('Part::MultiFuse'):
#does not solve all the problems
newobj=doc.addObject("Part::FeaturePython",'refine')
RefineShape(newobj,base)
ViewProviderTree(newobj.ViewObject)
base.ViewObject.hide()
base=newobj
if not twist:
obj.Base= base
obj.Dir = (0,0,height)
else: #twist
Twist(obj,base,height,-twist)
ViewProviderTree(obj.ViewObject)
if self.arguments['center']:
center(obj,0,0,height)
base.ViewObject.hide()
elif namel == 'rotate_extrude':
obj = doc.addObject("Part::Revolution",namel)
if len(self.children)==0:
base= Node('import',self.arguments).addtofreecad(doc,obj)
elif len(self.children)==1:
base = self.children[0].addtofreecad(doc,obj)
else:
base = Node(name='imp_union',\
children=self.children).addtofreecad(doc,obj)
if False and base.isDerivedFrom('Part::MultiFuse'):
#creates 'Axe and meridian are confused' Errors
newobj=doc.addObject("Part::FeaturePython",'refine')
RefineShape(newobj,base)
ViewProviderTree(newobj.ViewObject)
base.ViewObject.hide()
base=newobj
obj.Source= base
obj.Axis = (0.00,1.00,0.00)
obj.Base = (0.00,0.00,0.00)
obj.Angle = 360.00
base.ViewObject.hide()
obj.Placement=FreeCAD.Placement(FreeCAD.Vector(),FreeCAD.Rotation(0,0,90))
elif namel == 'projection':
if self.arguments['cut']:
planename='xy_plane_used_for_project_cut'
obj=doc.addObject('Part::MultiCommon','projection_cut')
plane = doc.getObject(planename)
if not plane:
plane=doc.addObject("Part::Plane",planename)
plane.Length=Node.planedim*2
plane.Width=Node.planedim*2
plane.Placement = FreeCAD.Placement(FreeCAD.Vector(\
-Node.planedim,-Node.planedim,0),FreeCAD.Rotation(0,0,0,1))
#plane.ViewObject.hide()
subobjs = [child.addtofreecad(doc,obj) for child in self.children]
subobjs.append(plane)
obj.Shapes = subobjs
for subobj in subobjs:
subobj.ViewObject.hide()
else:
#Do a proper projection
raise(NotImplementedError)
elif namel == 'import':
filename = self.arguments.get('file')
scale = self.arguments.get('scale')
origin = self.arguments.get('origin')
if filename:
import os
docname=os.path.split(filename)[1]
objname,extension = docname.split('.',1)
if not os.path.isabs(filename):
try:
global lastimportpath
filename=os.path.join(lastimportpath,filename)
except:
raise #no path given
# Check for a mesh fileformat support by the Mesh mddule
if extension.lower() in reverseimporttypes()['Mesh']:
import Mesh
mesh1 = doc.getObject(objname) #reuse imported object
if not mesh1:
Mesh.insert(filename)
mesh1=doc.getObject(objname)
mesh1.ViewObject.hide()
sh=Part.Shape()
sh.makeShapeFromMesh(mesh1.Mesh.Topology,0.1)
solid = Part.Solid(sh)
obj=doc.addObject("Part::FeaturePython",'import_%s_%s'%(extension,objname))
#obj=doc.addObject('Part::Feature',)
ImportObject(obj,mesh1) #This object is not mutable from the GUI
ViewProviderTree(obj.ViewObject)
solid=solid.removeSplitter()
if solid.Volume < 0:
#sh.reverse()
#sh = sh.copy()
solid.complement()
obj.Shape=solid#.removeSplitter()
elif extension in ['dxf']:
layera = self.arguments.get('layer')
featname='import_dxf_%s_%s'%(objname,layera)
# reusing an allready imported object does not work if the
#shape in not yet calculated
import importDXF
global dxfcache
layers=dxfcache.get(id(doc),[])
if layers:
try:
groupobj=[go for go in layers if (not layera) or go.Label == layera]
except:
groupobj= None
else:
groupobj= None
if not groupobj:
groupname=objname
layers = importDXF.processdxf(doc,filename) or importDXF.layers
dxfcache[id(doc)] = layers[:]
for l in layers:
for o in l.Group:
o.ViewObject.hide()
l.ViewObject.hide()
groupobj=[go for go in layers if (not layera) or go.Label == layera]
edges=[]
for shapeobj in groupobj[0].Group:
edges.extend(shapeobj.Shape.Edges)
try:
f=edgestofaces(edges)
except:
FreeCAD.Console.PrintError(\
'processing of dxf import faild\nPlease rework \'%s\' manualy\n' % layera)
f=Part.Shape() #empty Shape
obj=doc.addObject("Part::FeaturePython",'import_dxf_%s_%s'%(objname,layera))
#obj=doc.addObject('Part::Feature',)
ImportObject(obj,groupobj[0]) #This object is not mutable from the GUI
ViewProviderTree(obj.ViewObject)
obj.Shape=f
else:
FreeCAD.Console.ErrorMessage(\
'Filetype of %s not supported\n' % (filename))
raise(NotImplementedError)
if obj: #handle origin and scale
if scale is not None and scale !=1:
child = obj
m1=FreeCAD.Matrix()
m1.scale(scale,scale,scale)
obj=doc.addObject("Part::FeaturePython",'scale_import')
MatrixTransform(obj,m1,child) #This object is not mutable from the GUI
ViewProviderTree(obj.ViewObject)
elif origin is not None and any([c != 0 for c in origin]):
placement=FreeCAD.Placement(FreeCAD.Vector(*origin),FreeCAD.Rotation())
obj.Placement=placement.multiply(obj.Placement)
else:
FreeCAD.Console.ErrorMessage('Import of %s failed\n' % (filename))
elif namel == 'minkowski':
childrennames=[child.name.lower() for child in self.children]
if len(self.children) == 2 and \
childrennames.count('cube')==1 and \
(childrennames.count('sphere') + \
childrennames.count('cylinder')) == 1:
if self.children[0].name.lower() == 'cube':
cube = self.children[0]
roundobj = self.children[1]
elif self.children[1].name.lower() == 'cube':
cube = self.children[1]
roundobj = self.children[0]
roundobjname=roundobj.name.lower()
issphere = roundobjname == 'sphere'
cubeobj=doc.addObject('Part::Box','roundedcube')
x,y,z=cube.arguments['size']
r=roundobj.arguments.get('r') or \
roundobj.arguments.get('r1')
cubeobj.Length=x+2*r
cubeobj.Width=y+2*r
cubeobj.Height=z+2*r*issphere
obj=doc.addObject("Part::Fillet","%s_%s"%(namel,roundobjname))
obj.Base = cubeobj
cubeobj.ViewObject.hide()
if issphere:
obj.Edges = [(i,r,r) for i in range(1,13)]
else:#cylinder
obj.Edges = [(i,r,r) for i in [1,3,5,7]]
if cube.arguments['center']:
center(cubeobj,x+2*r,y+2*r,z+2*r*issphere)
else: #htandle a rotated cylinder
#OffsetShape
raise(NotImplementedError)
elif childrennames.count('sphere')==1:
sphereindex=childrennames.index('sphere')
sphere=self.children[sphereindex]
offset=sphere.arguments['r']
nonsphere=self.children[0:sphereindex]+\
self.sphere[sphereindex+1:]
obj=doc.addObject("Part::FeaturePython",'Offset')
if len(nonsphere) == 1:
child = nonsphere[0].addtofreecad(doc,obj)
else:
child = Node(name='imp_union',\
children=nonsphere).addtofreecad(doc,obj)
OffsetShape(obj,child,offset)
ViewProviderTree(obj.ViewObject)
elif False:
raise(NotImplementedError)
pass # handle rotated cylinders and select edges that
#radius = radius0 * m1.multiply(FreeCAD.Vector(0,0,1)).dot(edge.Curve.tangent(0)[0])
else:
raise(NotImplementedError)
elif namel == 'surface':
import os
scadstr = 'surface(file = "%s", center = %s );' % \
(self.arguments['file'], 'true' if self.arguments['center'] else 'false')
docname=os.path.split(self.arguments['file'])[1]
objname,extension = docname.split('.',1)
obj = openscadmesh(doc,scadstr,objname)
elif namel in ['glide','hull']:
raise(NotImplementedError)
elif namel in ['render','subdiv'] or True:
lenchld=len(self.children)
if lenchld == 1:
FreeCAD.Console.PrintMessage('Not recognized %s\n' % (self))
obj = self.children[0].addtofreecad(doc,fcpar)
elif lenchld >1:
obj = Node(name='imp_union',\
children=self.children).addtofreecad(doc,fcpar or True)
else:
obj = doc.addObject("Part::Feature",'Not_Impl_%s'%namel)
if fcpar == True: #We are the last real object, our parent is not rendered.
return obj
if fcpar:
try:
obj.ViewObject.hide()
except:
raise
if True: #never refine the Shape, as it itroduces crashes
return obj
else: #refine Shape
import Draft
if obj.Type =='Part::Extrusion' and obj.Base.Type == 'Part::Part2DObjectPython' and \
isinstance(obj.Base.Proxy,Draft._Polygon) or \
(not obj.isDerivedFrom('Part::Extrusion') and \
not obj.isDerivedFrom('Part::Boolean') and \
not obj.isDerivedFrom('Part::Cut') and \
not obj.isDerivedFrom('Part::MultiCommon') and \
not obj.isDerivedFrom('Part::MultiFuse') and \
not obj.isDerivedFrom('Part::Revolution') ) \
or (obj.isDerivedFrom('Part::FeaturePython') and isinstance(obj.Proxy,RefineShape)):
return obj
else:
newobj=doc.addObject("Part::FeaturePython",'refine')
RefineShape(newobj,obj)
ViewProviderTree(newobj.ViewObject)
obj.ViewObject.hide()
return newobj
else:
doc.recompute()
def flattengroups(self,name='group'):
"""removes group node with only one child and no arguments and empty groups"""
node=self
while (node.name==name and len(node.children)==1 and len(node.arguments)==0):
node=node.children[0]
node.children=[child for child in node.children if not (len(child.children)==0 and child.name==name)]
if node.children:
node.children = [child.flattengroups() for child in node.children]
return node
def pprint(self,level=0):
"""prints the indented tree"""
if self.arguments:
argstr = ' (%s)' % self.arguments
else:
argstr = ''
print '%s %s%s' %(' '*level,self.name,argstr)
for child in self.children:
child.pprint(level+1)
def pprint2(self,path='root',pathjust=24):
"""prints the tree. Left column contains the the systax to access a child"""
if self.arguments:
argstr = ' (%s)' % self.arguments
else:
argstr = ''
print '%s %s%s' %(path.ljust(pathjust),self.name,argstr)
for i,child in enumerate(self.children):
child.pprint2('%s[%d]'%(path,i),pathjust)
def parseexpression(e):
e=e.strip()
el = e.lower()
if len(el)==0: return None
if el == 'true': return True
elif el == 'false': return False
elif el == 'undef': return None
elif e[0].isdigit() or e[0] == '-' and len(e)>1 and e[1].isdigit():
try:
return float(e)
except ValueError:
import FreeCAD
FreeCAD.Console.PrintMessage('%s\n' % (el))
return 1.0
elif el.startswith('"'): return e.strip('"') #string literal
elif el.startswith('['):
bopen, bclose = e.count('['), e.count(']')
if bopen == bclose:
return eval(el)
else:
import FreeCAD
FreeCAD.Console.PrintMessage('%s\n' % (el))
#return eval(el)
#assert(False) #Malformed
else:
return e #Return the string
def parseargs(argstring):
if '=' in argstring:
level=0
tok=[]
a=[]
for i,char in enumerate(argstring):
if char=='[': level+=1
elif char ==']': level -=1
if level==0 and (char=='=' or char==','):
tok.append(''.join(a).strip())
a=[]
else:
a.append(char)
tok.append(''.join(a).strip())
#print tok
argdict=dict(zip(tok[0::2],[parseexpression(argstring) for argstring in tok[1::2]]))
# argdict={}
# for key, value in re.findall(r"(\$?\w+)\s*=\s*(\[?\w+]?),?\s*",argstring):
# argdict[key] = parseexpression(value)
return argdict
else:
return parseexpression(argstring)
def parsenode(str1):
name,str2=str1.strip().split('(',1)
assert('}' not in name)
name=name.strip('#!%* ')#remove/ignore modifiers
args,str3=str2.split(')',1)
str4=str3.lstrip()
if str4.startswith(';'):
#has no children
nextelement=str4[1:].lstrip()
return Node(name,parseargs(args)),nextelement
elif str4.startswith('{'):
#has children
level=0
for index,char in enumerate(str4):
if char == '{': level += 1
elif char == '}': level -= 1
if level == 0:
break
#end of children
childstr= str4[1:index].strip()
nextelement = str4[index+1:].lstrip()
bopen,bclose=childstr.count('{'),childstr.count('}')
assert(bopen == bclose)
children=[]
while childstr:
try:
childnode,childstr=parsenode(childstr)
children.append(childnode)
except ValueError:
raise
if args:
args=parseargs(args)
return Node(name,args,children),nextelement
def readfile(filename):
import os
global lastimportpath
lastimportpath,relname = os.path.split(filename)
isopenscad = relname.lower().endswith('.scad')
if isopenscad:
tmpfile=callopenscad(filename)
lastimportpath = os.getcwd() #https://github.com/openscad/openscad/issues/128
f = pythonopen(tmpfile)
else:
f = pythonopen(filename)
rootnode=parsenode(f.read())[0]
f.close()
if isopenscad:
os.unlink(tmpfile)
return rootnode.flattengroups()
def open(filename):
import os
docname=os.path.split(filename)[1]
doc=FreeCAD.newDocument(docname)
doc.Label = (docname.split('.',1)[0])
readfile(filename).addtofreecad(doc)
#doc.recompute()
return doc
def insert(filename,docname):
try:
doc=FreeCAD.getDocument(docname)
except:
doc=FreeCAD.newDocument(docname)
readfile(filename).addtofreecad(doc)
#doc.recompute()
import FreeCAD
global dxfcache
dxfcache = {}

View File

@ -0,0 +1,31 @@
def replaceobj(parent,oldchild,newchild):
for propname in parent.PropertiesList:
propvalue=parent.getPropertyByName(propname)
if type(propvalue) == list:
for dontcare in range(propvalue.count(oldchild)):
propvalue[propvalue.index(oldchild)] = newchild
setattr(parent,propname,propvalue)
#print propname, parent.getPropertyByName(propname)
else:
if propvalue == oldchild:
setattr(parent,propname,newchild)
print propname, parent.getPropertyByName(propname)
#else: print propname,propvalue
def replaceobjfromselection(objs):
assert(len(objs)==3)
if objs[0] in objs[1].InList: parent, oldchild, newchild = objs
elif objs[0] in objs[2].InList: parent, newchild, oldchild = objs
elif objs[1] in objs[0].InList: oldchild, parent, newchild = objs
elif objs[1] in objs[2].InList: newchild, parent, oldchild = objs
elif objs[2] in objs[0].InList: oldchild, newchild, parent = objs
elif objs[2] in objs[1].InList: newchild, oldchild, parent = objs
else: assert(False)
replaceobj(parent,oldchild,newchild)
if __name__ == '__main__':
import FreeCAD,FreeCADGui
objs=[selobj.Object for selobj in FreeCADGui.Selection.getSelectionEx()]
replaceobjfromselection(objs)

View File

@ -0,0 +1,132 @@
# -*- coding: utf8 -*-
#***************************************************************************
#* *
#* Copyright (c) 2012 Keith Sloan <keith@sloan-home.co.uk> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU General Public License (GPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* 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 Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
__title__="FreeCAD OpenSCAD Workbench - CSG importer Version 0.5c"
__author__ = "Keith Sloan <keith@sloan-home.co.uk>"
__url__ = ["http://www.sloan-home.co.uk/ImportCSG"]
# Reserved words
reserved = (
'group',
'sphere',
'cylinder',
'cube',
'size',
'multmatrix',
'intersection',
'difference',
'union',
'rotate_extrude',
'linear_extrude',
'center',
'true',
'false',
'circle',
'square',
'polygon',
'paths',
'points',
'undef',
'polyhedron',
'triangles',
'render',
'surface',
'subdiv',
'glide',
'hull',
'minkowski',
'projection',
'import_stl',
'import_dxf',
'import',
'origin',
'layer',
'file',
'color',
'cut',
)
# List of token names. This is always required
tokens = reserved + (
'WORD',
'NUMBER',
'LPAREN',
'RPAREN',
'OBRACE',
'EBRACE',
'OSQUARE',
'ESQUARE',
'COMMA',
'SEMICOL',
'EQ',
'STRING',
'ID',
'DOT'
)
# Regular expression rules for simple tokens
t_WORD = r'[$]?[a-zA-Z_]+[0-9]*'
t_NUMBER = r'[-]?[0-9]*[\.]*[0-9]+([eE]-?[0-9]+)*'
t_LPAREN = r'\('
t_RPAREN = r'\)'
t_OBRACE = r'{'
t_EBRACE = r'\}'
t_OSQUARE = r'\['
t_ESQUARE = r'\]'
t_COMMA = r','
t_SEMICOL = r';'
t_EQ = r'='
t_DOT = r'\.'
t_STRING = r'"[^"]*"'
#t_STRING = r'["]+[a-zA-Z.]+["]+'
# Deal with Reserved words
reserved_map = { }
for r in reserved:
reserved_map[r.lower()] = r
# Deal with Comments
def t_comment1(t) :
r'//[^\r\n]*((\r\n)|<<EOF>>)'
pass
def t_comment2(t) :
r'//[^\n]*((\n)|<<EOF>>)'
pass
def t_ID(t):
r'[$]?[a-zA-Z_]+[0-9]*'
t.type = reserved_map.get(t.value, "ID")
return t
# Define a rule so we can track line numbers
def t_newline(t):
r'\n+'
t.lexer.lineno += len(t.value)
# A string containing ignored characters (spaces and tabs)
t_ignore = " \t\r"
# Error handling rule
def t_error(t):
print "Illegal character '%s'" % t.value[0]
t.lexer.skip(1)

View File

@ -159,6 +159,8 @@
<ComponentRef Id="CompModStartLibStartPage" />
<ComponentRef Id="CompModStartDataStartPage" />
<ComponentRef Id="CompModWeb" />
<ComponentRef Id="CompModOpenSCAD" />
<ComponentRef Id="CompModOpenSCADPly" />
<ComponentRef Id="CompExampleData" />
<Feature Id="FeatModImage" Title="The Image module" Description="Module to handle pictures" Level="1">

View File

@ -52,6 +52,7 @@
<?include ModStart.wxi ?>
<?include ModWeb.wxi ?>
<?include ModArch.wxi ?>
<?include ModOpenSCAD.wxi ?>
</DirectoryRef>
</Fragment>
</Wix>

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?><!--
(c) Juergen Riegel (FreeCAD@juergen-riegel.net) 2012
This file is part of the FreeCAD CAx development system.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Library General Public License (LGPL)
as published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
for detail see the LICENCE text file.
FreeCAD 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 Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with FreeCAD; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
Juergen Riegel 2012
-->
<Include xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Directory Id="ModOpenSCAD" Name="OpenSCAD" FileSource="../../Mod/OpenSCAD">
<Component Id="CompModOpenSCAD" Guid="54fb73e3-7c3a-4a43-b376-008dfae6d103" Win64='$(var.Win_64)' KeyPath="yes">
<File Id="OpenSCADInitPy" Name="Init.py" DiskId="1" />
<File Id="OpenSCADInitGuiPy" Name="InitGui.py" DiskId="1" />
<File Id="colorcodeshapesPy" Name="colorcodeshapes.py" DiskId="1" />
<File Id="expandplacementspy" Name="expandplacements.py" DiskId="1" />
<File Id="exportCSGpy" Name="exportCSG.py" DiskId="1" />
<File Id="importCSGpy" Name="importCSG.py" DiskId="1" />
<File Id="OpenSCAD_rcpy" Name="OpenSCAD_rc.py" DiskId="1" />
<File Id="OpenSCAD2Dgeompy" Name="OpenSCAD2Dgeom.py" DiskId="1" />
<File Id="OpenSCADCommandspy" Name="OpenSCADCommandspy" DiskId="1" />
<File Id="OpenSCADFeaturespy" Name="OpenSCADFeatures.py" DiskId="1" />
<File Id="OpenSCADUtilspy" Name="OpenSCADUtils.py" DiskId="1" />
<File Id="prototypepy" Name="prototype.py" DiskId="1" />
<File Id="replaceobjpy" Name="replaceobj.py" DiskId="1" />
<File Id="tokrulespy" Name="tokrules.py" DiskId="1" />
</Component>
<Directory Id="ModOpenSCADPly" Name="OpenSCADPly" FileSource="../../Mod/OpenSCAD/ply">
<Component Id="CompModOpenSCADPly" Guid="870b896b-30f8-4d3b-b923-36536c0c2859" Win64='$(var.Win_64)' KeyPath="yes">
<File Id="OpenSCADLibInitPy" Name="__init__.py" DiskId="1" />
<File Id="OpenSCADLibREADME" Name="README" DiskId="1" />
<File Id="OpenSCADLibyaccpy" Name="yacc.py" DiskId="1" />
<File Id="OpenSCADLiblexpy" Name="lex.py" DiskId="1" />
</Component>
</Directory>
</Directory>
</Include>