Files
cadquery/tests/test_exporters.py

316 lines
8.9 KiB
Python
Raw Normal View History

2013-04-20 20:33:35 -04:00
"""
Tests basic workplane functionality
"""
# core modules
2022-05-18 09:36:43 -04:00
import os
2018-02-02 16:34:37 +01:00
import io
from pathlib import Path
import re
import pytest
# my modules
2013-04-20 20:33:35 -04:00
from cadquery import *
2020-07-28 22:23:13 +02:00
from cadquery import exporters, importers
2013-04-20 20:33:35 -04:00
from tests import BaseTest
2021-08-31 08:58:51 +02:00
from OCP.GeomConvert import GeomConvert
from OCP.BRepBuilderAPI import BRepBuilderAPI_MakeEdge
2013-04-20 20:33:35 -04:00
@pytest.fixture(scope="module")
def tmpdir(tmp_path_factory):
return tmp_path_factory.mktemp("out")
@pytest.fixture()
def box123():
return Workplane().box(1, 2, 3)
def test_step_options(tmpdir):
2022-05-18 09:36:43 -04:00
"""
Exports a box using the options to decrease STEP file size and
then imports that STEP to validate it.
"""
# Use a temporary directory
box_path = os.path.join(tmpdir, "out.step")
# Simple object to export
box = Workplane().box(1, 1, 1)
# Export the STEP with the size-saving options and then import it back in
box.val().exportStep(box_path, write_pcurves=False, precision_mode=0)
w = importers.importStep(box_path)
# Make sure there was a valid box in the exported file
assert w.solids().size() == 1
assert w.faces().size() == 6
2013-04-20 20:33:35 -04:00
class TestExporters(BaseTest):
2020-08-07 08:50:53 +02:00
def _exportBox(self, eType, stringsToFind, tolerance=0.1, angularTolerance=0.1):
2013-04-20 20:33:35 -04:00
"""
Exports a test object, and then looks for
all of the supplied strings to be in the result
returns the result in case the case wants to do more checks also
2013-04-20 20:33:35 -04:00
"""
p = Workplane("XY").box(1, 2, 3)
2018-02-02 16:34:37 +01:00
if eType == exporters.ExportTypes.AMF:
2018-02-02 16:34:37 +01:00
s = io.BytesIO()
else:
s = io.StringIO()
2020-08-06 14:34:22 -07:00
exporters.exportShape(
2020-08-07 08:50:53 +02:00
p, eType, s, tolerance=tolerance, angularTolerance=angularTolerance
2020-08-06 14:34:22 -07:00
)
result = "{}".format(s.getvalue())
2013-04-20 20:33:35 -04:00
for q in stringsToFind:
self.assertTrue(result.find(q) > -1)
2013-04-20 20:33:35 -04:00
return result
2020-07-29 08:52:41 +02:00
def _box(self):
return Workplane().box(1, 1, 1)
2013-04-20 20:33:35 -04:00
def testSVG(self):
self._exportBox(exporters.ExportTypes.SVG, ["<svg", "<g transform"])
2013-04-20 20:33:35 -04:00
2020-07-29 08:52:41 +02:00
exporters.export(self._box(), "out.svg")
def testSVGOptions(self):
self._exportBox(exporters.ExportTypes.SVG, ["<svg", "<g transform"])
exporters.export(
self._box(),
"out.svg",
opt={
"width": 100,
"height": 100,
"marginLeft": 10,
"marginTop": 10,
"showAxes": False,
"projectionDir": (0, 0, 1),
"strokeWidth": 0.25,
"strokeColor": (255, 0, 0),
"hiddenColor": (0, 0, 255),
"showHidden": True,
},
)
2013-04-20 20:33:35 -04:00
def testAMF(self):
self._exportBox(exporters.ExportTypes.AMF, ["<amf units", "</object>"])
2013-04-20 20:33:35 -04:00
2020-07-29 08:52:41 +02:00
exporters.export(self._box(), "out.amf")
2013-04-20 20:33:35 -04:00
def testSTEP(self):
self._exportBox(exporters.ExportTypes.STEP, ["FILE_SCHEMA"])
2013-04-20 20:33:35 -04:00
2020-07-29 08:52:41 +02:00
exporters.export(self._box(), "out.step")
2013-04-20 20:33:35 -04:00
def testTJS(self):
self._exportBox(
exporters.ExportTypes.TJS, ["vertices", "formatVersion", "faces"]
)
2020-07-28 22:23:13 +02:00
2020-07-29 08:52:41 +02:00
exporters.export(self._box(), "out.tjs")
2020-10-14 19:07:47 +02:00
def testVRML(self):
exporters.export(self._box(), "out.vrml")
with open("out.vrml") as f:
res = f.read(10)
assert res.startswith("#VRML V2.0")
2020-11-19 08:22:35 +01:00
# export again to trigger all paths in the code
exporters.export(self._box(), "out.vrml")
def testVTP(self):
exporters.export(self._box(), "out.vtp")
with open("out.vtp") as f:
res = f.read(100)
assert res.startswith('<?xml version="1.0"?>\n<VTKFile')
2020-07-28 22:23:13 +02:00
def testDXF(self):
2020-07-29 08:52:41 +02:00
exporters.export(self._box().section(), "out.dxf")
with self.assertRaises(ValueError):
exporters.export(self._box().val(), "out.dxf")
2020-07-28 22:23:13 +02:00
s1 = (
Workplane("XZ")
.polygon(10, 10)
.ellipse(1, 2)
.extrude(1)
.edges("|Y")
.fillet(1)
.section()
)
exporters.dxf.exportDXF(s1, "res1.dxf")
s1_i = importers.importDXF("res1.dxf")
self.assertAlmostEqual(s1.val().Area(), s1_i.val().Area(), 6)
self.assertAlmostEqual(s1.edges().size(), s1_i.edges().size())
2020-07-28 22:23:13 +02:00
pts = [(0, 0), (0, 0.5), (1, 1)]
s2 = (
Workplane().spline(pts).close().extrude(1).edges("|Z").fillet(0.1).section()
)
exporters.dxf.exportDXF(s2, "res2.dxf")
s2_i = importers.importDXF("res2.dxf")
self.assertAlmostEqual(s2.val().Area(), s2_i.val().Area(), 6)
self.assertAlmostEqual(s2.edges().size(), s2_i.edges().size())
2020-07-28 22:23:13 +02:00
s3 = (
Workplane("XY")
.ellipseArc(1, 2, 0, 180)
.close()
.extrude(1)
.edges("|Z")
.fillet(0.1)
.section()
)
exporters.dxf.exportDXF(s3, "res3.dxf")
s3_i = importers.importDXF("res3.dxf")
self.assertAlmostEqual(s3.val().Area(), s3_i.val().Area(), 6)
self.assertAlmostEqual(s3.edges().size(), s3_i.edges().size())
2020-07-29 08:52:41 +02:00
cyl = Workplane("XY").circle(22).extrude(10, both=True).translate((-50, 0, 0))
s4 = Workplane("XY").box(80, 60, 5).cut(cyl).section()
exporters.dxf.exportDXF(s4, "res4.dxf")
s4_i = importers.importDXF("res4.dxf")
self.assertAlmostEqual(s4.val().Area(), s4_i.val().Area(), 6)
self.assertAlmostEqual(s4.edges().size(), s4_i.edges().size())
2021-08-31 08:58:51 +02:00
# test periodic spline
2021-08-31 18:35:01 +02:00
w = Workplane().spline([(1, 1), (2, 2), (3, 2), (3, 1)], periodic=True)
exporters.dxf.exportDXF(w, "res5.dxf")
2021-08-31 08:58:51 +02:00
2021-08-31 18:35:01 +02:00
w_i = importers.importDXF("res5.dxf")
2021-08-31 08:58:51 +02:00
2021-08-31 18:35:01 +02:00
self.assertAlmostEqual(w.val().Length(), w_i.wires().val().Length(), 6)
2021-08-31 08:58:51 +02:00
# test rational spline
c = Edge.makeCircle(1)
adaptor = c._geomAdaptor()
curve = GeomConvert.CurveToBSplineCurve_s(adaptor.Curve().Curve())
e = Workplane().add(Edge(BRepBuilderAPI_MakeEdge(curve).Shape()))
2021-08-31 18:35:01 +02:00
exporters.dxf.exportDXF(e, "res6.dxf")
e_i = importers.importDXF("res6.dxf")
self.assertAlmostEqual(e.val().Length(), e_i.wires().val().Length(), 6)
# test non-planar section
s5 = (
Workplane()
.spline([(0, 0), (1, 0), (1, 1), (0, 1)])
.close()
.extrude(1, both=True)
.translate((-3, -4, 0))
)
s5.plane = Plane(origin=(0, 0.1, 0.5), normal=(0.05, 0.05, 1))
s5 = s5.section()
exporters.dxf.exportDXF(s5, "res7.dxf")
2021-08-31 08:58:51 +02:00
2021-08-31 18:35:01 +02:00
s5_i = importers.importDXF("res7.dxf")
2021-08-31 08:58:51 +02:00
2021-08-31 18:35:01 +02:00
self.assertAlmostEqual(s5.val().Area(), s5_i.val().Area(), 4)
2021-08-31 08:58:51 +02:00
2020-07-29 08:52:41 +02:00
def testTypeHandling(self):
with self.assertRaises(ValueError):
exporters.export(self._box(), "out.random")
with self.assertRaises(ValueError):
exporters.export(self._box(), "out.stl", "STP")
@pytest.mark.parametrize(
"id, opt, matchval",
[
(0, None, b"solid \n facet normal"),
(1, {"unknown_opt": 1}, b"solid \n facet normal"),
(2, {"unknown_opt": 1, "ascii": True}, b"solid \n facet normal"),
(3, {"ascii": True}, b"solid \n facet normal"),
(4, {"ascii": False}, b"STL Exported by OpenCASCADE"),
(5, {"ASCII": False}, b"STL Exported by OpenCASCADE"),
(6, {"ASCII": False, "ascii": True}, b"STL Exported by OpenCASCADE"),
(7, {"unknown_opt": 1, "ascii": False}, b"STL Exported by OpenCASCADE"),
],
)
def test_stl(tmpdir, box123, id, opt, matchval):
"""
:param tmpdir: temporary directory fixutre
:param box123: box fixture
:param id: The index or id; output filename is <test name>_<id>.stl
:param opt: The export opt dict
:param matchval: Check that the file starts with the specified value
"""
fpath = tmpdir.joinpath(f"stl_{id}.stl").resolve()
assert not fpath.exists()
# matchval must be specified
assert matchval
exporters.export(box123, str(fpath), None, 0.1, 0.1, opt)
with open(fpath, "rb") as f:
r = f.read(len(matchval))
assert r == matchval
def test_stl_simple(tmpdir, box123):
fpath = tmpdir.joinpath("stl_simple.stl").resolve()
assert not fpath.exists()
matchval = b"solid \n facet normal"
exporters.export(box123, str(fpath))
with open(fpath, "rb") as f:
r = f.read(len(matchval))
assert r == matchval
def test_assy_vtk_rotation(tmpdir):
v0 = Vertex.makeVertex(1, 0, 0)
assy = Assembly()
assy.add(
v0, name="v0", loc=Location(Vector(0, 0, 0), Vector(1, 0, 0), 90),
)
fwrl = Path(tmpdir, "v0.wrl")
assert not fwrl.exists()
assy.save(str(fwrl), "VRML")
assert fwrl.exists()
matched_rot = False
with open(fwrl) as f:
pat_rot = re.compile("""rotation 1 0 0 1.5707963267""")
for line in f:
if m := re.search(pat_rot, line):
matched_rot = True
break
assert matched_rot