added export test cases
This commit is contained in:
parent
1848b1226a
commit
59d2af7a6c
|
@ -834,18 +834,26 @@ class Workplane(CQ):
|
|||
self.parent = None
|
||||
self.ctx = CQContext()
|
||||
|
||||
def transformed(self,rotate=Vector(0,0,0),offset=Vector(0,0,0)):
|
||||
def transformed(self,rotate=(0,0,0),offset=(0,0,0)):
|
||||
"""
|
||||
Create a new workplane based on the current one.
|
||||
The origin of the new plane is located at the existing origin+offset vector, where offset is given in
|
||||
coordinates local to the current plane
|
||||
The new plane is rotated through the angles specified by the components of the rotation vector
|
||||
:param rotate: vector of angles to rotate, in degrees relative to work plane coordinates
|
||||
:param offset: vector to offset the new plane, in local work plane coordinates
|
||||
:param rotate: 3-tuple of angles to rotate, in degrees relative to work plane coordinates
|
||||
:param offset: 3-tuple to offset the new plane, in local work plane coordinates
|
||||
:return: a new work plane, transformed as requested
|
||||
"""
|
||||
|
||||
#old api accepted a vector, so we'll check for that.
|
||||
if rotate.__class__.__name__ == 'Vector':
|
||||
rotate = rotate.toTuple()
|
||||
|
||||
if offset.__class__.__name__ == 'Vector':
|
||||
offset = offset.toTuple()
|
||||
|
||||
p = self.plane.rotated(rotate)
|
||||
p.setOrigin3d(self.plane.toWorldCoords(offset.toTuple() ))
|
||||
p.setOrigin3d(self.plane.toWorldCoords(offset ))
|
||||
ns = self.newObject([p.origin])
|
||||
ns.plane = p
|
||||
|
||||
|
@ -1219,35 +1227,20 @@ class Workplane(CQ):
|
|||
|
||||
"""
|
||||
|
||||
#compute rotation matrix ( global --> local --> rotate --> global )
|
||||
#rm = self.plane.fG.multiply(matrix).multiply(self.plane.rG)
|
||||
rm = self.plane.computeTransform(matrix)
|
||||
|
||||
#convert edges to a wire, if there are pending edges
|
||||
n = self.wire(forConstruction=False)
|
||||
|
||||
#attempt to consolidate wires together.
|
||||
consolidated = n.consolidateWires()
|
||||
|
||||
#ok, mirror all the wires.
|
||||
rotatedWires = self.plane.rotateShapes(consolidated.wires().vals(),matrix)
|
||||
|
||||
#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
|
||||
|
||||
#!!!TODO: needs refactoring. rm.wrapped is a hack, w.transformGeometry is needing a FreeCAD matrix,
|
||||
#so this code is dependent on a freecad matrix even when we dont explicitly import it.
|
||||
#
|
||||
originalWires = consolidated.wires().vals()
|
||||
for w in originalWires:
|
||||
mirrored = w.transformGeometry(rm.wrapped)
|
||||
consolidated.objects.append(mirrored)
|
||||
consolidated._addPendingWire(mirrored)
|
||||
for w in rotatedWires:
|
||||
consolidated.objects.append(w)
|
||||
consolidated._addPendingWire(w)
|
||||
|
||||
#attempt again to consolidate all of the wires
|
||||
c = consolidated.consolidateWires()
|
||||
#c = consolidated
|
||||
return c
|
||||
|
||||
def mirrorY(self):
|
||||
|
|
|
@ -15,9 +15,13 @@
|
|||
|
||||
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/>
|
||||
"""
|
||||
|
||||
import FreeCAD
|
||||
An exporter should provide functionality to accept a shape, and return
|
||||
a string containing the model content.
|
||||
"""
|
||||
import cadquery
|
||||
|
||||
import FreeCAD,tempfile,os
|
||||
from FreeCAD import Drawing
|
||||
|
||||
try:
|
||||
|
@ -25,17 +29,82 @@ try:
|
|||
except ImportError:
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
class ExportFormats:
|
||||
class ExportTypes:
|
||||
STL = "STL"
|
||||
BREP = "BREP"
|
||||
STEP = "STEP"
|
||||
AMF = "AMF"
|
||||
IGES = "IGES"
|
||||
SVG = "SVG"
|
||||
TJS = "TJS"
|
||||
|
||||
class UNITS:
|
||||
MM = "mm"
|
||||
IN = "in"
|
||||
|
||||
|
||||
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
|
||||
object, the first value is exported
|
||||
:param exportFormat: the exportFormat to use
|
||||
: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
|
||||
"""
|
||||
|
||||
if isinstance(shape,cadquery.CQ):
|
||||
shape = shape.val()
|
||||
|
||||
if exportType == ExportTypes.TJS:
|
||||
#tessellate the model
|
||||
tess = shape.tessellate(tolerance)
|
||||
|
||||
mesher = JsonMesh() #warning: needs to be changed to remove buildTime and exportTime!!!
|
||||
#add vertices
|
||||
for vec in tess[0]:
|
||||
mesher.addVertex(vec.x, vec.y, vec.z)
|
||||
|
||||
#add faces
|
||||
for f in tess[1]:
|
||||
mesher.addTriangleFace(f[0],f[1], f[2])
|
||||
fileLike.write( mesher.toJson())
|
||||
elif exportType == ExportTypes.SVG:
|
||||
fileLike.write(getSVG(shape.wrapped))
|
||||
elif exportType == ExportTypes.AMF:
|
||||
tess = shape.tessellate(tolerance)
|
||||
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()
|
||||
#weird, but we need to close this file. the next step is going to write to
|
||||
#it from c code, so it needs to be closed.
|
||||
os.close(h)
|
||||
|
||||
if exportType == ExportTypes.STEP:
|
||||
shape.exportStep(outFileName)
|
||||
elif exportType == ExportTypes.STL:
|
||||
shape.wrapped.exportStl(outFileName)
|
||||
else:
|
||||
raise ValueError("No idea how i got here")
|
||||
|
||||
res = readAndDeleteFile(outFileName)
|
||||
fileLike.write(res)
|
||||
|
||||
def readAndDeleteFile(fileName):
|
||||
"""
|
||||
read data from file provided, and delete it when done
|
||||
return the contents as a string
|
||||
"""
|
||||
res = ""
|
||||
with open(fileName,'r') as f:
|
||||
res = f.read()
|
||||
|
||||
os.remove(fileName)
|
||||
return res
|
||||
|
||||
|
||||
def guessUnitOfMeasure(shape):
|
||||
"""
|
||||
Guess the unit of measure of a shape.
|
||||
|
@ -58,13 +127,13 @@ def guessUnitOfMeasure(shape):
|
|||
return UNITS.MM
|
||||
|
||||
|
||||
class AmfExporter(object):
|
||||
class AmfWriter(object):
|
||||
def __init__(self,tessellation):
|
||||
|
||||
self.units = "mm"
|
||||
self.tessellation = tessellation
|
||||
|
||||
def writeAmf(self,outFileName):
|
||||
def writeAmf(self,outFile):
|
||||
amf = ET.Element('amf',units=self.units)
|
||||
#TODO: if result is a compound, we need to loop through them
|
||||
object = ET.SubElement(amf,'object',id="0")
|
||||
|
@ -94,14 +163,14 @@ class AmfExporter(object):
|
|||
v3.text = str(t[2])
|
||||
|
||||
|
||||
ET.ElementTree(amf).write(outFileName,encoding='ISO-8859-1')
|
||||
ET.ElementTree(amf).write(outFile,encoding='ISO-8859-1')
|
||||
|
||||
"""
|
||||
Objects that represent
|
||||
three.js JSON object notation
|
||||
https://github.com/mrdoob/three.js/wiki/JSON-Model-format-3.0
|
||||
"""
|
||||
class JsonExporter(object):
|
||||
class JsonMesh(object):
|
||||
def __init__(self):
|
||||
|
||||
self.vertices = [];
|
||||
|
|
|
@ -428,7 +428,7 @@ class Plane:
|
|||
return Vector(self.rG.multiply(v.wrapped))
|
||||
|
||||
|
||||
def rotated(self,rotate=Vector(0,0,0)):
|
||||
def rotated(self,rotate=(0,0,0)):
|
||||
"""
|
||||
returns a copy of this plane, rotated about the specified axes, as measured from horizontal
|
||||
|
||||
|
@ -440,10 +440,12 @@ class Plane:
|
|||
rotations are done in order x,y,z. if you need a different order, manually chain together multiple .rotate()
|
||||
commands
|
||||
|
||||
:param roate: Vector [xDegrees,yDegrees,zDegrees]
|
||||
:param rotate: Vector [xDegrees,yDegrees,zDegrees]
|
||||
:return: a copy of this plane rotated as requested
|
||||
"""
|
||||
|
||||
if rotate.__class__.__name__ != 'Vector':
|
||||
rotate = Vector(rotate)
|
||||
#convert to radians
|
||||
rotate = rotate.multiply(math.pi / 180.0 )
|
||||
|
||||
|
@ -460,6 +462,32 @@ class Plane:
|
|||
newP= Plane(self.origin,newXdir,newZdir)
|
||||
return newP
|
||||
|
||||
def rotateShapes(self,listOfShapes,rotationMatrix):
|
||||
"""
|
||||
rotate the listOfShapes by the rotationMatrix supplied.
|
||||
@param listOfShapes is a list of shape objects
|
||||
@param rotationMatrix is a geom.Matrix object.
|
||||
returns a list of shape objects rotated according to the rotationMatrix
|
||||
"""
|
||||
|
||||
#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)
|
||||
resultWires.append(mirrored)
|
||||
|
||||
return resultWires
|
||||
|
||||
|
||||
def _calcTransforms(self):
|
||||
"""
|
||||
Computes transformation martrices to convert betwene local and global coordinates
|
||||
|
|
|
@ -12,5 +12,5 @@ suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCadObjects.TestCa
|
|||
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestWorkplanes.TestWorkplanes))
|
||||
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCQSelectors.TestCQSelectors))
|
||||
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestCadQuery.TestCadQuery))
|
||||
|
||||
suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestExporters.TestExporters))
|
||||
unittest.TextTestRunner().run(suite)
|
|
@ -46,4 +46,4 @@ class BaseTest(unittest.TestCase):
|
|||
for i,j in zip(actual,expected):
|
||||
self.assertAlmostEquals(i,j,places)
|
||||
|
||||
__all__ = [ 'TestCadObjects','TestCadQuery','TestCQSelectors','TestWorkplanes']
|
||||
__all__ = [ 'TestCadObjects','TestCadQuery','TestCQSelectors','TestWorkplanes','TestExporters','TestCQSelectors']
|
||||
|
|
Loading…
Reference in New Issue
Block a user