the test cases pass now at least

This commit is contained in:
Dave Cowden 2013-04-16 22:29:06 -04:00
parent 3f7d38cdeb
commit 26cd0a443d
17 changed files with 1576 additions and 133 deletions

File diff suppressed because it is too large Load Diff

8
cadquery/README.txt Normal file
View 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.
***

View File

@ -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'
] ]

View 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

View File

@ -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): class AmfExporter(object):
"""
return a string representing the model exported in the specified format
"""
raise NotImplementedError()
class AmfExporter(Exporter):
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()

View File

@ -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 )
@ -485,6 +480,13 @@ class Plane:
( 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"

View File

@ -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):

View File

@ -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):
""" """

View File

@ -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
View 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
View File

@ -0,0 +1 @@
It is OK for tests to import implementations like FreeCAD directly.

View File

@ -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):

View File

@ -1,10 +1,10 @@
#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):
@ -57,3 +57,6 @@ 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()

View File

@ -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

View File

@ -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):

View File

@ -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

View File

@ -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']