Updated to work with 0.13

- This is _totally_ experimental, and I haven't done exhaustive
   tests
 - 88 unit tests do pass though, and I added a new import test.
 - 0.12 and 0.13 seem to structure stuff a bit differently,
   probably due to changes in the FreeCAD lib wrappers.
 - Not tested on windows (but should work) or 0.12. Need some
   help there.
This commit is contained in:
Derek Anderson 2013-04-27 23:49:41 -07:00
parent 8089ef1e04
commit c3290fca92
9 changed files with 265 additions and 95 deletions

View File

@ -2,7 +2,7 @@
Copyright (C) 2011-2013 Parametric Products Intellectual Holdings, LLC
This file is part of CadQuery.
CadQuery is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
@ -15,14 +15,25 @@
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/>
An exporter should provide functionality to accept a shape, and return
a string containing the model content.
"""
import cadquery
import FreeCAD,tempfile,os,StringIO
from FreeCAD import Drawing
from .verutil import fc_import
FreeCAD = fc_import("FreeCAD")
import tempfile,os,StringIO
Drawing = fc_import("FreeCAD.Drawing")
#_FCVER = freecad_version()
#if _FCVER>=(0,13):
#import Drawing as FreeCADDrawing #It's in FreeCAD lib path
#elif _FCVER>=(0,12):
#import FreeCAD.Drawing as FreeCADDrawing
#else:
#raise RuntimeError, "Invalid freecad version: %s" % str(".".join(_FCVER))
try:
import xml.etree.cElementTree as ET
@ -35,7 +46,7 @@ class ExportTypes:
AMF = "AMF"
SVG = "SVG"
TJS = "TJS"
class UNITS:
MM = "mm"
IN = "in"
@ -45,7 +56,7 @@ def toString(shape,exportType,tolerance=0.1):
s= StringIO.StringIO()
exportShape(shape,exportType,s,tolerance)
return s.getvalue()
def exportShape(shape,exportType,fileLike,tolerance=0.1):
"""
:param shape: the shape to export. it can be a shape object, or a cadquery object. If a cadquery
@ -54,13 +65,13 @@ def exportShape(shape,exportType,fileLike,tolerance=0.1):
:param tolerance: the tolerance, in model units
:param fileLike: a file like object to which the content will be written.
The object should be already open and ready to write. The caller is responsible
for closing the object
for closing the object
"""
if isinstance(shape,cadquery.CQ):
shape = shape.val()
if exportType == ExportTypes.TJS:
#tessellate the model
tess = shape.tessellate(tolerance)
@ -78,9 +89,9 @@ def exportShape(shape,exportType,fileLike,tolerance=0.1):
fileLike.write(getSVG(shape.wrapped))
elif exportType == ExportTypes.AMF:
tess = shape.tessellate(tolerance)
aw = AmfWriter(tess).writeAmf(fileLike)
aw = AmfWriter(tess).writeAmf(fileLike)
else:
#all these types required writing to a file and then
#re-reading. this is due to the fact that FreeCAD writes these
(h, outFileName) = tempfile.mkstemp()
@ -130,8 +141,8 @@ def guessUnitOfMeasure(shape):
if sum(dimList) < 10:
return UNITS.IN
return UNITS.MM
return UNITS.MM
class AmfWriter(object):
def __init__(self,tessellation):
@ -172,7 +183,7 @@ class AmfWriter(object):
ET.ElementTree(amf).write(outFile,encoding='ISO-8859-1')
"""
Objects that represent
Objects that represent
three.js JSON object notation
https://github.com/mrdoob/three.js/wiki/JSON-Model-format-3.0
"""
@ -183,21 +194,21 @@ class JsonMesh(object):
self.faces = [];
self.nVertices = 0;
self.nFaces = 0;
def addVertex(self,x,y,z):
self.nVertices += 1;
self.vertices.extend([x,y,z]);
#add triangle composed of the three provided vertex indices
def addTriangleFace(self, i,j,k):
#first position means justa simple triangle
self.nFaces += 1;
self.faces.extend([0,int(i),int(j),int(k)]);
"""
Get a json model from this model.
For now we'll forget about colors, vertex normals, and all that stuff
"""
"""
def toJson(self):
return JSON_TEMPLATE % {
'vertices' : str(self.vertices),
@ -249,7 +260,7 @@ def getSVG(shape,opts=None):
"""
d = {'width':800,'height':240,'marginLeft':200,'marginTop':20}
if opts:
d.update(opts)
@ -314,14 +325,14 @@ def exportSVG(shape, fileName):
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
"""
svg = getSVG(shape.val().wrapped)
f = open(fileName,'w')
f.write(svg)
f.close()
JSON_TEMPLATE= """\
{
"metadata" :
@ -336,9 +347,9 @@ JSON_TEMPLATE= """\
"materials" : 1,
"morphTargets" : 0
},
"scale" : 1.0,
"materials": [ {
"DbgColor" : 15658734,
"DbgIndex" : 0,

View File

@ -2,7 +2,7 @@
Copyright (C) 2011-2013 Parametric Products Intellectual Holdings, LLC
This file is part of CadQuery.
CadQuery is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
@ -18,8 +18,10 @@
"""
import math,sys
import FreeCAD
import FreeCAD.Part
#import FreeCAD
from .verutil import fc_import
FreeCAD = fc_import("FreeCAD")
#Turns out we don't need the Part module here.
def sortWiresByBuildOrder(wireList,plane,result=[]):
"""
@ -187,8 +189,8 @@ class Vector(object):
class Matrix:
"""
A 3d , 4x4 transformation matrix.
A 3d , 4x4 transformation matrix.
Used to move geometry in space.
"""
def __init__(self,matrix=None):
@ -196,14 +198,14 @@ class Matrix:
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:
"""
A 2d coordinate system in space, with the x-y axes on the a plane, and a particular point as the origin.
@ -321,7 +323,7 @@ class Plane:
self.setOrigin3d(origin)
def setOrigin3d(self,originVector):
"""
Move the origin of the plane, leaving its orientation and xDirection unchanged.
@ -427,7 +429,7 @@ class Plane:
v = Vector(tuplePoint[0],tuplePoint[1],tuplePoint[2])
return Vector(self.rG.multiply(v.wrapped))
def rotated(self,rotate=(0,0,0)):
"""
returns a copy of this plane, rotated about the specified axes, as measured from horizontal
@ -445,7 +447,7 @@ class Plane:
"""
if rotate.__class__.__name__ != 'Vector':
rotate = Vector(rotate)
rotate = Vector(rotate)
#convert to radians
rotate = rotate.multiply(math.pi / 180.0 )
@ -473,13 +475,13 @@ class Plane:
#compute rotation matrix ( global --> local --> rotate --> global )
#rm = self.plane.fG.multiply(matrix).multiply(self.plane.rG)
rm = self.computeTransform(rotationMatrix)
#There might be a better way, but to do this rotation takes 3 steps
#transform geometry to local coordinates
#then rotate about x
#then transform back to global coordiante
resultWires = []
for w in listOfShapes:
mirrored = w.transformGeometry(rotationMatrix.wrapped)
@ -487,7 +489,7 @@ class Plane:
return resultWires
def _calcTransforms(self):
"""
Computes transformation martrices to convert betwene local and global coordinates
@ -507,12 +509,12 @@ class Plane:
(invR.A14,invR.A24,invR.A34) = (self.origin.x,self.origin.y,self.origin.z)
( 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)

View File

@ -2,7 +2,7 @@
Copyright (C) 2011-2013 Parametric Products Intellectual Holdings, LLC
This file is part of CadQuery.
CadQuery is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
@ -43,13 +43,15 @@
5. allow changing interfaces when we'd like. there are few cases where the freecad api is not
very userfriendly: we like to change those when necesary. As an example, in the freecad api,
all factory methods are on the 'Part' object, but it is very useful to know what kind of
object each one returns, so these are better grouped by the type of object they return.
all factory methods are on the 'Part' object, but it is very useful to know what kind of
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 ?
"""
from cadquery import Vector,BoundBox
import FreeCAD
import FreeCAD.Part
from .verutil import fc_import
FreeCADPart = fc_import("FreeCAD.Part")
class Shape(object):
"""
@ -269,7 +271,7 @@ class Shape(object):
def transformGeometry(self,tMatrix):
"""
tMatrix is a matrix object.
returns a copy of the object, but with geometry transformed insetad of just
rotated.
@ -316,9 +318,9 @@ class Edge(Shape):
#self.endPoint = None
self.edgetypes= {
FreeCAD.Part.Line : 'LINE',
FreeCAD.Part.ArcOfCircle : 'ARC',
FreeCAD.Part.Circle : 'CIRCLE'
FreeCADPart.Line : 'LINE',
FreeCADPart.ArcOfCircle : 'ARC',
FreeCADPart.Circle : 'CIRCLE'
}
def geomType(self):
@ -368,7 +370,7 @@ class Edge(Shape):
@classmethod
def makeCircle(cls,radius,pnt=(0,0,0),dir=(0,0,1),angle1=360.0,angle2=360):
return Edge(FreeCAD.Part.makeCircle(radius,toVector(pnt),toVector(dir),angle1,angle2))
return Edge(FreeCADPart.makeCircle(radius,toVector(pnt),toVector(dir),angle1,angle2))
@classmethod
def makeSpline(cls,listOfVector):
@ -380,7 +382,7 @@ class Edge(Shape):
"""
vecs = [v.wrapped for v in listOfVector]
spline = FreeCAD.Part.BSplineCurve()
spline = FreeCADPart.BSplineCurve()
spline.interpolate(vecs,False)
return Edge(spline.toShape())
@ -394,7 +396,7 @@ class Edge(Shape):
:param v3: end vector
:return: an edge object through the three points
"""
arc = FreeCAD.Part.Arc(v1.wrapped,v2.wrapped,v3.wrapped)
arc = FreeCADPart.Arc(v1.wrapped,v2.wrapped,v3.wrapped)
e = Edge(arc.toShape())
return e #arcane and undocumented, this creates an Edge object
@ -406,7 +408,7 @@ class Edge(Shape):
:param v2: Vector that represents the second point
:return: A linear edge between the two provided points
"""
return Edge(FreeCAD.Part.makeLine(v1.toTuple(),v2.toTuple() ))
return Edge(FreeCADPart.makeLine(v1.toTuple(),v2.toTuple() ))
class Wire(Shape):
@ -425,7 +427,7 @@ class Wire(Shape):
:param listOfWires:
:return:
"""
return Shape.cast(FreeCAD.Part.Wire([w.wrapped for w in listOfWires]))
return Shape.cast(FreeCADPart.Wire([w.wrapped for w in listOfWires]))
@classmethod
def assembleEdges(cls,listOfEdges):
@ -437,7 +439,7 @@ class Wire(Shape):
"""
fCEdges = [a.wrapped for a in listOfEdges]
wa = Wire( FreeCAD.Part.Wire(fCEdges) )
wa = Wire( FreeCADPart.Wire(fCEdges) )
return wa
@classmethod
@ -449,13 +451,13 @@ class Wire(Shape):
:param normal: vector representing the direction of the plane the circle should lie in
:return:
"""
w = Wire(FreeCAD.Part.Wire([FreeCAD.Part.makeCircle(radius,center.wrapped,normal.wrapped)]))
w = Wire(FreeCADPart.Wire([FreeCADPart.makeCircle(radius,center.wrapped,normal.wrapped)]))
return w
@classmethod
def makePolygon(cls,listOfVertices,forConstruction=False):
#convert list of tuples into Vectors.
w = Wire(FreeCAD.Part.makePolygon([i.wrapped for i in listOfVertices]))
w = Wire(FreeCADPart.makePolygon([i.wrapped for i in listOfVertices]))
w.forConstruction = forConstruction
return w
@ -466,7 +468,7 @@ class Wire(Shape):
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'
"""
return Wire(FreeCAD.Part.makeHelix(pitch,height,radius,angle))
return Wire(FreeCADPart.makeHelix(pitch,height,radius,angle))
class Face(Shape):
@ -478,9 +480,9 @@ class Face(Shape):
self.facetypes = {
#TODO: bezier,bspline etc
FreeCAD.Part.Plane : 'PLANE',
FreeCAD.Part.Sphere : 'SPHERE',
FreeCAD.Part.Cone : 'CONE'
FreeCADPart.Plane : 'PLANE',
FreeCADPart.Sphere : 'SPHERE',
FreeCADPart.Cone : 'CONE'
}
def geomType(self):
@ -506,7 +508,7 @@ class Face(Shape):
@classmethod
def makePlane(cls,length,width,basePnt=None,dir=None):
return Face(FreeCAD.Part.makePlan(length,width,toVector(basePnt),toVector(dir)))
return Face(FreeCADPart.makePlan(length,width,toVector(basePnt),toVector(dir)))
@classmethod
def makeRuledSurface(cls,edgeOrWire1,edgeOrWire2,dist=None):
@ -515,7 +517,7 @@ class Face(Shape):
Create a ruled surface out of two edges or wires. If wires are used then
these must have the same
"""
return Shape.cast(FreeCAD.Part.makeRuledSurface(edgeOrWire1.obj,edgeOrWire2.obj,dist))
return Shape.cast(FreeCADPart.makeRuledSurface(edgeOrWire1.obj,edgeOrWire2.obj,dist))
def cut(self,faceToCut):
"Remove a face from another one"
@ -541,7 +543,7 @@ class Shell(Shape):
@classmethod
def makeShell(cls,listOfFaces):
return Shell(FreeCAD.Part.makeShell([i.obj for i in listOfFaces]))
return Shell(FreeCADPart.makeShell([i.obj for i in listOfFaces]))
class Solid(Shape):
@ -568,7 +570,7 @@ class Solid(Shape):
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)'
"""
return Shape.cast(FreeCAD.Part.makeBox(length,width,height,pnt.wrapped,dir.wrapped))
return Shape.cast(FreeCADPart.makeBox(length,width,height,pnt.wrapped,dir.wrapped))
@classmethod
def makeCone(cls,radius1,radius2,height,pnt=Vector(0,0,0),dir=Vector(0,0,1),angleDegrees=360):
@ -577,7 +579,7 @@ class Solid(Shape):
Make a cone with given radii and height\nBy default pnt=Vector(0,0,0),
dir=Vector(0,0,1) and angle=360'
"""
return Shape.cast(FreeCAD.Part.makeCone(radius1,radius2,height,pnt.wrapped,dir.wrapped,angleDegrees))
return Shape.cast(FreeCADPart.makeCone(radius1,radius2,height,pnt.wrapped,dir.wrapped,angleDegrees))
@classmethod
def makeCylinder(cls,radius,height,pnt=Vector(0,0,0),dir=Vector(0,0,1),angleDegrees=360):
@ -586,7 +588,7 @@ class Solid(Shape):
Make a cylinder with a given radius and height
By default pnt=Vector(0,0,0),dir=Vector(0,0,1) and angle=360'
"""
return Shape.cast(FreeCAD.Part.makeCylinder(radius,height,pnt.wrapped,dir.wrapped,angleDegrees))
return Shape.cast(FreeCADPart.makeCylinder(radius,height,pnt.wrapped,dir.wrapped,angleDegrees))
@classmethod
def makeTorus(cls,radius1,radius2,pnt=None,dir=None,angleDegrees1=None,angleDegrees2=None):
@ -596,7 +598,7 @@ class Solid(Shape):
By default pnt=Vector(0,0,0),dir=Vector(0,0,1),angle1=0
,angle1=360 and angle=360'
"""
return Shape.cast(FreeCAD.Part.makeTorus(radius1,radius2,pnt,dir,angleDegrees1,angleDegrees2))
return Shape.cast(FreeCADPart.makeTorus(radius1,radius2,pnt,dir,angleDegrees1,angleDegrees2))
@classmethod
def sweep(cls,profileWire,pathWire):
@ -615,11 +617,11 @@ class Solid(Shape):
"""
makes a loft from a list of wires
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 FreeCAD.Part.
wants to make an infinitely thin shell for a real FreeCADPart.
"""
#the True flag requests building a solid instead of a shell.
return Shape.cast(FreeCAD.Part.makeLoft([i.wrapped for i in listOfWire],True))
return Shape.cast(FreeCADPart.makeLoft([i.wrapped for i in listOfWire],True))
@classmethod
def makeWedge(cls,xmin,ymin,zmin,z2min,x2min,xmax,ymax,zmax,z2max,x2max,pnt=None,dir=None):
@ -629,7 +631,7 @@ class Solid(Shape):
Make a wedge located in pnt\nBy default pnt=Vector(0,0,0) and dir=Vec
tor(0,0,1)'
"""
return Shape.cast(FreeCAD.Part.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
def makeSphere(cls,radius,pnt=None,angleDegrees1=None,angleDegrees2=None,angleDegrees3=None):
@ -638,7 +640,7 @@ class Solid(Shape):
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'
"""
return Solid(FreeCAD.Part.makeSphere(radius,pnt,angleDegrees1,angleDegrees2,angleDegrees3))
return Solid(FreeCADPart.makeSphere(radius,pnt,angleDegrees1,angleDegrees2,angleDegrees3))
@classmethod
def extrudeLinearWithRotation(cls,outerWire,innerWires,vecCenter, vecNormal,angleDegrees):
@ -679,12 +681,12 @@ class Solid(Shape):
#make a ruled surface for each set of wires
sides = []
for w1,w2 in zip(startWires,endWires):
rs = FreeCAD.Part.makeRuledSurface(w1,w2)
rs = FreeCADPart.makeRuledSurface(w1,w2)
sides.append(rs)
#make faces for the top and bottom
startFace = FreeCAD.Part.Face(startWires)
endFace = FreeCAD.Part.Face(endWires)
startFace = FreeCADPart.Face(startWires)
endFace = FreeCADPart.Face(endWires)
#collect all the faces from the sides
faceList = [ startFace]
@ -692,8 +694,8 @@ class Solid(Shape):
faceList.extend(s.Faces)
faceList.append(endFace)
shell = FreeCAD.Part.makeShell(faceList)
solid = FreeCAD.Part.makeSolid(shell)
shell = FreeCADPart.makeShell(faceList)
solid = FreeCADPart.makeSolid(shell)
return Shape.cast(solid)
@classmethod
@ -730,7 +732,7 @@ class Solid(Shape):
for w in innerWires:
freeCADWires.append(w.wrapped)
f = FreeCAD.Part.Face(freeCADWires)
f = FreeCADPart.Face(freeCADWires)
result = f.extrude(vecNormal.wrapped)
return Shape.cast(result)
@ -794,11 +796,11 @@ class Compound(Shape):
Create a compound out of a list of shapes
"""
solids = [s.wrapped for s in listOfShapes]
c = FreeCAD.Part.Compound(solids)
c = FreeCADPart.Compound(solids)
return Shape.cast( c)
def fuse(self,toJoin):
return Shape.cast(self.wrapped.fuse(toJoin.wrapped))
def tessellate(self,tolerance):
return self.wrapped.tessellate(tolerance)
return self.wrapped.tessellate(tolerance)

View File

@ -0,0 +1,113 @@
"""
This file is part of CadQuery.
CadQuery is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
CadQuery is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; If not, see <http://www.gnu.org/licenses/>
An exporter should provide functionality to accept a shape, and return
a string containing the model content.
"""
import re
from importlib import import_module
import os
import sys
MEMO_VERSION = None
SUBMODULES = None
_PATH = None
def _figure_out_version(freecadversion):
"""Break this out for testability."""
return tuple(
((int(re.sub("([0-9]*).*", "\\1", part) or 0))
for part in freecadversion[:3]))
def _fc_path():
"""Find FreeCAD"""
global _PATH
if _PATH:
return _PATH
if sys.platform.startswith('linux'):
#Make some dangerous assumptions...
for _PATH in [
os.path.join(os.path.expanduser("~"), "lib/freecad/lib"),
"/usr/local/lib/freecad/lib",
"/usr/lib/freecad/lib",
]:
if os.path.exists(_PATH):
return _PATH
elif sys.platform.startswith('win'):
for _PATH in [
"c:/apps/FreeCAD0.12/bin",
"c:/apps/FreeCAD0.13/bin",
]:
if os.path.exists(_PATH):
return _PATH
def freecad_version():
"""Determine the freecad version and return it as a simple
comparable tuple"""
#If we cannot find freecad, we append it to the path if possible
_pthtmp = _fc_path()
if not _pthtmp in sys.path:
sys.path.append(_pthtmp)
import FreeCAD
global MEMO_VERSION
if not MEMO_VERSION:
MEMO_VERSION = _figure_out_version(FreeCAD.Version())
return MEMO_VERSION
def _find_submodules():
"""Find the list of allowable submodules in fc13"""
global SUBMODULES
searchpath = _fc_path()
if not SUBMODULES:
SUBMODULES = [
re.sub("(.*)\\.(py|so)","\\1", filename)
for filename in os.listdir(searchpath)
if (
filename.endswith(".so") or
filename.endswith(".py") or
filename.endswith(".dll") )] #Yes, complex. Sorry.
return SUBMODULES
def fc_import(modulename):
"""Intelligent import of freecad components.
If we are in 0.12, we can import FreeCAD.Drawing
If we are in 0.13, we need to set sys.path and import Drawing as toplevel.
This may or may not be a FreeCAD bug though.
This is ludicrously complex and feels awful. Kinda like a lot of OCC.
"""
#Note that this also sets the path as a side effect.
_fcver = freecad_version()
if _fcver >= (0, 13):
if modulename in _find_submodules():
return import_module(modulename)
elif re.sub("^FreeCAD\\.", "", modulename) in _find_submodules():
return import_module(re.sub("^FreeCAD\\.", "", modulename))
else:
raise ImportError, "Module %s not found/allowed in %s" % (
modulename, _PATH)
elif _fcver >= (0, 12):
return import_module(modulename)
else:
raise RuntimeError, "Invalid freecad version: %s" % \
str(".".join(_fcver))
__ALL__ = ['fc_import', 'freecad_version']

View File

@ -1,4 +1,4 @@
from distutils.core import setup
from setuptools import setup
setup(
name='cadquery',
@ -37,4 +37,4 @@ setup(
'Topic :: Internet',
'Topic :: Scientific/Engineering'
]
)
)

View File

@ -3,9 +3,15 @@ import sys
import unittest
from tests import BaseTest
import FreeCAD
from cadquery.freecad_impl.verutil import fc_import
FreeCAD = fc_import("FreeCAD")
if not hasattr(FreeCAD, 'Part'):
FreeCAD.Part = fc_import("FreeCAD.Part")
from cadquery import *
class TestCadObjects(BaseTest):
def testVectorConstructors(self):
@ -57,6 +63,6 @@ class TestCadObjects(BaseTest):
def testVertices(self):
e = Shape.cast(FreeCAD.Part.makeLine((0,0,0),(1,1,0)))
self.assertEquals(2,len(e.Vertices()))
if __name__ == '__main__':
unittest.main()
unittest.main()

View File

@ -11,7 +11,11 @@ from cadquery import exporters
from tests import BaseTest,writeStringToFile,makeUnitCube,readFileAsString,makeUnitSquareWire,makeCube
#where unit test output will be saved
OUTDIR = "c:/temp"
import sys
if sys.platform.startswith("win"):
OUTDIR = "c:/temp"
else:
OUTDIR = "/tmp"
SUMMARY_FILE = os.path.join(OUTDIR,"testSummary.html")
SUMMARY_TEMPLATE="""<html>
@ -422,13 +426,14 @@ class TestCadQuery(BaseTest):
def testBasicLines(self):
"Make a triangluar boss"
global OUTDIR
s = Workplane(Plane.XY())
#TODO: extrude() should imply wire() if not done already
#most users dont understand what a wire is, they are just drawing
r = s.lineTo(1.0,0).lineTo(0,1.0).close().wire().extrude(0.25)
r.val().exportStep('c:/temp/testBasicLinesStep1.STEP')
r.val().exportStep(os.path.join(OUTDIR, 'testBasicLinesStep1.STEP'))
self.assertEqual(0,s.faces().size()) #no faces on the original workplane
self.assertEqual(5,r.faces().size() ) # 5 faces on newly created object
@ -436,12 +441,12 @@ class TestCadQuery(BaseTest):
#now add a circle through a side face
r.faces("+XY").workplane().circle(0.08).cutThruAll()
self.assertEqual(6,r.faces().size())
r.val().exportStep('c:/temp/testBasicLinesXY.STEP')
r.val().exportStep(os.path.join(OUTDIR, 'testBasicLinesXY.STEP'))
#now add a circle through a top
r.faces("+Z").workplane().circle(0.08).cutThruAll()
self.assertEqual(9,r.faces().size())
r.val().exportStep('c:/temp/testBasicLinesZ.STEP')
r.val().exportStep(os.path.join(OUTDIR, 'testBasicLinesZ.STEP'))
self.saveModel(r)

30
tests/TestImports.py Normal file
View File

@ -0,0 +1,30 @@
"""
Tests basic workplane functionality
"""
#core modules
#my modules
from cadquery.freecad_impl import verutil
from tests import BaseTest
class TestVersionsForImport(BaseTest):
"""Test version checks."""
def test_013_version(self):
"""Make sure various 0.13 Version calls work correctly"""
self.assertEquals(verutil._figure_out_version(
['0', '13', '2055 (Git)',
'git://git.code.sf.net/p/free-cad/code',
'2013/04/18 13:48:49', 'master',
'3511a807a30cf41909aaf12a1efe1db6c53db577']),
(0,13,2055))
self.assertEquals(verutil._figure_out_version(
['0', '13', '12345']),
(0,13,12345))
self.assertEquals(verutil._figure_out_version(
['0', '13', 'SOMETAGTHATBREAKSSTUFF']),
(0,13,0))

View File

@ -1,12 +1,13 @@
from cadquery import *
import unittest
import sys
FREECAD_LIB = "c:/apps/FreeCAD0.12/bin";
sys.path.append(FREECAD_LIB);
import FreeCAD
import os
P = FreeCAD.Part
V = FreeCAD.Base.Vector
from cadquery.freecad_impl.verutil import fc_import
FreeCAD = fc_import("FreeCAD")
P = fc_import("FreeCAD.Part")
V = fc_import("FreeCAD").Base.Vector
def readFileAsString(fileName):
f= open(fileName,'r')
@ -18,7 +19,7 @@ def writeStringToFile(strToWrite,fileName):
f = open(fileName,'w')
f.write(strToWrite)
f.close()
def makeUnitSquareWire():
return Solid.cast(P.makePolygon([V(0,0,0),V(1,0,0),V(1,1,0),V(0,1,0),V(0,0,0)]))