diff --git a/cadquery/freecad_impl/exporters.py b/cadquery/freecad_impl/exporters.py index 21768a7..a774a84 100644 --- a/cadquery/freecad_impl/exporters.py +++ b/cadquery/freecad_impl/exporters.py @@ -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 - + 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, diff --git a/cadquery/freecad_impl/geom.py b/cadquery/freecad_impl/geom.py index ae9ed51..a036d62 100644 --- a/cadquery/freecad_impl/geom.py +++ b/cadquery/freecad_impl/geom.py @@ -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) diff --git a/cadquery/freecad_impl/shapes.py b/cadquery/freecad_impl/shapes.py index bcfc4c1..0f97b41 100644 --- a/cadquery/freecad_impl/shapes.py +++ b/cadquery/freecad_impl/shapes.py @@ -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) \ No newline at end of file + return self.wrapped.tessellate(tolerance) diff --git a/cadquery/freecad_impl/verutil.py b/cadquery/freecad_impl/verutil.py new file mode 100644 index 0000000..9b09d3d --- /dev/null +++ b/cadquery/freecad_impl/verutil.py @@ -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 + + 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'] diff --git a/setup.py b/setup.py index 8e8c0da..9d212cb 100644 --- a/setup.py +++ b/setup.py @@ -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' ] -) \ No newline at end of file +) diff --git a/tests/TestCadObjects.py b/tests/TestCadObjects.py index d1c9d86..7cb0863 100644 --- a/tests/TestCadObjects.py +++ b/tests/TestCadObjects.py @@ -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() \ No newline at end of file + unittest.main() diff --git a/tests/TestCadQuery.py b/tests/TestCadQuery.py index 6407bd3..4c2c655 100644 --- a/tests/TestCadQuery.py +++ b/tests/TestCadQuery.py @@ -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=""" @@ -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) diff --git a/tests/TestImports.py b/tests/TestImports.py new file mode 100644 index 0000000..2c7d04f --- /dev/null +++ b/tests/TestImports.py @@ -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)) + + + + diff --git a/tests/__init__.py b/tests/__init__.py index 7c6e821..21b9646 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -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)]))