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)]))