the test cases pass now at least
This commit is contained in:
parent
3f7d38cdeb
commit
26cd0a443d
1417
cadquery/CQ.py
1417
cadquery/CQ.py
File diff suppressed because it is too large
Load Diff
8
cadquery/README.txt
Normal file
8
cadquery/README.txt
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
***
|
||||||
|
Core CadQuery implementation.
|
||||||
|
|
||||||
|
No files should depend on or import FreeCAD , pythonOCC, or other CAD Kernel libraries!!!
|
||||||
|
Dependencies should be on the classes provided by implementation packages, which in turn
|
||||||
|
can depend on CAD libraries.
|
||||||
|
|
||||||
|
***
|
|
@ -19,20 +19,20 @@
|
||||||
|
|
||||||
|
|
||||||
#these items point to the freecad implementation
|
#these items point to the freecad implementation
|
||||||
from .freecad_impl.geom import Plane,BoundBox,Vector
|
from .freecad_impl.geom import Plane,BoundBox,Vector,Matrix,sortWiresByBuildOrder
|
||||||
from .freecad_impl.shapes import Shape,Vertex,Edge,Wire,Solid,Shell,Compound
|
from .freecad_impl.shapes import Shape,Vertex,Edge,Face,Wire,Solid,Shell,Compound
|
||||||
from .freecad_impl.exporters import SvgExporter, AmfExporter, JsonExporter
|
from .freecad_impl import exporters
|
||||||
|
|
||||||
#these items are the common implementation
|
#these items are the common implementation
|
||||||
from .CQ import CQ
|
|
||||||
from .workplane import Workplane
|
#the order of these matter
|
||||||
from . import plugins
|
|
||||||
from . import selectors
|
from . import selectors
|
||||||
|
from .CQ import CQ,CQContext,Workplane
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'CQ','Workplane','plugins','selectors','Plane','BoundBox',
|
'CQ','Workplane','plugins','selectors','Plane','BoundBox','Matrix','Vector','sortWiresByBuildOrder',
|
||||||
'Shape','Vertex','Edge','Wire','Solid','Shell','Compound',
|
'Shape','Vertex','Edge','Wire','Solid','Shell','Compound','exporters',
|
||||||
'SvgExporter','AmfExporter','JsonExporter',
|
|
||||||
'plugins'
|
'plugins'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
3
cadquery/freecad_impl/README.txt
Normal file
3
cadquery/freecad_impl/README.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
It is ok for files in this directory to import FreeCAD, FreeCAD.Base, and FreeCAD.Part.
|
||||||
|
|
||||||
|
Other modules should _not_ depend on FreeCAD
|
|
@ -17,6 +17,9 @@
|
||||||
License along with this library; If not, see <http://www.gnu.org/licenses/>
|
License along with this library; If not, see <http://www.gnu.org/licenses/>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import FreeCAD
|
||||||
|
from FreeCAD import Drawing
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import xml.etree.cElementTree as ET
|
import xml.etree.cElementTree as ET
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -54,15 +57,8 @@ def guessUnitOfMeasure(shape):
|
||||||
|
|
||||||
return UNITS.MM
|
return UNITS.MM
|
||||||
|
|
||||||
class Exporter(object):
|
|
||||||
|
|
||||||
def export(self):
|
|
||||||
"""
|
|
||||||
return a string representing the model exported in the specified format
|
|
||||||
"""
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
class AmfExporter(Exporter):
|
class AmfExporter(object):
|
||||||
def __init__(self,tessellation):
|
def __init__(self,tessellation):
|
||||||
|
|
||||||
self.units = "mm"
|
self.units = "mm"
|
||||||
|
@ -105,7 +101,7 @@ class AmfExporter(Exporter):
|
||||||
three.js JSON object notation
|
three.js JSON object notation
|
||||||
https://github.com/mrdoob/three.js/wiki/JSON-Model-format-3.0
|
https://github.com/mrdoob/three.js/wiki/JSON-Model-format-3.0
|
||||||
"""
|
"""
|
||||||
class JsonExporter(Exporter):
|
class JsonExporter(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
self.vertices = [];
|
self.vertices = [];
|
||||||
|
@ -135,10 +131,6 @@ class JsonExporter(Exporter):
|
||||||
'nFaces' : self.nFaces
|
'nFaces' : self.nFaces
|
||||||
};
|
};
|
||||||
|
|
||||||
class SvgExporter(Exporter):
|
|
||||||
|
|
||||||
def export(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def getPaths(freeCadSVG):
|
def getPaths(freeCadSVG):
|
||||||
"""
|
"""
|
||||||
|
@ -240,11 +232,15 @@ def getSVG(shape,opts=None):
|
||||||
#)
|
#)
|
||||||
return svg
|
return svg
|
||||||
|
|
||||||
|
|
||||||
def exportSVG(shape, fileName):
|
def exportSVG(shape, fileName):
|
||||||
"""
|
"""
|
||||||
|
accept a cadquery shape, and export it to the provided file
|
||||||
|
TODO: should use file-like objects, not a fileName, and/or be able to return a string instead
|
||||||
export a view of a part to svg
|
export a view of a part to svg
|
||||||
"""
|
"""
|
||||||
svg = getSVG(shape)
|
|
||||||
|
svg = getSVG(shape.val().wrapped)
|
||||||
f = open(fileName,'w')
|
f = open(fileName,'w')
|
||||||
f.write(svg)
|
f.write(svg)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
"""
|
"""
|
||||||
Copyright (C) 2011-2013 Parametric Products Intellectual Holdings, LLC
|
Copyright (C) 2011-2013 Parametric Products Intellectual Holdings, LLC
|
||||||
|
|
||||||
This file is part of CadQuery.
|
This file is part of CadQuery.
|
||||||
|
|
||||||
CadQuery is free software; you can redistribute it and/or
|
CadQuery is free software; you can redistribute it and/or
|
||||||
modify it under the terms of the GNU Lesser General Public
|
modify it under the terms of the GNU Lesser General Public
|
||||||
License as published by the Free Software Foundation; either
|
License as published by the Free Software Foundation; either
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
CadQuery is distributed in the hope that it will be useful,
|
CadQuery is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
Lesser General Public License for more details.
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
You should have received a copy of the GNU Lesser General Public
|
||||||
License along with this library; If not, see <http://www.gnu.org/licenses/>
|
License along with this library; If not, see <http://www.gnu.org/licenses/>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import math,sys
|
import math,sys
|
||||||
|
@ -48,33 +48,6 @@ def sortWiresByBuildOrder(wireList,plane,result=[]):
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def sortWiresByBuildOrderOld(wireList,plane,result=[]):
|
|
||||||
"""
|
|
||||||
Tries to determine how wires should be combined into faces.
|
|
||||||
Assume:
|
|
||||||
The wires make up one or more faces, which could have 'holes'
|
|
||||||
Outer wires are listed ahead of inner wires
|
|
||||||
there are no wires inside wires inside wires ( IE, islands -- we can deal with that later on )
|
|
||||||
none of the wires are construction wires
|
|
||||||
Compute:
|
|
||||||
one or more sets of wires, with the outer wire listed first, and inner ones
|
|
||||||
Returns, list of lists.
|
|
||||||
"""
|
|
||||||
outerWire = wireList.pop(0)
|
|
||||||
|
|
||||||
remainingWires = list(wireList)
|
|
||||||
childWires = []
|
|
||||||
for w in wireList:
|
|
||||||
if plane.isWireInside(outerWire,w):
|
|
||||||
childWires.append(remainingWires.pop(0))
|
|
||||||
else:
|
|
||||||
#doesnt match, assume this wire is a new outer
|
|
||||||
result.append([outerWire] + childWires)
|
|
||||||
return sortWiresByBuildOrder(remainingWires,plane,result)
|
|
||||||
|
|
||||||
result.append([outerWire] + childWires)
|
|
||||||
return result
|
|
||||||
|
|
||||||
class Vector(object):
|
class Vector(object):
|
||||||
"""
|
"""
|
||||||
Create a 3-dimensional vector
|
Create a 3-dimensional vector
|
||||||
|
@ -212,6 +185,25 @@ class Vector(object):
|
||||||
def __eq__(self,other):
|
def __eq__(self,other):
|
||||||
return self.wrapped.__eq__(other)
|
return self.wrapped.__eq__(other)
|
||||||
|
|
||||||
|
class Matrix:
|
||||||
|
"""
|
||||||
|
A 3d , 4x4 transformation matrix.
|
||||||
|
|
||||||
|
Used to move geometry in space.
|
||||||
|
"""
|
||||||
|
def __init__(self,matrix=None):
|
||||||
|
if matrix == None:
|
||||||
|
self.wrapped = FreeCAD.Base.Matrix()
|
||||||
|
else:
|
||||||
|
self.wrapped = matrix
|
||||||
|
|
||||||
|
def rotateX(self,angle):
|
||||||
|
self.wrapped.rotateX(angle)
|
||||||
|
|
||||||
|
def rotateY(self,angle):
|
||||||
|
self.wrapped.rotateY(angle)
|
||||||
|
|
||||||
|
|
||||||
class Plane:
|
class Plane:
|
||||||
"""
|
"""
|
||||||
A 2d coordinate system in space, with the x-y axes on the a plane, and a particular point as the origin.
|
A 2d coordinate system in space, with the x-y axes on the a plane, and a particular point as the origin.
|
||||||
|
@ -329,6 +321,7 @@ class Plane:
|
||||||
|
|
||||||
self.setOrigin3d(origin)
|
self.setOrigin3d(origin)
|
||||||
|
|
||||||
|
|
||||||
def setOrigin3d(self,originVector):
|
def setOrigin3d(self,originVector):
|
||||||
"""
|
"""
|
||||||
Move the origin of the plane, leaving its orientation and xDirection unchanged.
|
Move the origin of the plane, leaving its orientation and xDirection unchanged.
|
||||||
|
@ -417,6 +410,7 @@ class Plane:
|
||||||
else:
|
else:
|
||||||
raise ValueError("Dont know how to convert type %s to local coordinates" % str(type(obj)))
|
raise ValueError("Dont know how to convert type %s to local coordinates" % str(type(obj)))
|
||||||
|
|
||||||
|
|
||||||
def toWorldCoords(self, tuplePoint):
|
def toWorldCoords(self, tuplePoint):
|
||||||
"""
|
"""
|
||||||
Convert a point in local coordinates to global coordinates.
|
Convert a point in local coordinates to global coordinates.
|
||||||
|
@ -433,6 +427,7 @@ class Plane:
|
||||||
v = Vector(tuplePoint[0],tuplePoint[1],tuplePoint[2])
|
v = Vector(tuplePoint[0],tuplePoint[1],tuplePoint[2])
|
||||||
return Vector(self.rG.multiply(v.wrapped))
|
return Vector(self.rG.multiply(v.wrapped))
|
||||||
|
|
||||||
|
|
||||||
def rotated(self,rotate=Vector(0,0,0)):
|
def rotated(self,rotate=Vector(0,0,0)):
|
||||||
"""
|
"""
|
||||||
returns a copy of this plane, rotated about the specified axes, as measured from horizontal
|
returns a copy of this plane, rotated about the specified axes, as measured from horizontal
|
||||||
|
@ -453,7 +448,7 @@ class Plane:
|
||||||
rotate = rotate.multiply(math.pi / 180.0 )
|
rotate = rotate.multiply(math.pi / 180.0 )
|
||||||
|
|
||||||
#compute rotation matrix
|
#compute rotation matrix
|
||||||
m = Base.Matrix()
|
m = FreeCAD.Base.Matrix()
|
||||||
m.rotateX(rotate.x)
|
m.rotateX(rotate.x)
|
||||||
m.rotateY(rotate.y)
|
m.rotateY(rotate.y)
|
||||||
m.rotateZ(rotate.z)
|
m.rotateZ(rotate.z)
|
||||||
|
@ -473,7 +468,7 @@ class Plane:
|
||||||
#ok i will be really honest-- i cannot understand exactly why this works
|
#ok i will be really honest-- i cannot understand exactly why this works
|
||||||
#something bout the order of the transaltion and the rotation.
|
#something bout the order of the transaltion and the rotation.
|
||||||
# the double-inverting is strange, and i dont understand it.
|
# the double-inverting is strange, and i dont understand it.
|
||||||
r = Base.Matrix()
|
r = FreeCAD.Base.Matrix()
|
||||||
|
|
||||||
#forward transform must rotate and adjust for origin
|
#forward transform must rotate and adjust for origin
|
||||||
(r.A11, r.A12, r.A13 ) = (self.xDir.x, self.xDir.y, self.xDir.z )
|
(r.A11, r.A12, r.A13 ) = (self.xDir.x, self.xDir.y, self.xDir.z )
|
||||||
|
@ -484,7 +479,14 @@ class Plane:
|
||||||
(invR.A14,invR.A24,invR.A34) = (self.origin.x,self.origin.y,self.origin.z)
|
(invR.A14,invR.A24,invR.A34) = (self.origin.x,self.origin.y,self.origin.z)
|
||||||
|
|
||||||
( self.rG,self.fG ) = ( invR,invR.inverse() )
|
( self.rG,self.fG ) = ( invR,invR.inverse() )
|
||||||
|
|
||||||
|
def computeTransform(self,tMatrix):
|
||||||
|
"""
|
||||||
|
Computes the 2-d projection of the supplied matrix
|
||||||
|
"""
|
||||||
|
|
||||||
|
rm = self.fG.multiply(tMatrix.wrapped).multiply(self.rG)
|
||||||
|
return Matrix(rm)
|
||||||
|
|
||||||
class BoundBox(object):
|
class BoundBox(object):
|
||||||
"A BoundingBox for an object or set of objects. Wraps the FreeCAD one"
|
"A BoundingBox for an object or set of objects. Wraps the FreeCAD one"
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
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
|
from cadquery import Vector,BoundBox
|
||||||
import FreeCAD
|
import FreeCAD
|
||||||
import FreeCAD.Part
|
import FreeCAD.Part
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ class Shape(object):
|
||||||
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) == Base.Vector:
|
if type(obj) == FreeCAD.Base.Vector:
|
||||||
return Vector(obj)
|
return Vector(obj)
|
||||||
tr = None
|
tr = None
|
||||||
|
|
||||||
|
@ -99,7 +99,9 @@ class Shape(object):
|
||||||
|
|
||||||
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!
|
||||||
|
#
|
||||||
def exportStl(self,fileName):
|
def exportStl(self,fileName):
|
||||||
self.wrapped.exportStl(fileName)
|
self.wrapped.exportStl(fileName)
|
||||||
|
|
||||||
|
@ -254,6 +256,7 @@ class Shape(object):
|
||||||
|
|
||||||
def transformShape(self,tMatrix):
|
def transformShape(self,tMatrix):
|
||||||
"""
|
"""
|
||||||
|
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,
|
||||||
with all objects keeping their type
|
with all objects keeping their type
|
||||||
"""
|
"""
|
||||||
|
@ -265,6 +268,8 @@ class Shape(object):
|
||||||
|
|
||||||
def transformGeometry(self,tMatrix):
|
def transformGeometry(self,tMatrix):
|
||||||
"""
|
"""
|
||||||
|
tMatrix is a matrix object.
|
||||||
|
|
||||||
returns a copy of the object, but with geometry transformed insetad of just
|
returns a copy of the object, but with geometry transformed insetad of just
|
||||||
rotated.
|
rotated.
|
||||||
|
|
||||||
|
@ -311,9 +316,9 @@ class Edge(Shape):
|
||||||
#self.endPoint = None
|
#self.endPoint = None
|
||||||
|
|
||||||
self.edgetypes= {
|
self.edgetypes= {
|
||||||
Part.Line : 'LINE',
|
FreeCAD.Part.Line : 'LINE',
|
||||||
Part.ArcOfCircle : 'ARC',
|
FreeCAD.Part.ArcOfCircle : 'ARC',
|
||||||
Part.Circle : 'CIRCLE'
|
FreeCAD.Part.Circle : 'CIRCLE'
|
||||||
}
|
}
|
||||||
|
|
||||||
def geomType(self):
|
def geomType(self):
|
||||||
|
@ -363,7 +368,7 @@ class Edge(Shape):
|
||||||
|
|
||||||
@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(Part.makeCircle(radius,toVector(pnt),toVector(dir),angle1,angle2))
|
return Edge(FreeCAD.Part.makeCircle(radius,toVector(pnt),toVector(dir),angle1,angle2))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makeSpline(cls,listOfVector):
|
def makeSpline(cls,listOfVector):
|
||||||
|
@ -375,7 +380,7 @@ class Edge(Shape):
|
||||||
"""
|
"""
|
||||||
vecs = [v.wrapped for v in listOfVector]
|
vecs = [v.wrapped for v in listOfVector]
|
||||||
|
|
||||||
spline = Part.BSplineCurve()
|
spline = FreeCAD.Part.BSplineCurve()
|
||||||
spline.interpolate(vecs,False)
|
spline.interpolate(vecs,False)
|
||||||
return Edge(spline.toShape())
|
return Edge(spline.toShape())
|
||||||
|
|
||||||
|
@ -389,7 +394,7 @@ 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 = Part.Arc(v1.wrapped,v2.wrapped,v3.wrapped)
|
arc = FreeCAD.Part.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
|
||||||
|
|
||||||
|
@ -401,7 +406,7 @@ class Edge(Shape):
|
||||||
: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(Part.makeLine(v1.toTuple(),v2.toTuple() ))
|
return Edge(FreeCAD.Part.makeLine(v1.toTuple(),v2.toTuple() ))
|
||||||
|
|
||||||
|
|
||||||
class Wire(Shape):
|
class Wire(Shape):
|
||||||
|
@ -420,7 +425,7 @@ class Wire(Shape):
|
||||||
:param listOfWires:
|
:param listOfWires:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
return Shape.cast(Part.Wire([w.wrapped for w in listOfWires]))
|
return Shape.cast(FreeCAD.Part.Wire([w.wrapped for w in listOfWires]))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def assembleEdges(cls,listOfEdges):
|
def assembleEdges(cls,listOfEdges):
|
||||||
|
@ -432,7 +437,7 @@ class Wire(Shape):
|
||||||
"""
|
"""
|
||||||
fCEdges = [a.wrapped for a in listOfEdges]
|
fCEdges = [a.wrapped for a in listOfEdges]
|
||||||
|
|
||||||
wa = Wire( Part.Wire(fCEdges) )
|
wa = Wire( FreeCAD.Part.Wire(fCEdges) )
|
||||||
return wa
|
return wa
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -444,13 +449,13 @@ 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(Part.Wire([Part.makeCircle(radius,center.wrapped,normal.wrapped)]))
|
w = Wire(FreeCAD.Part.Wire([FreeCAD.Part.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(Part.makePolygon([i.wrapped for i in listOfVertices]))
|
w = Wire(FreeCAD.Part.makePolygon([i.wrapped for i in listOfVertices]))
|
||||||
w.forConstruction = forConstruction
|
w.forConstruction = forConstruction
|
||||||
return w
|
return w
|
||||||
|
|
||||||
|
@ -461,7 +466,7 @@ class Wire(Shape):
|
||||||
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(Part.makeHelix(pitch,height,radius,angle))
|
return Wire(FreeCAD.Part.makeHelix(pitch,height,radius,angle))
|
||||||
|
|
||||||
|
|
||||||
class Face(Shape):
|
class Face(Shape):
|
||||||
|
@ -473,9 +478,9 @@ class Face(Shape):
|
||||||
|
|
||||||
self.facetypes = {
|
self.facetypes = {
|
||||||
#TODO: bezier,bspline etc
|
#TODO: bezier,bspline etc
|
||||||
Part.Plane : 'PLANE',
|
FreeCAD.Part.Plane : 'PLANE',
|
||||||
Part.Sphere : 'SPHERE',
|
FreeCAD.Part.Sphere : 'SPHERE',
|
||||||
Part.Cone : 'CONE'
|
FreeCAD.Part.Cone : 'CONE'
|
||||||
}
|
}
|
||||||
|
|
||||||
def geomType(self):
|
def geomType(self):
|
||||||
|
@ -501,7 +506,7 @@ class Face(Shape):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makePlane(cls,length,width,basePnt=None,dir=None):
|
def makePlane(cls,length,width,basePnt=None,dir=None):
|
||||||
return Face(Part.makePlan(length,width,toVector(basePnt),toVector(dir)))
|
return Face(FreeCAD.Part.makePlan(length,width,toVector(basePnt),toVector(dir)))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makeRuledSurface(cls,edgeOrWire1,edgeOrWire2,dist=None):
|
def makeRuledSurface(cls,edgeOrWire1,edgeOrWire2,dist=None):
|
||||||
|
@ -510,7 +515,7 @@ class Face(Shape):
|
||||||
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(Part.makeRuledSurface(edgeOrWire1.obj,edgeOrWire2.obj,dist))
|
return Shape.cast(FreeCAD.Part.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"
|
||||||
|
@ -536,7 +541,7 @@ class Shell(Shape):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def makeShell(cls,listOfFaces):
|
def makeShell(cls,listOfFaces):
|
||||||
return Shell(Part.makeShell([i.obj for i in listOfFaces]))
|
return Shell(FreeCAD.Part.makeShell([i.obj for i in listOfFaces]))
|
||||||
|
|
||||||
|
|
||||||
class Solid(Shape):
|
class Solid(Shape):
|
||||||
|
@ -563,7 +568,7 @@ class Solid(Shape):
|
||||||
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(Part.makeBox(length,width,height,pnt.wrapped,dir.wrapped))
|
return Shape.cast(FreeCAD.Part.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):
|
||||||
|
@ -572,7 +577,7 @@ class Solid(Shape):
|
||||||
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(Part.makeCone(radius1,radius2,height,pnt.wrapped,dir.wrapped,angleDegrees))
|
return Shape.cast(FreeCAD.Part.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):
|
||||||
|
@ -581,7 +586,7 @@ class Solid(Shape):
|
||||||
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(Part.makeCylinder(radius,height,pnt.wrapped,dir.wrapped,angleDegrees))
|
return Shape.cast(FreeCAD.Part.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):
|
||||||
|
@ -591,7 +596,7 @@ class Solid(Shape):
|
||||||
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(Part.makeTorus(radius1,radius2,pnt,dir,angleDegrees1,angleDegrees2))
|
return Shape.cast(FreeCAD.Part.makeTorus(radius1,radius2,pnt,dir,angleDegrees1,angleDegrees2))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def sweep(cls,profileWire,pathWire):
|
def sweep(cls,profileWire,pathWire):
|
||||||
|
@ -610,11 +615,11 @@ class Solid(Shape):
|
||||||
"""
|
"""
|
||||||
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 part.
|
wants to make an infinitely thin shell for a real FreeCAD.Part.
|
||||||
"""
|
"""
|
||||||
#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(Part.makeLoft([i.wrapped for i in listOfWire],True))
|
return Shape.cast(FreeCAD.Part.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):
|
||||||
|
@ -624,7 +629,7 @@ class Solid(Shape):
|
||||||
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(Part.makeWedge(xmin,ymin,zmin,z2min,x2min,xmax,ymax,zmax,z2max,x2max,pnt,dir))
|
return Shape.cast(FreeCAD.Part.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):
|
||||||
|
@ -633,7 +638,7 @@ class Solid(Shape):
|
||||||
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(Part.makeSphere(radius,pnt,angleDegrees1,angleDegrees2,angleDegrees3))
|
return Solid(FreeCAD.Part.makeSphere(radius,pnt,angleDegrees1,angleDegrees2,angleDegrees3))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def extrudeLinearWithRotation(cls,outerWire,innerWires,vecCenter, vecNormal,angleDegrees):
|
def extrudeLinearWithRotation(cls,outerWire,innerWires,vecCenter, vecNormal,angleDegrees):
|
||||||
|
@ -674,12 +679,12 @@ class Solid(Shape):
|
||||||
#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 = Part.makeRuledSurface(w1,w2)
|
rs = FreeCAD.Part.makeRuledSurface(w1,w2)
|
||||||
sides.append(rs)
|
sides.append(rs)
|
||||||
|
|
||||||
#make faces for the top and bottom
|
#make faces for the top and bottom
|
||||||
startFace = Part.Face(startWires)
|
startFace = FreeCAD.Part.Face(startWires)
|
||||||
endFace = Part.Face(endWires)
|
endFace = FreeCAD.Part.Face(endWires)
|
||||||
|
|
||||||
#collect all the faces from the sides
|
#collect all the faces from the sides
|
||||||
faceList = [ startFace]
|
faceList = [ startFace]
|
||||||
|
@ -687,8 +692,8 @@ class Solid(Shape):
|
||||||
faceList.extend(s.Faces)
|
faceList.extend(s.Faces)
|
||||||
faceList.append(endFace)
|
faceList.append(endFace)
|
||||||
|
|
||||||
shell = Part.makeShell(faceList)
|
shell = FreeCAD.Part.makeShell(faceList)
|
||||||
solid = Part.makeSolid(shell)
|
solid = FreeCAD.Part.makeSolid(shell)
|
||||||
return Shape.cast(solid)
|
return Shape.cast(solid)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -725,7 +730,7 @@ class Solid(Shape):
|
||||||
for w in innerWires:
|
for w in innerWires:
|
||||||
freeCADWires.append(w.wrapped)
|
freeCADWires.append(w.wrapped)
|
||||||
|
|
||||||
f = Part.Face(freeCADWires)
|
f = FreeCAD.Part.Face(freeCADWires)
|
||||||
result = f.extrude(vecNormal.wrapped)
|
result = f.extrude(vecNormal.wrapped)
|
||||||
|
|
||||||
return Shape.cast(result)
|
return Shape.cast(result)
|
||||||
|
@ -789,7 +794,7 @@ class Compound(Shape):
|
||||||
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 = Part.Compound(solids)
|
c = FreeCAD.Part.Compound(solids)
|
||||||
return Shape.cast( c)
|
return Shape.cast( c)
|
||||||
|
|
||||||
def fuse(self,toJoin):
|
def fuse(self,toJoin):
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import math
|
import math
|
||||||
|
from cadquery import Vector,Edge,Vertex,Face,Solid,Shell,Compound
|
||||||
|
|
||||||
class Selector(object):
|
class Selector(object):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import math
|
import math
|
||||||
from . import CQ
|
|
||||||
from cadquery import Vector
|
from cadquery import Vector,CQ,CQContext,Plane,Wire
|
||||||
|
|
||||||
class Workplane(CQ):
|
class Workplane(CQ):
|
||||||
"""
|
"""
|
||||||
|
|
16
runtests.py
Normal file
16
runtests.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import sys
|
||||||
|
from tests import *
|
||||||
|
import cadquery
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
#if you are on python 2.7, you can use -m uniitest discover.
|
||||||
|
#but this is required for python 2.6.6 on windows. FreeCAD0.12 will not load
|
||||||
|
#on py 2.7.x on win
|
||||||
|
suite = unittest.TestSuite()
|
||||||
|
|
||||||
|
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCadObjects.TestCadObjects))
|
||||||
|
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestWorkplanes.TestWorkplanes))
|
||||||
|
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCQSelectors.TestCQSelectors))
|
||||||
|
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCadQuery.TestCadQuery))
|
||||||
|
|
||||||
|
unittest.TextTestRunner().run(suite)
|
1
tests/README.txt
Normal file
1
tests/README.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
It is OK for tests to import implementations like FreeCAD directly.
|
|
@ -13,8 +13,9 @@ import unittest,sys
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
#my modules
|
#my modules
|
||||||
from TestBase import *
|
from tests import BaseTest,makeUnitCube,makeUnitSquareWire
|
||||||
from cadquery import *
|
from cadquery import *
|
||||||
|
from cadquery import selectors
|
||||||
|
|
||||||
class TestCQSelectors(BaseTest):
|
class TestCQSelectors(BaseTest):
|
||||||
|
|
||||||
|
@ -122,8 +123,8 @@ class TestCQSelectors(BaseTest):
|
||||||
#faces parallel to Z axis
|
#faces parallel to Z axis
|
||||||
self.assertEqual(2, c.faces("|Z").size())
|
self.assertEqual(2, c.faces("|Z").size())
|
||||||
#TODO: provide short names for ParallelDirSelector
|
#TODO: provide short names for ParallelDirSelector
|
||||||
self.assertEqual(2, c.faces(ParallelDirSelector(Vector((0,0,1)))).size()) #same thing as above
|
self.assertEqual(2, c.faces(selectors.ParallelDirSelector(Vector((0,0,1)))).size()) #same thing as above
|
||||||
self.assertEqual(2, c.faces(ParallelDirSelector(Vector((0,0,-1)))).size()) #same thing as above
|
self.assertEqual(2, c.faces(selectors.ParallelDirSelector(Vector((0,0,-1)))).size()) #same thing as above
|
||||||
|
|
||||||
#just for fun, vertices on faces parallel to z
|
#just for fun, vertices on faces parallel to z
|
||||||
self.assertEqual(8, c.faces("|Z").vertices().size())
|
self.assertEqual(8, c.faces("|Z").vertices().size())
|
||||||
|
@ -164,17 +165,17 @@ class TestCQSelectors(BaseTest):
|
||||||
#nearest vertex to origin is (0,0,0)
|
#nearest vertex to origin is (0,0,0)
|
||||||
t = Vector(0.1,0.1,0.1)
|
t = Vector(0.1,0.1,0.1)
|
||||||
|
|
||||||
v = c.vertices(NearestToPointSelector(t)).vals()[0]
|
v = c.vertices(selectors.NearestToPointSelector(t)).vals()[0]
|
||||||
self.assertTupleAlmostEquals((0.0,0.0,0.0),(v.X,v.Y,v.Z),3)
|
self.assertTupleAlmostEquals((0.0,0.0,0.0),(v.X,v.Y,v.Z),3)
|
||||||
|
|
||||||
t = Vector(0.1,0.1,0.2)
|
t = Vector(0.1,0.1,0.2)
|
||||||
#nearest edge is the vertical side edge, 0,0,0 -> 0,0,1
|
#nearest edge is the vertical side edge, 0,0,0 -> 0,0,1
|
||||||
e = c.edges(NearestToPointSelector(t)).vals()[0]
|
e = c.edges(selectors.NearestToPointSelector(t)).vals()[0]
|
||||||
v = c.edges(NearestToPointSelector(t)).vertices().vals()
|
v = c.edges(selectors.NearestToPointSelector(t)).vertices().vals()
|
||||||
self.assertEqual(2,len(v))
|
self.assertEqual(2,len(v))
|
||||||
|
|
||||||
#nearest solid is myself
|
#nearest solid is myself
|
||||||
s = c.solids(NearestToPointSelector(t)).vals()
|
s = c.solids(selectors.NearestToPointSelector(t)).vals()
|
||||||
self.assertEqual(1,len(s))
|
self.assertEqual(1,len(s))
|
||||||
|
|
||||||
def testFaceCount(self):
|
def testFaceCount(self):
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
#system modules
|
#system modules
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
#my modules
|
import unittest
|
||||||
|
from tests import BaseTest
|
||||||
import from cadquery import *
|
import FreeCAD
|
||||||
|
from cadquery import *
|
||||||
|
|
||||||
class TestCadObjects(BaseTest):
|
class TestCadObjects(BaseTest):
|
||||||
|
|
||||||
def testVectorConstructors(self):
|
def testVectorConstructors(self):
|
||||||
|
@ -56,4 +56,7 @@ class TestCadObjects(BaseTest):
|
||||||
|
|
||||||
def testVertices(self):
|
def testVertices(self):
|
||||||
e = Shape.cast(FreeCAD.Part.makeLine((0,0,0),(1,1,0)))
|
e = Shape.cast(FreeCAD.Part.makeLine((0,0,0),(1,1,0)))
|
||||||
self.assertEquals(2,len(e.Vertices()))
|
self.assertEquals(2,len(e.Vertices()))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
|
@ -7,6 +7,8 @@ import math,sys,os.path,time
|
||||||
|
|
||||||
#my modules
|
#my modules
|
||||||
from cadquery import *
|
from cadquery import *
|
||||||
|
from cadquery import exporters
|
||||||
|
from tests import BaseTest,writeStringToFile,makeUnitCube,readFileAsString,makeUnitSquareWire,makeCube
|
||||||
|
|
||||||
#where unit test output will be saved
|
#where unit test output will be saved
|
||||||
OUTDIR = "c:/temp"
|
OUTDIR = "c:/temp"
|
||||||
|
@ -739,16 +741,6 @@ class TestCadQuery(BaseTest):
|
||||||
self.saveModel(s3)
|
self.saveModel(s3)
|
||||||
|
|
||||||
|
|
||||||
def testTwistedGear3(self):
|
|
||||||
pts = plugins.make_gear(14.5,10,2.5) #make involutes
|
|
||||||
s = Workplane("XY").polyline(pts).twistExtrude(4.0,8.0)
|
|
||||||
#s2 = s.faces(">Z").workplane().transformed(rotate=Vector(0,0,8)).polyline(pts).twistExtrude(4.0,-8.0,combine=False)
|
|
||||||
#s3 = s.union(s2)
|
|
||||||
#s.val().exportStl("c:\\temp\\pleasework3.stl")
|
|
||||||
#s3.val().exportStl("c:\\temp\\pleasework5.stl")
|
|
||||||
self.saveModel(s)
|
|
||||||
|
|
||||||
|
|
||||||
def testEnclosure(self):
|
def testEnclosure(self):
|
||||||
"""
|
"""
|
||||||
Builds an electronics enclosure
|
Builds an electronics enclosure
|
||||||
|
|
|
@ -3,7 +3,7 @@ __author__ = 'dcowden'
|
||||||
from cadquery import *
|
from cadquery import *
|
||||||
|
|
||||||
import unittest,sys
|
import unittest,sys
|
||||||
import MakeTestObjects
|
from tests import MakeTestObjects
|
||||||
import SVGexporter
|
import SVGexporter
|
||||||
|
|
||||||
class TestCadQuery(unittest.TestCase):
|
class TestCadQuery(unittest.TestCase):
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
|
|
||||||
#my modules
|
#my modules
|
||||||
from cadquery import *
|
from cadquery import *
|
||||||
|
from tests import BaseTest,toTuple
|
||||||
|
|
||||||
class TestPlane(BaseTest):
|
class TestWorkplanes(BaseTest):
|
||||||
|
|
||||||
|
|
||||||
def testYZPlaneOrigins(self):
|
def testYZPlaneOrigins(self):
|
||||||
#xy plane-- with origin at x=0.25
|
#xy plane-- with origin at x=0.25
|
||||||
|
|
|
@ -32,7 +32,7 @@ def makeCube(size):
|
||||||
def toTuple(v):
|
def toTuple(v):
|
||||||
"convert a vector or a vertex to a 3-tuple: x,y,z"
|
"convert a vector or a vertex to a 3-tuple: x,y,z"
|
||||||
pnt = v
|
pnt = v
|
||||||
if type(v) == Base.Vector:
|
if type(v) == FreeCAD.Base.Vector:
|
||||||
return (v.Point.x,v.Point.y,v.Point.z)
|
return (v.Point.x,v.Point.y,v.Point.z)
|
||||||
elif type(v) == Vector:
|
elif type(v) == Vector:
|
||||||
return v.toTuple()
|
return v.toTuple()
|
||||||
|
@ -45,3 +45,5 @@ class BaseTest(unittest.TestCase):
|
||||||
def assertTupleAlmostEquals(self,expected,actual,places):
|
def assertTupleAlmostEquals(self,expected,actual,places):
|
||||||
for i,j in zip(actual,expected):
|
for i,j in zip(actual,expected):
|
||||||
self.assertAlmostEquals(i,j,places)
|
self.assertAlmostEquals(i,j,places)
|
||||||
|
|
||||||
|
__all__ = [ 'TestCadObjects','TestCadQuery','TestCQSelectors','TestWorkplanes']
|
||||||
|
|
Loading…
Reference in New Issue
Block a user