Merge pull request #43 from jmwright/master
Suuuhhhh Weet! thanks for contributing this feature!
This commit is contained in:
commit
8041accf9f
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
*.pyc
|
*.pyc
|
||||||
doc/_build/*
|
doc/_build/*
|
||||||
dist/*
|
dist/*
|
||||||
|
.idea/*
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
Metadata-Version: 1.0
|
Metadata-Version: 1.1
|
||||||
Name: cadquery
|
Name: cadquery
|
||||||
Version: 0.1.5
|
Version: 0.1.6
|
||||||
Summary: CadQuery is a parametric scripting language for creating and traversing CAD models
|
Summary: CadQuery is a parametric scripting language for creating and traversing CAD models
|
||||||
Home-page: https://github.com/dcowden/cadquery
|
Home-page: https://github.com/dcowden/cadquery
|
||||||
Author: David Cowden
|
Author: David Cowden
|
||||||
|
|
|
@ -16,6 +16,7 @@ cadquery/contrib/__init__.py
|
||||||
cadquery/freecad_impl/__init__.py
|
cadquery/freecad_impl/__init__.py
|
||||||
cadquery/freecad_impl/exporters.py
|
cadquery/freecad_impl/exporters.py
|
||||||
cadquery/freecad_impl/geom.py
|
cadquery/freecad_impl/geom.py
|
||||||
|
cadquery/freecad_impl/importers.py
|
||||||
cadquery/freecad_impl/shapes.py
|
cadquery/freecad_impl/shapes.py
|
||||||
cadquery/freecad_impl/verutil.py
|
cadquery/freecad_impl/verutil.py
|
||||||
cadquery/plugins/__init__.py
|
cadquery/plugins/__init__.py
|
||||||
|
@ -23,6 +24,7 @@ tests/TestCQSelectors.py
|
||||||
tests/TestCadObjects.py
|
tests/TestCadObjects.py
|
||||||
tests/TestCadQuery.py
|
tests/TestCadQuery.py
|
||||||
tests/TestExporters.py
|
tests/TestExporters.py
|
||||||
|
tests/TestImporters.py
|
||||||
tests/TestImports.py
|
tests/TestImports.py
|
||||||
tests/TestWorkplanes.py
|
tests/TestWorkplanes.py
|
||||||
tests/__init__.py
|
tests/__init__.py
|
|
@ -1868,6 +1868,55 @@ class Workplane(CQ):
|
||||||
else:
|
else:
|
||||||
return self.newObject([r])
|
return self.newObject([r])
|
||||||
|
|
||||||
|
def revolve(self, angleDegrees=360.0, axisStart=None, axisEnd=None, combine=True):
|
||||||
|
"""
|
||||||
|
Use all un-revolved wires in the parent chain to create a solid.
|
||||||
|
|
||||||
|
:param angleDegrees: the angle to revolve through.
|
||||||
|
:type angleDegrees: float, anything less than 360 degrees will leave the shape open
|
||||||
|
:param axisStart: the start point of the axis of rotation
|
||||||
|
:type axisStart: tuple, a two tuple
|
||||||
|
:param axisEnd: the end point of the axis of rotation
|
||||||
|
:type axisEnd: tuple, a two tuple
|
||||||
|
:param combine: True to combine the resulting solid with parent solids if found.
|
||||||
|
:type combine: boolean, combine with parent solid
|
||||||
|
:return: a CQ object with the resulting solid selected.
|
||||||
|
|
||||||
|
The returned object is always a CQ object, and depends on wither combine is True, and
|
||||||
|
whether a context solid is already defined:
|
||||||
|
|
||||||
|
* if combine is False, the new value is pushed onto the stack.
|
||||||
|
* if combine is true, the value is combined with the context solid if it exists,
|
||||||
|
and the resulting solid becomes the new context solid.
|
||||||
|
"""
|
||||||
|
#Make sure we account for users specifying angles larger than 360 degrees
|
||||||
|
angleDegrees = angleDegrees % 360.0
|
||||||
|
|
||||||
|
#Compensate for FreeCAD not assuming that a 0 degree revolve means a 360 degree revolve
|
||||||
|
angleDegrees = 360.0 if angleDegrees == 0 else angleDegrees
|
||||||
|
|
||||||
|
#The default start point of the vector defining the axis of rotation will be the origin of the workplane
|
||||||
|
if axisStart is None:
|
||||||
|
axisStart = self.plane.toWorldCoords((0,0)).toTuple()
|
||||||
|
else:
|
||||||
|
axisStart = self.plane.toWorldCoords(axisStart).toTuple()
|
||||||
|
|
||||||
|
#The default end point of the vector defining the axis of rotation should be along the normal from the plane
|
||||||
|
if axisEnd is None:
|
||||||
|
#Make sure we match the user's assumed axis of rotation if they specified an start but not an end
|
||||||
|
if axisStart[1] != 0:
|
||||||
|
axisEnd = self.plane.toWorldCoords((0,axisStart[1])).toTuple()
|
||||||
|
else:
|
||||||
|
axisEnd = self.plane.toWorldCoords((0,1)).toTuple()
|
||||||
|
else:
|
||||||
|
axisEnd = self.plane.toWorldCoords(axisEnd).toTuple()
|
||||||
|
|
||||||
|
r = self._revolve(angleDegrees, axisStart, axisEnd) # returns a Solid ( or a compound if there were multiple )
|
||||||
|
if combine:
|
||||||
|
return self._combineWithBase(r)
|
||||||
|
else:
|
||||||
|
return self.newObject([r])
|
||||||
|
|
||||||
def _combineWithBase2(self,obj):
|
def _combineWithBase2(self,obj):
|
||||||
"""
|
"""
|
||||||
Combines the provided object with the base solid, if one can be found.
|
Combines the provided object with the base solid, if one can be found.
|
||||||
|
@ -2105,6 +2154,33 @@ class Workplane(CQ):
|
||||||
|
|
||||||
return Compound.makeCompound(toFuse)
|
return Compound.makeCompound(toFuse)
|
||||||
|
|
||||||
|
def _revolve(self, angleDegrees, axisStart, axisEnd):
|
||||||
|
"""
|
||||||
|
Make a solid from the existing set of pending wires.
|
||||||
|
|
||||||
|
:param angleDegrees: the angle to revolve through.
|
||||||
|
:type angleDegrees: float, anything less than 360 degrees will leave the shape open
|
||||||
|
:param axisStart: the start point of the axis of rotation
|
||||||
|
:type axisStart: tuple, a two tuple
|
||||||
|
:param axisEnd: the end point of the axis of rotation
|
||||||
|
:type axisEnd: tuple, a two tuple
|
||||||
|
:return: a FreeCAD solid, suitable for boolean operations.
|
||||||
|
|
||||||
|
This method is a utility method, primarily for plugin and internal use.
|
||||||
|
"""
|
||||||
|
#We have to gather the wires to be revolved
|
||||||
|
wireSets = sortWiresByBuildOrder(list(self.ctx.pendingWires),self.plane,[])
|
||||||
|
|
||||||
|
#Mark that all of the wires have been used to create a revolution
|
||||||
|
self.ctx.pendingWires = []
|
||||||
|
|
||||||
|
#Revolve the wires, make a compound out of them and then fuse them
|
||||||
|
toFuse = []
|
||||||
|
for ws in wireSets:
|
||||||
|
thisObj = Solid.revolve(ws[0], ws[1:], angleDegrees, axisStart, axisEnd)
|
||||||
|
toFuse.append(thisObj)
|
||||||
|
|
||||||
|
return Compound.makeCompound(toFuse)
|
||||||
|
|
||||||
def box(self,length,width,height,centered=(True,True,True),combine=True):
|
def box(self,length,width,height,centered=(True,True,True),combine=True):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -47,45 +47,47 @@
|
||||||
object each one returns, so these are better grouped by the type of object they return.
|
object each one returns, so these are better grouped by the type of object they return.
|
||||||
(who would know that Part.makeCircle() returns an Edge, but Part.makePolygon() returns a Wire ?
|
(who would know that Part.makeCircle() returns an Edge, but Part.makePolygon() returns a Wire ?
|
||||||
"""
|
"""
|
||||||
from cadquery import Vector,BoundBox
|
from cadquery import Vector, BoundBox
|
||||||
import FreeCAD
|
import FreeCAD
|
||||||
|
|
||||||
from .verutil import fc_import
|
from .verutil import fc_import
|
||||||
|
|
||||||
FreeCADPart = fc_import("FreeCAD.Part")
|
FreeCADPart = fc_import("FreeCAD.Part")
|
||||||
|
|
||||||
|
|
||||||
class Shape(object):
|
class Shape(object):
|
||||||
"""
|
"""
|
||||||
Represents a shape in the system.
|
Represents a shape in the system.
|
||||||
Wrappers the FreeCAD api
|
Wrappers the FreeCAD api
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self,obj):
|
def __init__(self, obj):
|
||||||
self.wrapped = obj
|
self.wrapped = obj
|
||||||
self.forConstruction = False
|
self.forConstruction = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def cast(cls,obj,forConstruction = False):
|
def cast(cls, obj, forConstruction=False):
|
||||||
"Returns the right type of wrapper, given a FreeCAD object"
|
"Returns the right type of wrapper, given a FreeCAD object"
|
||||||
s = obj.ShapeType
|
s = obj.ShapeType
|
||||||
if type(obj) == FreeCAD.Base.Vector:
|
if type(obj) == FreeCAD.Base.Vector:
|
||||||
return Vector(obj)
|
return Vector(obj)
|
||||||
tr = None
|
tr = None
|
||||||
|
|
||||||
#TODO: there is a clever way to do this i'm sure with a lookup
|
# TODO: there is a clever way to do this i'm sure with a lookup
|
||||||
#but it is not a perfect mapping, because we are trying to hide
|
# but it is not a perfect mapping, because we are trying to hide
|
||||||
#a bit of the complexity of Compounds in FreeCAD.
|
# a bit of the complexity of Compounds in FreeCAD.
|
||||||
if s == 'Vertex':
|
if s == 'Vertex':
|
||||||
tr= Vertex(obj)
|
tr = Vertex(obj)
|
||||||
elif s == 'Edge':
|
elif s == 'Edge':
|
||||||
tr= Edge(obj)
|
tr = Edge(obj)
|
||||||
elif s == 'Wire':
|
elif s == 'Wire':
|
||||||
tr = Wire(obj)
|
tr = Wire(obj)
|
||||||
elif s == 'Face':
|
elif s == 'Face':
|
||||||
tr= Face(obj)
|
tr = Face(obj)
|
||||||
elif s == 'Shell':
|
elif s == 'Shell':
|
||||||
tr= Shell(obj)
|
tr = Shell(obj)
|
||||||
elif s == 'Solid':
|
elif s == 'Solid':
|
||||||
tr= Solid(obj)
|
tr = Solid(obj)
|
||||||
elif s == 'Compound':
|
elif s == 'Compound':
|
||||||
#compound of solids, lets return a solid instead
|
#compound of solids, lets return a solid instead
|
||||||
if len(obj.Solids) > 1:
|
if len(obj.Solids) > 1:
|
||||||
|
@ -95,22 +97,23 @@ class Shape(object):
|
||||||
elif len(obj.Wires) > 0:
|
elif len(obj.Wires) > 0:
|
||||||
tr = Wire(obj)
|
tr = Wire(obj)
|
||||||
else:
|
else:
|
||||||
tr= Compound(obj)
|
tr = Compound(obj)
|
||||||
else:
|
else:
|
||||||
raise ValueError("cast:unknown shape type %s" % s)
|
raise ValueError("cast:unknown shape type %s" % s)
|
||||||
|
|
||||||
tr.forConstruction = forConstruction
|
tr.forConstruction = forConstruction
|
||||||
return tr
|
return tr
|
||||||
#TODO: all these should move into the exporters folder.
|
|
||||||
#we dont need a bunch of exporting code stored in here!
|
# TODO: all these should move into the exporters folder.
|
||||||
|
# we dont need a bunch of exporting code stored in here!
|
||||||
#
|
#
|
||||||
def exportStl(self,fileName):
|
def exportStl(self, fileName):
|
||||||
self.wrapped.exportStl(fileName)
|
self.wrapped.exportStl(fileName)
|
||||||
|
|
||||||
def exportStep(self,fileName):
|
def exportStep(self, fileName):
|
||||||
self.wrapped.exportStep(fileName)
|
self.wrapped.exportStep(fileName)
|
||||||
|
|
||||||
def exportShape(self,fileName, fileFormat):
|
def exportShape(self, fileName, fileFormat):
|
||||||
if fileFormat == ExportFormats.STL:
|
if fileFormat == ExportFormats.STL:
|
||||||
self.wrapped.exportStl(fileName)
|
self.wrapped.exportStl(fileName)
|
||||||
elif fileFormat == ExportFormats.BREP:
|
elif fileFormat == ExportFormats.BREP:
|
||||||
|
@ -118,7 +121,7 @@ class Shape(object):
|
||||||
elif fileFormat == ExportFormats.STEP:
|
elif fileFormat == ExportFormats.STEP:
|
||||||
self.wrapped.exportStep(fileName)
|
self.wrapped.exportStep(fileName)
|
||||||
elif fileFormat == ExportFormats.AMF:
|
elif fileFormat == ExportFormats.AMF:
|
||||||
#not built into FreeCAD
|
# not built into FreeCAD
|
||||||
#TODO: user selected tolerance
|
#TODO: user selected tolerance
|
||||||
tess = self.wrapped.tessellate(0.1)
|
tess = self.wrapped.tessellate(0.1)
|
||||||
aw = amfUtils.AMFWriter(tess)
|
aw = amfUtils.AMFWriter(tess)
|
||||||
|
@ -154,14 +157,14 @@ class Shape(object):
|
||||||
"""
|
"""
|
||||||
return self.wrapped.ShapeType
|
return self.wrapped.ShapeType
|
||||||
|
|
||||||
def isType(self,obj,strType):
|
def isType(self, obj, strType):
|
||||||
"""
|
"""
|
||||||
Returns True if the shape is the specified type, false otherwise
|
Returns True if the shape is the specified type, false otherwise
|
||||||
|
|
||||||
contrast with ShapeType, which will raise an exception
|
contrast with ShapeType, which will raise an exception
|
||||||
if the provide object is not a shape at all
|
if the provide object is not a shape at all
|
||||||
"""
|
"""
|
||||||
if hasattr(obj,'ShapeType'):
|
if hasattr(obj, 'ShapeType'):
|
||||||
return obj.ShapeType == strType
|
return obj.ShapeType == strType
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
@ -172,10 +175,10 @@ class Shape(object):
|
||||||
def isNull(self):
|
def isNull(self):
|
||||||
return self.wrapped.isNull()
|
return self.wrapped.isNull()
|
||||||
|
|
||||||
def isSame(self,other):
|
def isSame(self, other):
|
||||||
return self.wrapped.isSame(other.wrapped)
|
return self.wrapped.isSame(other.wrapped)
|
||||||
|
|
||||||
def isEqual(self,other):
|
def isEqual(self, other):
|
||||||
return self.wrapped.isEqual(other.wrapped)
|
return self.wrapped.isEqual(other.wrapped)
|
||||||
|
|
||||||
def isValid(self):
|
def isValid(self):
|
||||||
|
@ -189,6 +192,7 @@ class Shape(object):
|
||||||
return Vector(self.wrapped.CenterOfMass)
|
return Vector(self.wrapped.CenterOfMass)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def Closed(self):
|
def Closed(self):
|
||||||
return self.wrapped.Closed
|
return self.wrapped.Closed
|
||||||
|
|
||||||
|
@ -222,7 +226,7 @@ class Shape(object):
|
||||||
def Length(self):
|
def Length(self):
|
||||||
return self.wrapped.Length
|
return self.wrapped.Length
|
||||||
|
|
||||||
def rotate(self,startVector,endVector,angleDegrees):
|
def rotate(self, startVector, endVector, angleDegrees):
|
||||||
"""
|
"""
|
||||||
Rotates a shape around an axis
|
Rotates a shape around an axis
|
||||||
:param startVector: start point of rotation axis either a 3-tuple or a Vector
|
:param startVector: start point of rotation axis either a 3-tuple or a Vector
|
||||||
|
@ -237,10 +241,10 @@ class Shape(object):
|
||||||
endVector = Vector(endVector)
|
endVector = Vector(endVector)
|
||||||
|
|
||||||
tmp = self.wrapped.copy()
|
tmp = self.wrapped.copy()
|
||||||
tmp.rotate(startVector.wrapped,endVector.wrapped,angleDegrees)
|
tmp.rotate(startVector.wrapped, endVector.wrapped, angleDegrees)
|
||||||
return Shape.cast(tmp)
|
return Shape.cast(tmp)
|
||||||
|
|
||||||
def translate(self,vector):
|
def translate(self, vector):
|
||||||
|
|
||||||
if type(vector) == tuple:
|
if type(vector) == tuple:
|
||||||
vector = Vector(vector)
|
vector = Vector(vector)
|
||||||
|
@ -248,7 +252,7 @@ class Shape(object):
|
||||||
tmp.translate(vector.wrapped)
|
tmp.translate(vector.wrapped)
|
||||||
return Shape.cast(tmp)
|
return Shape.cast(tmp)
|
||||||
|
|
||||||
def scale(self,factor):
|
def scale(self, factor):
|
||||||
tmp = self.wrapped.copy()
|
tmp = self.wrapped.copy()
|
||||||
tmp.scale(factor)
|
tmp.scale(factor)
|
||||||
return Shape.cast(tmp)
|
return Shape.cast(tmp)
|
||||||
|
@ -256,7 +260,7 @@ class Shape(object):
|
||||||
def copy(self):
|
def copy(self):
|
||||||
return Shape.cast(self.wrapped.copy())
|
return Shape.cast(self.wrapped.copy())
|
||||||
|
|
||||||
def transformShape(self,tMatrix):
|
def transformShape(self, tMatrix):
|
||||||
"""
|
"""
|
||||||
tMatrix is a matrix object.
|
tMatrix is a matrix object.
|
||||||
returns a copy of the ojbect, transformed by the provided matrix,
|
returns a copy of the ojbect, transformed by the provided matrix,
|
||||||
|
@ -268,7 +272,7 @@ class Shape(object):
|
||||||
r.forConstruction = self.forConstruction
|
r.forConstruction = self.forConstruction
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def transformGeometry(self,tMatrix):
|
def transformGeometry(self, tMatrix):
|
||||||
"""
|
"""
|
||||||
tMatrix is a matrix object.
|
tMatrix is a matrix object.
|
||||||
|
|
||||||
|
@ -288,8 +292,9 @@ class Shape(object):
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return self.wrapped.hashCode()
|
return self.wrapped.hashCode()
|
||||||
|
|
||||||
|
|
||||||
class Vertex(Shape):
|
class Vertex(Shape):
|
||||||
def __init__(self,obj,forConstruction=False):
|
def __init__(self, obj, forConstruction=False):
|
||||||
"""
|
"""
|
||||||
Create a vertex from a FreeCAD Vertex
|
Create a vertex from a FreeCAD Vertex
|
||||||
"""
|
"""
|
||||||
|
@ -300,7 +305,7 @@ class Vertex(Shape):
|
||||||
self.Z = obj.Z
|
self.Z = obj.Z
|
||||||
|
|
||||||
def toTuple(self):
|
def toTuple(self):
|
||||||
return (self.X,self.Y,self.Z)
|
return (self.X, self.Y, self.Z)
|
||||||
|
|
||||||
def Center(self):
|
def Center(self):
|
||||||
"""
|
"""
|
||||||
|
@ -308,19 +313,20 @@ class Vertex(Shape):
|
||||||
"""
|
"""
|
||||||
return Vector(self.wrapped.Point)
|
return Vector(self.wrapped.Point)
|
||||||
|
|
||||||
|
|
||||||
class Edge(Shape):
|
class Edge(Shape):
|
||||||
def __init__(self,obj):
|
def __init__(self, obj):
|
||||||
"""
|
"""
|
||||||
An Edge
|
An Edge
|
||||||
"""
|
"""
|
||||||
self.wrapped = obj
|
self.wrapped = obj
|
||||||
#self.startPoint = None
|
# self.startPoint = None
|
||||||
#self.endPoint = None
|
# self.endPoint = None
|
||||||
|
|
||||||
self.edgetypes= {
|
self.edgetypes = {
|
||||||
FreeCADPart.Line : 'LINE',
|
FreeCADPart.Line: 'LINE',
|
||||||
FreeCADPart.ArcOfCircle : 'ARC',
|
FreeCADPart.ArcOfCircle: 'ARC',
|
||||||
FreeCADPart.Circle : 'CIRCLE'
|
FreeCADPart.Circle: 'CIRCLE'
|
||||||
}
|
}
|
||||||
|
|
||||||
def geomType(self):
|
def geomType(self):
|
||||||
|
@ -337,9 +343,9 @@ class Edge(Shape):
|
||||||
|
|
||||||
Note, circles may have the start and end points the same
|
Note, circles may have the start and end points the same
|
||||||
"""
|
"""
|
||||||
#work around freecad bug where valueAt is unreliable
|
# work around freecad bug where valueAt is unreliable
|
||||||
curve = self.wrapped.Curve
|
curve = self.wrapped.Curve
|
||||||
return Vector( curve.value(self.wrapped.ParameterRange[0]))
|
return Vector(curve.value(self.wrapped.ParameterRange[0]))
|
||||||
|
|
||||||
def endPoint(self):
|
def endPoint(self):
|
||||||
"""
|
"""
|
||||||
|
@ -349,14 +355,14 @@ class Edge(Shape):
|
||||||
Note, circles may have the start and end points the same
|
Note, circles may have the start and end points the same
|
||||||
|
|
||||||
"""
|
"""
|
||||||
#warning: easier syntax in freecad of <Edge>.valueAt(<Edge>.ParameterRange[1]) has
|
# warning: easier syntax in freecad of <Edge>.valueAt(<Edge>.ParameterRange[1]) has
|
||||||
#a bug with curves other than arcs, but using the underlying curve directly seems to work
|
# a bug with curves other than arcs, but using the underlying curve directly seems to work
|
||||||
#that's the solution i'm using below
|
# that's the solution i'm using below
|
||||||
curve = self.wrapped.Curve
|
curve = self.wrapped.Curve
|
||||||
v = Vector( curve.value(self.wrapped.ParameterRange[1]))
|
v = Vector(curve.value(self.wrapped.ParameterRange[1]))
|
||||||
return v
|
return v
|
||||||
|
|
||||||
def tangentAt(self,locationVector=None):
|
def tangentAt(self, locationVector=None):
|
||||||
"""
|
"""
|
||||||
Compute tangent vector at the specified location.
|
Compute tangent vector at the specified location.
|
||||||
:param locationVector: location to use. Use the center point if None
|
:param locationVector: location to use. Use the center point if None
|
||||||
|
@ -369,11 +375,11 @@ class Edge(Shape):
|
||||||
return Vector(self.wrapped.tangentAt(p))
|
return Vector(self.wrapped.tangentAt(p))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makeCircle(cls,radius,pnt=(0,0,0),dir=(0,0,1),angle1=360.0,angle2=360):
|
def makeCircle(cls, radius, pnt=(0, 0, 0), dir=(0, 0, 1), angle1=360.0, angle2=360):
|
||||||
return Edge(FreeCADPart.makeCircle(radius,toVector(pnt),toVector(dir),angle1,angle2))
|
return Edge(FreeCADPart.makeCircle(radius, toVector(pnt), toVector(dir), angle1, angle2))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makeSpline(cls,listOfVector):
|
def makeSpline(cls, listOfVector):
|
||||||
"""
|
"""
|
||||||
Interpolate a spline through the provided points.
|
Interpolate a spline through the provided points.
|
||||||
:param cls:
|
:param cls:
|
||||||
|
@ -383,11 +389,11 @@ class Edge(Shape):
|
||||||
vecs = [v.wrapped for v in listOfVector]
|
vecs = [v.wrapped for v in listOfVector]
|
||||||
|
|
||||||
spline = FreeCADPart.BSplineCurve()
|
spline = FreeCADPart.BSplineCurve()
|
||||||
spline.interpolate(vecs,False)
|
spline.interpolate(vecs, False)
|
||||||
return Edge(spline.toShape())
|
return Edge(spline.toShape())
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makeThreePointArc(cls,v1,v2,v3):
|
def makeThreePointArc(cls, v1, v2, v3):
|
||||||
"""
|
"""
|
||||||
Makes a three point arc through the provided points
|
Makes a three point arc through the provided points
|
||||||
:param cls:
|
:param cls:
|
||||||
|
@ -396,30 +402,30 @@ class Edge(Shape):
|
||||||
:param v3: end vector
|
:param v3: end vector
|
||||||
:return: an edge object through the three points
|
:return: an edge object through the three points
|
||||||
"""
|
"""
|
||||||
arc = FreeCADPart.Arc(v1.wrapped,v2.wrapped,v3.wrapped)
|
arc = FreeCADPart.Arc(v1.wrapped, v2.wrapped, v3.wrapped)
|
||||||
e = Edge(arc.toShape())
|
e = Edge(arc.toShape())
|
||||||
return e #arcane and undocumented, this creates an Edge object
|
return e # arcane and undocumented, this creates an Edge object
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makeLine(cls,v1,v2):
|
def makeLine(cls, v1, v2):
|
||||||
"""
|
"""
|
||||||
Create a line between two points
|
Create a line between two points
|
||||||
:param v1: Vector that represents the first point
|
:param v1: Vector that represents the first point
|
||||||
:param v2: Vector that represents the second point
|
:param v2: Vector that represents the second point
|
||||||
:return: A linear edge between the two provided points
|
:return: A linear edge between the two provided points
|
||||||
"""
|
"""
|
||||||
return Edge(FreeCADPart.makeLine(v1.toTuple(),v2.toTuple() ))
|
return Edge(FreeCADPart.makeLine(v1.toTuple(), v2.toTuple()))
|
||||||
|
|
||||||
|
|
||||||
class Wire(Shape):
|
class Wire(Shape):
|
||||||
def __init__(self,obj):
|
def __init__(self, obj):
|
||||||
"""
|
"""
|
||||||
A Wire
|
A Wire
|
||||||
"""
|
"""
|
||||||
self.wrapped = obj
|
self.wrapped = obj
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def combine(cls,listOfWires):
|
def combine(cls, listOfWires):
|
||||||
"""
|
"""
|
||||||
Attempt to combine a list of wires into a new wire.
|
Attempt to combine a list of wires into a new wire.
|
||||||
the wires are returned in a list.
|
the wires are returned in a list.
|
||||||
|
@ -430,7 +436,7 @@ class Wire(Shape):
|
||||||
return Shape.cast(FreeCADPart.Wire([w.wrapped for w in listOfWires]))
|
return Shape.cast(FreeCADPart.Wire([w.wrapped for w in listOfWires]))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def assembleEdges(cls,listOfEdges):
|
def assembleEdges(cls, listOfEdges):
|
||||||
"""
|
"""
|
||||||
Attempts to build a wire that consists of the edges in the provided list
|
Attempts to build a wire that consists of the edges in the provided list
|
||||||
:param cls:
|
:param cls:
|
||||||
|
@ -439,11 +445,11 @@ class Wire(Shape):
|
||||||
"""
|
"""
|
||||||
fCEdges = [a.wrapped for a in listOfEdges]
|
fCEdges = [a.wrapped for a in listOfEdges]
|
||||||
|
|
||||||
wa = Wire( FreeCADPart.Wire(fCEdges) )
|
wa = Wire(FreeCADPart.Wire(fCEdges))
|
||||||
return wa
|
return wa
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makeCircle(cls,radius,center,normal):
|
def makeCircle(cls, radius, center, normal):
|
||||||
"""
|
"""
|
||||||
Makes a Circle centered at the provided point, having normal in the provided direction
|
Makes a Circle centered at the provided point, having normal in the provided direction
|
||||||
:param radius: floating point radius of the circle, must be > 0
|
:param radius: floating point radius of the circle, must be > 0
|
||||||
|
@ -451,38 +457,38 @@ class Wire(Shape):
|
||||||
:param normal: vector representing the direction of the plane the circle should lie in
|
:param normal: vector representing the direction of the plane the circle should lie in
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
w = Wire(FreeCADPart.Wire([FreeCADPart.makeCircle(radius,center.wrapped,normal.wrapped)]))
|
w = Wire(FreeCADPart.Wire([FreeCADPart.makeCircle(radius, center.wrapped, normal.wrapped)]))
|
||||||
return w
|
return w
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makePolygon(cls,listOfVertices,forConstruction=False):
|
def makePolygon(cls, listOfVertices, forConstruction=False):
|
||||||
#convert list of tuples into Vectors.
|
# convert list of tuples into Vectors.
|
||||||
w = Wire(FreeCADPart.makePolygon([i.wrapped for i in listOfVertices]))
|
w = Wire(FreeCADPart.makePolygon([i.wrapped for i in listOfVertices]))
|
||||||
w.forConstruction = forConstruction
|
w.forConstruction = forConstruction
|
||||||
return w
|
return w
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makeHelix(cls,pitch,height,radius,angle=360.0):
|
def makeHelix(cls, pitch, height, radius, angle=360.0):
|
||||||
"""
|
"""
|
||||||
Make a helix with a given pitch, height and radius
|
Make a helix with a given pitch, height and radius
|
||||||
By default a cylindrical surface is used to create the helix. If
|
By default a cylindrical surface is used to create the helix. If
|
||||||
the fourth parameter is set (the apex given in degree) a conical surface is used instead'
|
the fourth parameter is set (the apex given in degree) a conical surface is used instead'
|
||||||
"""
|
"""
|
||||||
return Wire(FreeCADPart.makeHelix(pitch,height,radius,angle))
|
return Wire(FreeCADPart.makeHelix(pitch, height, radius, angle))
|
||||||
|
|
||||||
|
|
||||||
class Face(Shape):
|
class Face(Shape):
|
||||||
def __init__(self,obj):
|
def __init__(self, obj):
|
||||||
"""
|
"""
|
||||||
A Face
|
A Face
|
||||||
"""
|
"""
|
||||||
self.wrapped = obj
|
self.wrapped = obj
|
||||||
|
|
||||||
self.facetypes = {
|
self.facetypes = {
|
||||||
#TODO: bezier,bspline etc
|
# TODO: bezier,bspline etc
|
||||||
FreeCADPart.Plane : 'PLANE',
|
FreeCADPart.Plane: 'PLANE',
|
||||||
FreeCADPart.Sphere : 'SPHERE',
|
FreeCADPart.Sphere: 'SPHERE',
|
||||||
FreeCADPart.Cone : 'CONE'
|
FreeCADPart.Cone: 'CONE'
|
||||||
}
|
}
|
||||||
|
|
||||||
def geomType(self):
|
def geomType(self):
|
||||||
|
@ -492,7 +498,7 @@ class Face(Shape):
|
||||||
else:
|
else:
|
||||||
return "Unknown Face Surface Type: %s" % str(t)
|
return "Unknown Face Surface Type: %s" % str(t)
|
||||||
|
|
||||||
def normalAt(self,locationVector=None):
|
def normalAt(self, locationVector=None):
|
||||||
"""
|
"""
|
||||||
Computes the normal vector at the desired location on the face.
|
Computes the normal vector at the desired location on the face.
|
||||||
|
|
||||||
|
@ -502,31 +508,31 @@ class Face(Shape):
|
||||||
"""
|
"""
|
||||||
if locationVector == None:
|
if locationVector == None:
|
||||||
locationVector = self.Center()
|
locationVector = self.Center()
|
||||||
(u,v) = self.wrapped.Surface.parameter(locationVector.wrapped)
|
(u, v) = self.wrapped.Surface.parameter(locationVector.wrapped)
|
||||||
|
|
||||||
return Vector(self.wrapped.normalAt(u,v).normalize() )
|
return Vector(self.wrapped.normalAt(u, v).normalize())
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makePlane(cls,length,width,basePnt=None,dir=None):
|
def makePlane(cls, length, width, basePnt=None, dir=None):
|
||||||
return Face(FreeCADPart.makePlan(length,width,toVector(basePnt),toVector(dir)))
|
return Face(FreeCADPart.makePlan(length, width, toVector(basePnt), toVector(dir)))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makeRuledSurface(cls,edgeOrWire1,edgeOrWire2,dist=None):
|
def makeRuledSurface(cls, edgeOrWire1, edgeOrWire2, dist=None):
|
||||||
"""
|
"""
|
||||||
'makeRuledSurface(Edge|Wire,Edge|Wire) -- Make a ruled surface
|
'makeRuledSurface(Edge|Wire,Edge|Wire) -- Make a ruled surface
|
||||||
Create a ruled surface out of two edges or wires. If wires are used then
|
Create a ruled surface out of two edges or wires. If wires are used then
|
||||||
these must have the same
|
these must have the same
|
||||||
"""
|
"""
|
||||||
return Shape.cast(FreeCADPart.makeRuledSurface(edgeOrWire1.obj,edgeOrWire2.obj,dist))
|
return Shape.cast(FreeCADPart.makeRuledSurface(edgeOrWire1.obj, edgeOrWire2.obj, dist))
|
||||||
|
|
||||||
def cut(self,faceToCut):
|
def cut(self, faceToCut):
|
||||||
"Remove a face from another one"
|
"Remove a face from another one"
|
||||||
return Shape.cast(self.obj.cut(faceToCut.obj))
|
return Shape.cast(self.obj.cut(faceToCut.obj))
|
||||||
|
|
||||||
def fuse(self,faceToJoin):
|
def fuse(self, faceToJoin):
|
||||||
return Shape.cast(self.obj.fuse(faceToJoin.obj))
|
return Shape.cast(self.obj.fuse(faceToJoin.obj))
|
||||||
|
|
||||||
def intersect(self,faceToIntersect):
|
def intersect(self, faceToIntersect):
|
||||||
"""
|
"""
|
||||||
computes the intersection between the face and the supplied one.
|
computes the intersection between the face and the supplied one.
|
||||||
The result could be a face or a compound of faces
|
The result could be a face or a compound of faces
|
||||||
|
@ -535,73 +541,73 @@ class Face(Shape):
|
||||||
|
|
||||||
|
|
||||||
class Shell(Shape):
|
class Shell(Shape):
|
||||||
def __init__(self,wrapped):
|
def __init__(self, wrapped):
|
||||||
"""
|
"""
|
||||||
A Shell
|
A Shell
|
||||||
"""
|
"""
|
||||||
self.wrapped = wrapped
|
self.wrapped = wrapped
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makeShell(cls,listOfFaces):
|
def makeShell(cls, listOfFaces):
|
||||||
return Shell(FreeCADPart.makeShell([i.obj for i in listOfFaces]))
|
return Shell(FreeCADPart.makeShell([i.obj for i in listOfFaces]))
|
||||||
|
|
||||||
|
|
||||||
class Solid(Shape):
|
class Solid(Shape):
|
||||||
def __init__(self,obj):
|
def __init__(self, obj):
|
||||||
"""
|
"""
|
||||||
A Solid
|
A Solid
|
||||||
"""
|
"""
|
||||||
self.wrapped = obj
|
self.wrapped = obj
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def isSolid(cls,obj):
|
def isSolid(cls, obj):
|
||||||
"""
|
"""
|
||||||
Returns true if the object is a FreeCAD solid, false otherwise
|
Returns true if the object is a FreeCAD solid, false otherwise
|
||||||
"""
|
"""
|
||||||
if hasattr(obj, 'ShapeType'):
|
if hasattr(obj, 'ShapeType'):
|
||||||
if obj.ShapeType == 'Solid' or\
|
if obj.ShapeType == 'Solid' or \
|
||||||
(obj.ShapeType == 'Compound' and len(obj.Solids) > 0):
|
(obj.ShapeType == 'Compound' and len(obj.Solids) > 0):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makeBox(cls,length,width,height,pnt=Vector(0,0,0),dir=Vector(0,0,1)):
|
def makeBox(cls, length, width, height, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1)):
|
||||||
"""
|
"""
|
||||||
makeBox(length,width,height,[pnt,dir]) -- Make a box located\nin pnt with the d
|
makeBox(length,width,height,[pnt,dir]) -- Make a box located\nin pnt with the d
|
||||||
imensions (length,width,height)\nBy default pnt=Vector(0,0,0) and dir=Vector(0,0,1)'
|
imensions (length,width,height)\nBy default pnt=Vector(0,0,0) and dir=Vector(0,0,1)'
|
||||||
"""
|
"""
|
||||||
return Shape.cast(FreeCADPart.makeBox(length,width,height,pnt.wrapped,dir.wrapped))
|
return Shape.cast(FreeCADPart.makeBox(length, width, height, pnt.wrapped, dir.wrapped))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makeCone(cls,radius1,radius2,height,pnt=Vector(0,0,0),dir=Vector(0,0,1),angleDegrees=360):
|
def makeCone(cls, radius1, radius2, height, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1), angleDegrees=360):
|
||||||
"""
|
"""
|
||||||
'makeCone(radius1,radius2,height,[pnt,dir,angle]) --
|
'makeCone(radius1,radius2,height,[pnt,dir,angle]) --
|
||||||
Make a cone with given radii and height\nBy default pnt=Vector(0,0,0),
|
Make a cone with given radii and height\nBy default pnt=Vector(0,0,0),
|
||||||
dir=Vector(0,0,1) and angle=360'
|
dir=Vector(0,0,1) and angle=360'
|
||||||
"""
|
"""
|
||||||
return Shape.cast(FreeCADPart.makeCone(radius1,radius2,height,pnt.wrapped,dir.wrapped,angleDegrees))
|
return Shape.cast(FreeCADPart.makeCone(radius1, radius2, height, pnt.wrapped, dir.wrapped, angleDegrees))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makeCylinder(cls,radius,height,pnt=Vector(0,0,0),dir=Vector(0,0,1),angleDegrees=360):
|
def makeCylinder(cls, radius, height, pnt=Vector(0, 0, 0), dir=Vector(0, 0, 1), angleDegrees=360):
|
||||||
"""
|
"""
|
||||||
makeCylinder(radius,height,[pnt,dir,angle]) --
|
makeCylinder(radius,height,[pnt,dir,angle]) --
|
||||||
Make a cylinder with a given radius and height
|
Make a cylinder with a given radius and height
|
||||||
By default pnt=Vector(0,0,0),dir=Vector(0,0,1) and angle=360'
|
By default pnt=Vector(0,0,0),dir=Vector(0,0,1) and angle=360'
|
||||||
"""
|
"""
|
||||||
return Shape.cast(FreeCADPart.makeCylinder(radius,height,pnt.wrapped,dir.wrapped,angleDegrees))
|
return Shape.cast(FreeCADPart.makeCylinder(radius, height, pnt.wrapped, dir.wrapped, angleDegrees))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makeTorus(cls,radius1,radius2,pnt=None,dir=None,angleDegrees1=None,angleDegrees2=None):
|
def makeTorus(cls, radius1, radius2, pnt=None, dir=None, angleDegrees1=None, angleDegrees2=None):
|
||||||
"""
|
"""
|
||||||
makeTorus(radius1,radius2,[pnt,dir,angle1,angle2,angle]) --
|
makeTorus(radius1,radius2,[pnt,dir,angle1,angle2,angle]) --
|
||||||
Make a torus with agiven radii and angles
|
Make a torus with agiven radii and angles
|
||||||
By default pnt=Vector(0,0,0),dir=Vector(0,0,1),angle1=0
|
By default pnt=Vector(0,0,0),dir=Vector(0,0,1),angle1=0
|
||||||
,angle1=360 and angle=360'
|
,angle1=360 and angle=360'
|
||||||
"""
|
"""
|
||||||
return Shape.cast(FreeCADPart.makeTorus(radius1,radius2,pnt,dir,angleDegrees1,angleDegrees2))
|
return Shape.cast(FreeCADPart.makeTorus(radius1, radius2, pnt, dir, angleDegrees1, angleDegrees2))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def sweep(cls,profileWire,pathWire):
|
def sweep(cls, profileWire, pathWire):
|
||||||
"""
|
"""
|
||||||
make a solid by sweeping the profileWire along the specified path
|
make a solid by sweeping the profileWire along the specified path
|
||||||
:param cls:
|
:param cls:
|
||||||
|
@ -609,41 +615,42 @@ class Solid(Shape):
|
||||||
:param pathWire:
|
:param pathWire:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
#needs to use freecad wire.makePipe or makePipeShell
|
# needs to use freecad wire.makePipe or makePipeShell
|
||||||
#needs to allow free-space wires ( those not made from a workplane )
|
# needs to allow free-space wires ( those not made from a workplane )
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makeLoft(cls,listOfWire):
|
def makeLoft(cls, listOfWire):
|
||||||
"""
|
"""
|
||||||
makes a loft from a list of wires
|
makes a loft from a list of wires
|
||||||
The wires will be converted into faces when possible-- it is presumed that nobody ever actually
|
The wires will be converted into faces when possible-- it is presumed that nobody ever actually
|
||||||
wants to make an infinitely thin shell for a real FreeCADPart.
|
wants to make an infinitely thin shell for a real FreeCADPart.
|
||||||
"""
|
"""
|
||||||
#the True flag requests building a solid instead of a shell.
|
# the True flag requests building a solid instead of a shell.
|
||||||
|
|
||||||
return Shape.cast(FreeCADPart.makeLoft([i.wrapped for i in listOfWire],True))
|
return Shape.cast(FreeCADPart.makeLoft([i.wrapped for i in listOfWire], True))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makeWedge(cls,xmin,ymin,zmin,z2min,x2min,xmax,ymax,zmax,z2max,x2max,pnt=None,dir=None):
|
def makeWedge(cls, xmin, ymin, zmin, z2min, x2min, xmax, ymax, zmax, z2max, x2max, pnt=None, dir=None):
|
||||||
"""
|
"""
|
||||||
'makeWedge(xmin, ymin, zmin, z2min, x2min,
|
'makeWedge(xmin, ymin, zmin, z2min, x2min,
|
||||||
xmax, ymax, zmax, z2max, x2max,[pnt, dir])
|
xmax, ymax, zmax, z2max, x2max,[pnt, dir])
|
||||||
Make a wedge located in pnt\nBy default pnt=Vector(0,0,0) and dir=Vec
|
Make a wedge located in pnt\nBy default pnt=Vector(0,0,0) and dir=Vec
|
||||||
tor(0,0,1)'
|
tor(0,0,1)'
|
||||||
"""
|
"""
|
||||||
return Shape.cast(FreeCADPart.makeWedge(xmin,ymin,zmin,z2min,x2min,xmax,ymax,zmax,z2max,x2max,pnt,dir))
|
return Shape.cast(
|
||||||
|
FreeCADPart.makeWedge(xmin, ymin, zmin, z2min, x2min, xmax, ymax, zmax, z2max, x2max, pnt, dir))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makeSphere(cls,radius,pnt=None,angleDegrees1=None,angleDegrees2=None,angleDegrees3=None):
|
def makeSphere(cls, radius, pnt=None, angleDegrees1=None, angleDegrees2=None, angleDegrees3=None):
|
||||||
"""
|
"""
|
||||||
'makeSphere(radius,[pnt, dir, angle1,angle2,angle3]) --
|
'makeSphere(radius,[pnt, dir, angle1,angle2,angle3]) --
|
||||||
Make a sphere with a giv
|
Make a sphere with a giv
|
||||||
en radius\nBy default pnt=Vector(0,0,0), dir=Vector(0,0,1), angle1=0, angle2=90 and angle3=360'
|
en radius\nBy default pnt=Vector(0,0,0), dir=Vector(0,0,1), angle1=0, angle2=90 and angle3=360'
|
||||||
"""
|
"""
|
||||||
return Solid(FreeCADPart.makeSphere(radius,pnt,angleDegrees1,angleDegrees2,angleDegrees3))
|
return Solid(FreeCADPart.makeSphere(radius, pnt, angleDegrees1, angleDegrees2, angleDegrees3))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def extrudeLinearWithRotation(cls,outerWire,innerWires,vecCenter, vecNormal,angleDegrees):
|
def extrudeLinearWithRotation(cls, outerWire, innerWires, vecCenter, vecNormal, angleDegrees):
|
||||||
"""
|
"""
|
||||||
Creates a 'twisted prism' by extruding, while simultaneously rotating around the extrusion vector.
|
Creates a 'twisted prism' by extruding, while simultaneously rotating around the extrusion vector.
|
||||||
|
|
||||||
|
@ -665,23 +672,23 @@ class Solid(Shape):
|
||||||
:return: a cad.Solid object
|
:return: a cad.Solid object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#from this point down we are dealing with FreeCAD wires not cad.wires
|
# from this point down we are dealing with FreeCAD wires not cad.wires
|
||||||
startWires = [outerWire.wrapped] + [ i.wrapped for i in innerWires]
|
startWires = [outerWire.wrapped] + [i.wrapped for i in innerWires]
|
||||||
endWires = []
|
endWires = []
|
||||||
p1 = vecCenter.wrapped
|
p1 = vecCenter.wrapped
|
||||||
p2 = vecCenter.add(vecNormal).wrapped
|
p2 = vecCenter.add(vecNormal).wrapped
|
||||||
|
|
||||||
#make translated and rotated copy of each wire
|
# make translated and rotated copy of each wire
|
||||||
for w in startWires:
|
for w in startWires:
|
||||||
w2 = w.copy()
|
w2 = w.copy()
|
||||||
w2.translate(vecNormal.wrapped)
|
w2.translate(vecNormal.wrapped)
|
||||||
w2.rotate(p1,p2,angleDegrees)
|
w2.rotate(p1, p2, angleDegrees)
|
||||||
endWires.append(w2)
|
endWires.append(w2)
|
||||||
|
|
||||||
#make a ruled surface for each set of wires
|
# make a ruled surface for each set of wires
|
||||||
sides = []
|
sides = []
|
||||||
for w1,w2 in zip(startWires,endWires):
|
for w1, w2 in zip(startWires, endWires):
|
||||||
rs = FreeCADPart.makeRuledSurface(w1,w2)
|
rs = FreeCADPart.makeRuledSurface(w1, w2)
|
||||||
sides.append(rs)
|
sides.append(rs)
|
||||||
|
|
||||||
#make faces for the top and bottom
|
#make faces for the top and bottom
|
||||||
|
@ -689,7 +696,7 @@ class Solid(Shape):
|
||||||
endFace = FreeCADPart.Face(endWires)
|
endFace = FreeCADPart.Face(endWires)
|
||||||
|
|
||||||
#collect all the faces from the sides
|
#collect all the faces from the sides
|
||||||
faceList = [ startFace]
|
faceList = [startFace]
|
||||||
for s in sides:
|
for s in sides:
|
||||||
faceList.extend(s.Faces)
|
faceList.extend(s.Faces)
|
||||||
faceList.append(endFace)
|
faceList.append(endFace)
|
||||||
|
@ -699,7 +706,7 @@ class Solid(Shape):
|
||||||
return Shape.cast(solid)
|
return Shape.cast(solid)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def extrudeLinear(cls,outerWire,innerWires,vecNormal):
|
def extrudeLinear(cls, outerWire, innerWires, vecNormal):
|
||||||
"""
|
"""
|
||||||
Attempt to extrude the list of wires into a prismatic solid in the provided direction
|
Attempt to extrude the list of wires into a prismatic solid in the provided direction
|
||||||
|
|
||||||
|
@ -722,9 +729,9 @@ class Solid(Shape):
|
||||||
reliable.
|
reliable.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#one would think that fusing faces into a compound and then extruding would work,
|
# one would think that fusing faces into a compound and then extruding would work,
|
||||||
#but it doesnt-- the resulting compound appears to look right, ( right number of faces, etc),
|
# but it doesnt-- the resulting compound appears to look right, ( right number of faces, etc),
|
||||||
#but then cutting it from the main solid fails with BRep_NotDone.
|
# but then cutting it from the main solid fails with BRep_NotDone.
|
||||||
#the work around is to extrude each and then join the resulting solids, which seems to work
|
#the work around is to extrude each and then join the resulting solids, which seems to work
|
||||||
|
|
||||||
#FreeCAD allows this in one operation, but others might not
|
#FreeCAD allows this in one operation, but others might not
|
||||||
|
@ -737,24 +744,68 @@ class Solid(Shape):
|
||||||
|
|
||||||
return Shape.cast(result)
|
return Shape.cast(result)
|
||||||
|
|
||||||
def tessellate(self,tolerance):
|
@classmethod
|
||||||
|
def revolve(cls, outerWire, innerWires, angleDegrees, axisStart, axisEnd):
|
||||||
|
"""
|
||||||
|
Attempt to revolve the list of wires into a solid in the provided direction
|
||||||
|
|
||||||
|
:param outerWire: the outermost wire
|
||||||
|
:param innerWires: a list of inner wires
|
||||||
|
:param angleDegrees: the angle to revolve through.
|
||||||
|
:type angleDegrees: float, anything less than 360 degrees will leave the shape open
|
||||||
|
:param axisStart: the start point of the axis of rotation
|
||||||
|
:type axisStart: tuple, a two tuple
|
||||||
|
:param axisEnd: the end point of the axis of rotation
|
||||||
|
:type axisEnd: tuple, a two tuple
|
||||||
|
:return: a Solid object
|
||||||
|
|
||||||
|
The wires must not intersect
|
||||||
|
|
||||||
|
* all wires must be closed
|
||||||
|
* there cannot be any intersecting or self-intersecting wires
|
||||||
|
* wires must be listed from outside in
|
||||||
|
* more than one levels of nesting is not supported reliably
|
||||||
|
* the wire(s) that you're revolving cannot be centered
|
||||||
|
|
||||||
|
This method will attempt to sort the wires, but there is much work remaining to make this method
|
||||||
|
reliable.
|
||||||
|
"""
|
||||||
|
freeCADWires = [outerWire.wrapped]
|
||||||
|
|
||||||
|
for w in innerWires:
|
||||||
|
freeCADWires.append(w.wrapped)
|
||||||
|
|
||||||
|
f = FreeCADPart.Face(freeCADWires)
|
||||||
|
|
||||||
|
rotateCenter = FreeCAD.Base.Vector(axisStart)
|
||||||
|
rotateAxis = FreeCAD.Base.Vector(axisEnd)
|
||||||
|
|
||||||
|
#Convert our axis end vector into to something FreeCAD will understand (an axis specification vector)
|
||||||
|
rotateAxis = rotateCenter.sub(rotateAxis)
|
||||||
|
|
||||||
|
#FreeCAD wants a rotation center and then an axis to rotate around rather than an axis of rotation
|
||||||
|
result = f.revolve(rotateCenter, rotateAxis, angleDegrees)
|
||||||
|
|
||||||
|
return Shape.cast(result)
|
||||||
|
|
||||||
|
def tessellate(self, tolerance):
|
||||||
return self.wrapped.tessellate(tolerance)
|
return self.wrapped.tessellate(tolerance)
|
||||||
|
|
||||||
def intersect(self,toIntersect):
|
def intersect(self, toIntersect):
|
||||||
"""
|
"""
|
||||||
computes the intersection between this solid and the supplied one
|
computes the intersection between this solid and the supplied one
|
||||||
The result could be a face or a compound of faces
|
The result could be a face or a compound of faces
|
||||||
"""
|
"""
|
||||||
return Shape.cast(self.wrapped.common(toIntersect.wrapped))
|
return Shape.cast(self.wrapped.common(toIntersect.wrapped))
|
||||||
|
|
||||||
def cut(self,solidToCut):
|
def cut(self, solidToCut):
|
||||||
"Remove a solid from another one"
|
"Remove a solid from another one"
|
||||||
return Shape.cast(self.wrapped.cut(solidToCut.wrapped))
|
return Shape.cast(self.wrapped.cut(solidToCut.wrapped))
|
||||||
|
|
||||||
def fuse(self,solidToJoin):
|
def fuse(self, solidToJoin):
|
||||||
return Shape.cast(self.wrapped.fuse(solidToJoin.wrapped))
|
return Shape.cast(self.wrapped.fuse(solidToJoin.wrapped))
|
||||||
|
|
||||||
def fillet(self,radius,edgeList):
|
def fillet(self, radius, edgeList):
|
||||||
"""
|
"""
|
||||||
Fillets the specified edges of this solid.
|
Fillets the specified edges of this solid.
|
||||||
:param radius: float > 0, the radius of the fillet
|
:param radius: float > 0, the radius of the fillet
|
||||||
|
@ -762,9 +813,9 @@ class Solid(Shape):
|
||||||
:return: Filleted solid
|
:return: Filleted solid
|
||||||
"""
|
"""
|
||||||
nativeEdges = [e.wrapped for e in edgeList]
|
nativeEdges = [e.wrapped for e in edgeList]
|
||||||
return Shape.cast(self.wrapped.makeFillet(radius,nativeEdges))
|
return Shape.cast(self.wrapped.makeFillet(radius, nativeEdges))
|
||||||
|
|
||||||
def shell(self,faceList,thickness,tolerance=0.0001):
|
def shell(self, faceList, thickness, tolerance=0.0001):
|
||||||
"""
|
"""
|
||||||
make a shelled solid of given by removing the list of faces
|
make a shelled solid of given by removing the list of faces
|
||||||
|
|
||||||
|
@ -776,31 +827,32 @@ class Solid(Shape):
|
||||||
**WARNING** The underlying FreeCAD implementation can very frequently have problems
|
**WARNING** The underlying FreeCAD implementation can very frequently have problems
|
||||||
with shelling complex geometries!
|
with shelling complex geometries!
|
||||||
"""
|
"""
|
||||||
nativeFaces = [ f.wrapped for f in faceList]
|
nativeFaces = [f.wrapped for f in faceList]
|
||||||
return Shape.cast( self.wrapped.makeThickness(nativeFaces,thickness,tolerance))
|
return Shape.cast(self.wrapped.makeThickness(nativeFaces, thickness, tolerance))
|
||||||
|
|
||||||
|
|
||||||
class Compound(Shape):
|
class Compound(Shape):
|
||||||
def __init__(self,obj):
|
def __init__(self, obj):
|
||||||
"""
|
"""
|
||||||
An Edge
|
An Edge
|
||||||
"""
|
"""
|
||||||
self.wrapped = obj
|
self.wrapped = obj
|
||||||
|
|
||||||
def Center(self):
|
def Center(self):
|
||||||
#TODO: compute the weighted average instead of the first solid
|
# TODO: compute the weighted average instead of the first solid
|
||||||
return self.Solids()[0].Center()
|
return self.Solids()[0].Center()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makeCompound(cls,listOfShapes):
|
def makeCompound(cls, listOfShapes):
|
||||||
"""
|
"""
|
||||||
Create a compound out of a list of shapes
|
Create a compound out of a list of shapes
|
||||||
"""
|
"""
|
||||||
solids = [s.wrapped for s in listOfShapes]
|
solids = [s.wrapped for s in listOfShapes]
|
||||||
c = FreeCADPart.Compound(solids)
|
c = FreeCADPart.Compound(solids)
|
||||||
return Shape.cast( c)
|
return Shape.cast(c)
|
||||||
|
|
||||||
def fuse(self,toJoin):
|
def fuse(self, toJoin):
|
||||||
return Shape.cast(self.wrapped.fuse(toJoin.wrapped))
|
return Shape.cast(self.wrapped.fuse(toJoin.wrapped))
|
||||||
|
|
||||||
def tessellate(self,tolerance):
|
def tessellate(self, tolerance):
|
||||||
return self.wrapped.tessellate(tolerance)
|
return self.wrapped.tessellate(tolerance)
|
||||||
|
|
44
examples/FreeCAD/Ex025_Revolution.py
Normal file
44
examples/FreeCAD/Ex025_Revolution.py
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#File: Ex025_Revolution.py
|
||||||
|
#To use this example file, you need to first follow the "Using CadQuery From Inside FreeCAD"
|
||||||
|
#instructions here: https://github.com/dcowden/cadquery#installing----using-cadquery-from-inside-freecad
|
||||||
|
|
||||||
|
#You run this example by typing the following in the FreeCAD python console, making sure to change
|
||||||
|
#the path to this example, and the name of the example appropriately.
|
||||||
|
#import sys
|
||||||
|
#sys.path.append('/home/user/Downloads/cadquery/examples/FreeCAD')
|
||||||
|
#import Ex025_Revolution
|
||||||
|
|
||||||
|
#If you need to reload the part after making a change, you can use the following lines within the FreeCAD console.
|
||||||
|
#reload(Ex025_Revolution)
|
||||||
|
|
||||||
|
#You'll need to delete the original shape that was created, and the new shape should be named sequentially (Shape001, etc).
|
||||||
|
|
||||||
|
#You can also tie these blocks of code to macros, buttons, and keybindings in FreeCAD for quicker access.
|
||||||
|
#You can get a more information on this example at http://parametricparts.com/docs/examples.html#an-extruded-prismatic-solid
|
||||||
|
|
||||||
|
import cadquery
|
||||||
|
import Part
|
||||||
|
|
||||||
|
#The dimensions of the model. These can be modified rather than changing the shape's code directly.
|
||||||
|
rectangle_width = 10.0
|
||||||
|
rectangle_length = 10.0
|
||||||
|
angle_degrees = 360.0
|
||||||
|
|
||||||
|
#Revolve a cylinder from a rectangle
|
||||||
|
#Switch comments around in this section to try the revolve operation with different parameters
|
||||||
|
result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length, False).revolve()
|
||||||
|
#result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length, False).revolve(angle_degrees)
|
||||||
|
#result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5,-5))
|
||||||
|
#result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5, -5),(-5, 5))
|
||||||
|
#result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5,-5),(-5,5), False)
|
||||||
|
|
||||||
|
#Revolve a donut with square walls
|
||||||
|
#result = cadquery.Workplane("XY").rect(rectangle_width, rectangle_length, True).revolve(angle_degrees, (20, 0), (20, 10))
|
||||||
|
|
||||||
|
#Get a cadquery solid object
|
||||||
|
solid = result.val()
|
||||||
|
|
||||||
|
#Use the wrapped property of a cadquery primitive to get a FreeCAD solid
|
||||||
|
Part.show(solid.wrapped)
|
||||||
|
|
||||||
|
#Would like to zoom to fit the part here, but FreeCAD doesn't seem to have that scripting functionality
|
2
setup.py
2
setup.py
|
@ -2,7 +2,7 @@ from setuptools import setup
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='cadquery',
|
name='cadquery',
|
||||||
version='0.1.6',
|
version='0.1.7',
|
||||||
url='https://github.com/dcowden/cadquery',
|
url='https://github.com/dcowden/cadquery',
|
||||||
license='LGPL',
|
license='LGPL',
|
||||||
author='David Cowden',
|
author='David Cowden',
|
||||||
|
|
|
@ -219,6 +219,87 @@ class TestCadQuery(BaseTest):
|
||||||
#self.assertEqual(1,s.solids().size() )
|
#self.assertEqual(1,s.solids().size() )
|
||||||
#self.assertEqual(8,s.faces().size() )
|
#self.assertEqual(8,s.faces().size() )
|
||||||
|
|
||||||
|
def testRevolveCylinder(self):
|
||||||
|
"""
|
||||||
|
Test creating a solid using the revolve operation.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
#The dimensions of the model. These can be modified rather than changing the shape's code directly.
|
||||||
|
rectangle_width = 10.0
|
||||||
|
rectangle_length = 10.0
|
||||||
|
angle_degrees = 360.0
|
||||||
|
|
||||||
|
#Test revolve without any options for making a cylinder
|
||||||
|
result = Workplane("XY").rect(rectangle_width, rectangle_length, False).revolve()
|
||||||
|
self.assertEqual(3, result.faces().size())
|
||||||
|
self.assertEqual(2, result.vertices().size())
|
||||||
|
self.assertEqual(3, result.edges().size())
|
||||||
|
|
||||||
|
#Test revolve when only setting the angle to revolve through
|
||||||
|
result = Workplane("XY").rect(rectangle_width, rectangle_length, False).revolve(angle_degrees)
|
||||||
|
self.assertEqual(3, result.faces().size())
|
||||||
|
self.assertEqual(2, result.vertices().size())
|
||||||
|
self.assertEqual(3, result.edges().size())
|
||||||
|
result = Workplane("XY").rect(rectangle_width, rectangle_length, False).revolve(270.0)
|
||||||
|
self.assertEqual(5, result.faces().size())
|
||||||
|
self.assertEqual(6, result.vertices().size())
|
||||||
|
self.assertEqual(9, result.edges().size())
|
||||||
|
|
||||||
|
#Test when passing revolve the angle and the axis of revolution's start point
|
||||||
|
result = Workplane("XY").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5,-5))
|
||||||
|
self.assertEqual(3, result.faces().size())
|
||||||
|
self.assertEqual(2, result.vertices().size())
|
||||||
|
self.assertEqual(3, result.edges().size())
|
||||||
|
result = Workplane("XY").rect(rectangle_width, rectangle_length).revolve(270.0,(-5,-5))
|
||||||
|
self.assertEqual(5, result.faces().size())
|
||||||
|
self.assertEqual(6, result.vertices().size())
|
||||||
|
self.assertEqual(9, result.edges().size())
|
||||||
|
|
||||||
|
#Test when passing revolve the angle and both the start and ends of the axis of revolution
|
||||||
|
result = Workplane("XY").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5, -5),(-5, 5))
|
||||||
|
self.assertEqual(3, result.faces().size())
|
||||||
|
self.assertEqual(2, result.vertices().size())
|
||||||
|
self.assertEqual(3, result.edges().size())
|
||||||
|
result = Workplane("XY").rect(rectangle_width, rectangle_length).revolve(270.0,(-5, -5),(-5, 5))
|
||||||
|
self.assertEqual(5, result.faces().size())
|
||||||
|
self.assertEqual(6, result.vertices().size())
|
||||||
|
self.assertEqual(9, result.edges().size())
|
||||||
|
|
||||||
|
#Testing all of the above without combine
|
||||||
|
result = Workplane("XY").rect(rectangle_width, rectangle_length).revolve(angle_degrees,(-5,-5),(-5,5), False)
|
||||||
|
self.assertEqual(3, result.faces().size())
|
||||||
|
self.assertEqual(2, result.vertices().size())
|
||||||
|
self.assertEqual(3, result.edges().size())
|
||||||
|
result = Workplane("XY").rect(rectangle_width, rectangle_length).revolve(270.0,(-5,-5),(-5,5), False)
|
||||||
|
self.assertEqual(5, result.faces().size())
|
||||||
|
self.assertEqual(6, result.vertices().size())
|
||||||
|
self.assertEqual(9, result.edges().size())
|
||||||
|
|
||||||
|
def testRevolveDonut(self):
|
||||||
|
"""
|
||||||
|
Test creating a solid donut shape with square walls
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
#The dimensions of the model. These can be modified rather than changing the shape's code directly.
|
||||||
|
rectangle_width = 10.0
|
||||||
|
rectangle_length = 10.0
|
||||||
|
angle_degrees = 360.0
|
||||||
|
|
||||||
|
result = Workplane("XY").rect(rectangle_width, rectangle_length, True).revolve(angle_degrees, (20, 0), (20, 10))
|
||||||
|
self.assertEqual(4, result.faces().size())
|
||||||
|
self.assertEqual(4, result.vertices().size())
|
||||||
|
self.assertEqual(6, result.edges().size())
|
||||||
|
|
||||||
|
def testRevolveCone(self):
|
||||||
|
"""
|
||||||
|
Test creating a solid from a revolved triangle
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
result = Workplane("XY").lineTo(0,10).lineTo(5,0).close().revolve()
|
||||||
|
self.assertEqual(2, result.faces().size())
|
||||||
|
self.assertEqual(2, result.vertices().size())
|
||||||
|
self.assertEqual(3, result.edges().size())
|
||||||
|
|
||||||
def testRectArray(self):
|
def testRectArray(self):
|
||||||
NUMX=3
|
NUMX=3
|
||||||
NUMY=3
|
NUMY=3
|
||||||
|
|
Loading…
Reference in New Issue
Block a user