DXF multilayer support (#1267)

* Test DXF export with text

* DXF multilayer support

Also supports:

* setting units
* setting color by layer
* setting line type by layer

* Apply transformation to bspline curve on DXF export

* Typing improvements

* Improve test coverage

---------

Co-authored-by: Lorenz Neureuter <hello@lorenz.space>
This commit is contained in:
Seth Fischer
2023-03-16 19:39:13 +13:00
committed by GitHub
parent 44b769a436
commit 2d35517787
7 changed files with 631 additions and 111 deletions

View File

@ -15,7 +15,7 @@ from .svg import getSVG
from .json import JsonMesh
from .amf import AmfWriter
from .threemf import ThreeMFWriter
from .dxf import exportDXF
from .dxf import exportDXF, DxfDocument
from .vtk import exportVTP
from .utils import toCompound

View File

@ -1,56 +1,246 @@
from ...cq import Workplane, Plane, Face
"""DXF export utilities."""
from typing import Any, Dict, List, Literal, Optional, Tuple, Union
import ezdxf
from ezdxf import units, zoom
from ezdxf.entities import factory
from OCP.GeomConvert import GeomConvert
from OCP.gp import gp_Dir
from typing_extensions import Self
from ...cq import Face, Plane, Workplane
from ...units import RAD2DEG
from ..shapes import Edge
from .utils import toCompound
from OCP.gp import gp_Dir
from OCP.GeomConvert import GeomConvert
from typing import Optional, Literal
import ezdxf
CURVE_TOLERANCE = 1e-9
ApproxOptions = Literal["spline", "arc"]
DxfEntityAttributes = Tuple[
Literal["ARC", "CIRCLE", "ELLIPSE", "LINE", "SPLINE",], Dict[str, Any]
]
def _dxf_line(e: Edge, msp: ezdxf.layouts.Modelspace, plane: Plane):
class DxfDocument:
"""Create DXF document from CadQuery objects.
msp.add_line(
e.startPoint().toTuple(), e.endPoint().toTuple(),
A wrapper for `ezdxf <https://ezdxf.readthedocs.io/>`_ providing methods for
converting :class:`cadquery.Workplane` objects to DXF entities.
The ezdxf document is available as the property ``document``, allowing most
features of ezdxf to be utilised directly.
.. rubric:: Example usage
.. code-block:: python
:caption: Single layer DXF document
rectangle = cq.Workplane().rect(10, 20)
dxf = DxfDocument()
dxf.add_shape(rectangle)
dxf.document.saveas("rectangle.dxf")
.. code-block:: python
:caption: Multilayer DXF document
rectangle = cq.Workplane().rect(10, 20)
circle = cq.Workplane().circle(3)
dxf = DxfDocument()
dxf = (
dxf.add_layer("layer_1", color=2)
.add_layer("layer_2", color=3)
.add_shape(rectangle, "layer_1")
.add_shape(circle, "layer_2")
)
dxf.document.saveas("rectangle-with-hole.dxf")
"""
CURVE_TOLERANCE = 1e-9
def __init__(
self,
dxfversion: str = "AC1027",
setup: Union[bool, List[str]] = False,
doc_units: int = units.MM,
*,
metadata: Union[Dict[str, str], None] = None,
approx: Optional[ApproxOptions] = None,
tolerance: float = 1e-3,
):
"""Initialize DXF document.
:param dxfversion: :attr:`DXF version specifier <ezdxf-stable:ezdxf.document.Drawing.dxfversion>`
as string, default is "AC1027" respectively "R2013"
:param setup: setup default styles, ``False`` for no setup, ``True`` to set up
everything or a list of topics as strings, e.g. ``["linetypes", "styles"]``
refer to :func:`ezdxf-stable:ezdxf.new`.
:param doc_units: ezdxf document/modelspace :doc:`units <ezdxf-stable:concepts/units>`
:param metadata: document :ref:`metadata <ezdxf-stable:ezdxf_metadata>` a dictionary of name value pairs
:param approx: Approximation strategy for converting :class:`cadquery.Workplane` objects to DXF entities:
``None``
no approximation applied
``"spline"``
all splines approximated as cubic splines
``"arc"``
all curves approximated as arcs and straight segments
:param tolerance: Approximation tolerance for converting :class:`cadquery.Workplane` objects to DXF entities.
"""
if metadata is None:
metadata = {}
self._DISPATCH_MAP = {
"LINE": self._dxf_line,
"CIRCLE": self._dxf_circle,
"ELLIPSE": self._dxf_ellipse,
}
self.approx = approx
self.tolerance = tolerance
self.document = ezdxf.new(dxfversion=dxfversion, setup=setup, units=doc_units) # type: ignore[attr-defined]
self.msp = self.document.modelspace()
doc_metadata = self.document.ezdxf_metadata()
for key, value in metadata.items():
doc_metadata[key] = value
def add_layer(
self, name: str, *, color: int = 7, linetype: str = "CONTINUOUS"
) -> Self:
"""Create a layer definition
Refer to :ref:`ezdxf layers <ezdxf-stable:layer_concept>` and
:doc:`ezdxf layer tutorial <ezdxf-stable:tutorials/layers>`.
:param name: layer definition name
:param color: color index. Standard colors include:
1 red, 2 yellow, 3 green, 4 cyan, 5 blue, 6 magenta, 7 white/black
:param linetype: ezdxf :doc:`line type <ezdxf-stable:concepts/linetypes>`
"""
self.document.layers.add(name, color=color, linetype=linetype)
return self
def add_shape(self, workplane: Workplane, layer: str = "") -> Self:
"""Add CadQuery shape to a DXF layer.
:param workplane: CadQuery Workplane
:param layer: layer definition name
"""
plane = workplane.plane
shape = toCompound(workplane).transformShape(plane.fG)
general_attributes = {}
if layer:
general_attributes["layer"] = layer
if self.approx == "spline":
edges = [
e.toSplines() if e.geomType() == "BSPLINE" else e for e in shape.Edges()
]
elif self.approx == "arc":
edges = []
# this is needed to handle free wires
for el in shape.Wires():
edges.extend(Face.makeFromWires(el).toArcs(self.tolerance).Edges())
else:
edges = shape.Edges()
for edge in edges:
converter = self._DISPATCH_MAP.get(edge.geomType(), None)
if converter:
entity_type, entity_attributes = converter(edge)
entity = factory.new(
entity_type, dxfattribs={**entity_attributes, **general_attributes}
)
self.msp.add_entity(entity) # type: ignore[arg-type]
else:
_, entity_attributes = self._dxf_spline(edge, plane)
entity = ezdxf.math.BSpline(**entity_attributes) # type: ignore[assignment]
self.msp.add_spline(
dxfattribs=general_attributes
).apply_construction_tool(entity)
zoom.extents(self.msp)
return self
@staticmethod
def _dxf_line(edge: Edge) -> DxfEntityAttributes:
"""Convert a Line to DXF entity attributes.
:param edge: CadQuery Edge to be converted to a DXF line
:return: dictionary of DXF entity attributes for creating a line
"""
return (
"LINE",
{"start": edge.startPoint().toTuple(), "end": edge.endPoint().toTuple(),},
)
@staticmethod
def _dxf_circle(edge: Edge) -> DxfEntityAttributes:
"""Convert a Circle to DXF entity attributes.
def _dxf_circle(e: Edge, msp: ezdxf.layouts.Modelspace, plane: Plane):
:param edge: CadQuery Edge to be converted to a DXF circle
geom = e._geomAdaptor()
:return: dictionary of DXF entity attributes for creating either a circle or arc
"""
geom = edge._geomAdaptor()
circ = geom.Circle()
r = circ.Radius()
c = circ.Location()
radius = circ.Radius()
location = circ.Location()
c_dy = circ.YAxis().Direction()
c_dz = circ.Axis().Direction()
direction_y = circ.YAxis().Direction()
direction_z = circ.Axis().Direction()
dy = gp_Dir(0, 1, 0)
phi = c_dy.AngleWithRef(dy, c_dz)
phi = direction_y.AngleWithRef(dy, direction_z)
if c_dz.XYZ().Z() > 0:
if direction_z.XYZ().Z() > 0:
a1 = RAD2DEG * (geom.FirstParameter() - phi)
a2 = RAD2DEG * (geom.LastParameter() - phi)
else:
a1 = -RAD2DEG * (geom.LastParameter() - phi) + 180
a2 = -RAD2DEG * (geom.FirstParameter() - phi) + 180
if e.IsClosed():
msp.add_circle((c.X(), c.Y(), c.Z()), r)
if edge.IsClosed():
return (
"CIRCLE",
{
"center": (location.X(), location.Y(), location.Z()),
"radius": radius,
},
)
else:
msp.add_arc((c.X(), c.Y(), c.Z()), r, a1, a2)
return (
"ARC",
{
"center": (location.X(), location.Y(), location.Z()),
"radius": radius,
"start_angle": a1,
"end_angle": a2,
},
)
@staticmethod
def _dxf_ellipse(edge: Edge) -> DxfEntityAttributes:
"""Convert an Ellipse to DXF entity attributes.
def _dxf_ellipse(e: Edge, msp: ezdxf.layouts.Modelspace, plane: Plane):
:param edge: CadQuery Edge to be converted to a DXF ellipse
geom = e._geomAdaptor()
:return: dictionary of DXF entity attributes for creating an ellipse
"""
geom = edge._geomAdaptor()
ellipse = geom.Ellipse()
r1 = ellipse.MinorRadius()
@ -60,26 +250,38 @@ def _dxf_ellipse(e: Edge, msp: ezdxf.layouts.Modelspace, plane: Plane):
xdir = ellipse.XAxis().Direction()
xax = r2 * xdir.XYZ()
msp.add_ellipse(
(c.X(), c.Y(), c.Z()),
(xax.X(), xax.Y(), xax.Z()),
r1 / r2,
geom.FirstParameter(),
geom.LastParameter(),
return (
"ELLIPSE",
{
"center": (c.X(), c.Y(), c.Z()),
"major_axis": (xax.X(), xax.Y(), xax.Z()),
"ratio": r1 / r2,
"start_param": geom.FirstParameter(),
"end_param": geom.LastParameter(),
},
)
@classmethod
def _dxf_spline(cls, edge: Edge, plane: Plane) -> DxfEntityAttributes:
"""Convert a Spline to ezdxf.math.BSpline parameters.
def _dxf_spline(e: Edge, msp: ezdxf.layouts.Modelspace, plane: Plane):
:param edge: CadQuery Edge to be converted to a DXF spline
:param plane: CadQuery Plane
adaptor = e._geomAdaptor()
:return: dictionary of ezdxf.math.BSpline parameters
"""
adaptor = edge._geomAdaptor()
curve = GeomConvert.CurveToBSplineCurve_s(adaptor.Curve().Curve())
spline = GeomConvert.SplitBSplineCurve_s(
curve, adaptor.FirstParameter(), adaptor.LastParameter(), CURVE_TOLERANCE
curve,
adaptor.FirstParameter(),
adaptor.LastParameter(),
cls.CURVE_TOLERANCE,
)
# need to apply the transform on the geometry level
spline.Transform(plane.fG.wrapped.Trsf())
spline.Transform(adaptor.Trsf())
order = spline.Degree() + 1
knots = list(spline.KnotSequence())
@ -94,25 +296,25 @@ def _dxf_spline(e: Edge, msp: ezdxf.layouts.Modelspace, plane: Plane):
pad = spline.NbKnots() - spline.LastUKnotIndex()
poles += poles[:pad]
dxf_spline = ezdxf.math.BSpline(poles, order, knots, weights)
msp.add_spline().apply_construction_tool(dxf_spline)
DXF_CONVERTERS = {
"LINE": _dxf_line,
"CIRCLE": _dxf_circle,
"ELLIPSE": _dxf_ellipse,
"BSPLINE": _dxf_spline,
}
return (
"SPLINE",
{
"control_points": poles,
"order": order,
"knots": knots,
"weights": weights,
},
)
def exportDXF(
w: Workplane,
fname: str,
approx: Optional[Literal["spline", "arc"]] = None,
approx: Optional[ApproxOptions] = None,
tolerance: float = 1e-3,
):
*,
doc_units: int = units.MM,
) -> None:
"""
Export Workplane content to DXF. Works with 2D sections.
@ -122,33 +324,9 @@ def exportDXF(
"spline" results in all splines being approximated as cubic splines. "arc" results
in all curves being approximated as arcs and straight segments.
:param tolerance: Approximation tolerance.
:param doc_units: ezdxf document/modelspace :doc:`units <ezdxf-stable:concepts/units>` (in. = ``1``, mm = ``4``).
"""
plane = w.plane
shape = toCompound(w).transformShape(plane.fG)
dxf = ezdxf.new()
msp = dxf.modelspace()
if approx == "spline":
edges = [
e.toSplines() if e.geomType() == "BSPLINE" else e for e in shape.Edges()
]
elif approx == "arc":
edges = []
# this is needed to handle free wires
for el in shape.Wires():
edges.extend(Face.makeFromWires(el).toArcs(tolerance).Edges())
else:
edges = shape.Edges()
for e in edges:
conv = DXF_CONVERTERS.get(e.geomType(), _dxf_spline)
conv(e, msp, plane)
dxf.saveas(fname)
dxf = DxfDocument(approx=approx, tolerance=tolerance, doc_units=doc_units)
dxf.add_shape(w)
dxf.document.saveas(fname)

View File

@ -206,6 +206,7 @@ File Management and Export
importers.importStep
importers.importDXF
exporters.export
occ_impl.exporters.dxf.DxfDocument
Iteration Methods

View File

@ -101,3 +101,8 @@ Class Details
:members:
.. autofunction:: cadquery.occ_impl.assembly.toJSON
.. autoclass:: cadquery.occ_impl.exporters.dxf.DxfDocument
:members:
.. automethod:: __init__

View File

@ -38,6 +38,7 @@ extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.viewcode",
"sphinx.ext.autosummary",
"sphinx.ext.intersphinx",
"cadquery.cq_directive",
"sphinx.ext.mathjax",
"sphinx_autodoc_multimethod",
@ -212,6 +213,11 @@ html_show_sphinx = False
# Output file base name for HTML help builder.
htmlhelp_basename = "CadQuerydoc"
# -- Options for intersphinx --------------------------------------------------
intersphinx_mapping = {
"ezdxf-stable": ("https://ezdxf.readthedocs.io/en/stable/", None),
}
# -- Options for LaTeX output --------------------------------------------------

View File

@ -239,17 +239,99 @@ optimum values that will produce an acceptable mesh.
Exporting DXF
##############
.. seealso::
:class:`cadquery.occ_impl.exporters.dxf.DxfDocument` for exporting multiple
Workplanes to one or many layers of a DXF document.
Options
-------
``approx``
Approximation strategy for converting :class:`cadquery.Workplane` objects to DXF entities:
``None``
no approximation applied
``"spline"``
all splines approximated as cubic splines
``"arc"``
all curves approximated as arcs and straight segments
``tolerance``
Approximation tolerance for converting :class:`cadquery.Workplane` objects to DXF entities.
See `Approximation strategy`_.
``doc_units``
Ezdxf document/modelspace :doc:`units <ezdxf-stable:concepts/units>`.
See `Units`_.
.. code-block:: python
:caption: DXF document without options.
import cadquery as cq
from cadquery import exporters
result = cq.Workplane().box(10, 10, 10)
exporters.exportDXF(result, "/path/to/file/object.dxf")
# or
exporters.export(result, "/path/to/file/object.dxf")
Units
-----
The default DXF document units are mm (:code:`doc_units = 4`).
========= ===============
doc_units Unit
========= ===============
0 Unitless
1 Inches
2 Feet
3 Miles
4 Millimeters
5 Centimeters
6 Meters
========= ===============
Document units can be set to any :doc:`unit supported by ezdxf <ezdxf-stable:concepts/units>`.
.. code-block:: python
:caption: DXF document with units set to meters.
import cadquery as cq
from cadquery import exporters
result = cq.Workplane().box(10, 10, 10)
exporters.exportDXF(
result, "/path/to/file/object.dxf", doc_units=6, # set DXF document units to meters
)
# or
exporters.export(
result,
"/path/to/file/object.dxf",
opt={"doc_units": 6}, # set DXF document units to meters
)
.. _Approximation strategy:
Approximation strategy
----------------------
By default, the DXF exporter will output splines exactly as they are represented by the OpenCascade kernel. Unfortunately some software cannot handle higher-order splines resulting in missing curves after DXF import. To resolve this, specify an approximation strategy controlled by the following options:
* ``approx`` - ``None``, ``"spline"`` or ``"arc"``. ``"spline"`` results in all splines approximated with cubic splines. ``"arc"`` results in all curves approximated with arcs and line segments.
* ``tolerance``: Acceptable error of the approximation, in the DXF's coordinate system. Defaults to 0.001 (1 thou for inch-scale drawings, 1 µm for mm-scale drawings).
* ``tolerance``: Acceptable error of the approximation, in document/modelspace units. Defaults to 0.001 (1 thou for inch-scale drawings, 1 µm for mm-scale drawings).
.. code-block:: python
:caption: DXF document with curves approximated with cubic splines.
cq.exporters.exportDXF(
result,
'/path/to/file/object.dxf',
"/path/to/file/object.dxf",
approx="spline"
)

View File

@ -1,5 +1,5 @@
"""
Tests basic workplane functionality
Tests exporters
"""
# core modules
import os
@ -16,6 +16,7 @@ from pytest import approx
from cadquery import (
exporters,
importers,
Sketch,
Workplane,
Edge,
Vertex,
@ -24,6 +25,8 @@ from cadquery import (
Location,
Vector,
)
from cadquery.occ_impl.exporters.dxf import DxfDocument
from cadquery.occ_impl.exporters.utils import toCompound
from tests import BaseTest
from OCP.GeomConvert import GeomConvert
from OCP.BRepBuilderAPI import BRepBuilderAPI_MakeEdge
@ -34,6 +37,11 @@ def tmpdir(tmp_path_factory):
return tmp_path_factory.mktemp("out")
@pytest.fixture(scope="module")
def testdatadir():
return Path(__file__).parent.joinpath("testdata")
@pytest.fixture()
def box123():
return Workplane().box(1, 2, 3)
@ -59,6 +67,223 @@ def test_step_options(tmpdir):
assert w.faces().size() == 6
class TestDxfDocument(BaseTest):
"""Test class DxfDocument."""
def test_line(self):
workplane = Workplane().line(1, 1)
plane = workplane.plane
shape = toCompound(workplane).transformShape(plane.fG)
edges = shape.Edges()
result = DxfDocument._dxf_line(edges[0])
expected = ("LINE", {"start": (0.0, 0.0, 0.0), "end": (1.0, 1.0, 0.0)})
self.assertEqual(expected, result)
def test_circle(self):
workplane = Workplane().circle(1)
plane = workplane.plane
shape = toCompound(workplane).transformShape(plane.fG)
edges = shape.Edges()
result = DxfDocument._dxf_circle(edges[0])
expected = ("CIRCLE", {"center": (0.0, 0.0, 0.0), "radius": 1.0})
self.assertEqual(expected, result)
def test_arc(self):
workplane = Workplane().radiusArc((1, 1), 1)
plane = workplane.plane
shape = toCompound(workplane).transformShape(plane.fG)
edges = shape.Edges()
result_type, result_attributes = DxfDocument._dxf_circle(edges[0])
expected_type, expected_attributes = (
"ARC",
{"center": (1, 0, 0), "radius": 1, "start_angle": 90, "end_angle": 180,},
)
self.assertEqual(expected_type, result_type)
self.assertTupleAlmostEquals(
expected_attributes["center"], result_attributes["center"], 3
)
self.assertAlmostEqual(
expected_attributes["radius"], approx(result_attributes["radius"])
)
self.assertAlmostEqual(
expected_attributes["start_angle"], result_attributes["start_angle"]
)
self.assertAlmostEqual(
expected_attributes["end_angle"], result_attributes["end_angle"]
)
def test_ellipse(self):
workplane = Workplane().ellipse(2, 1, 0)
plane = workplane.plane
shape = toCompound(workplane).transformShape(plane.fG)
edges = shape.Edges()
result_type, result_attributes = DxfDocument._dxf_ellipse(edges[0])
expected_type, expected_attributes = (
"ELLIPSE",
{
"center": (0, 0, 0),
"major_axis": (2.0, 0, 0),
"ratio": 0.5,
"start_param": 0,
"end_param": 6.283185307179586,
},
)
self.assertEqual(expected_type, result_type)
self.assertEqual(expected_attributes["center"], result_attributes["center"])
self.assertEqual(
expected_attributes["major_axis"], result_attributes["major_axis"]
)
self.assertEqual(expected_attributes["ratio"], result_attributes["ratio"])
self.assertEqual(
expected_attributes["start_param"], result_attributes["start_param"]
)
self.assertAlmostEqual(
expected_attributes["end_param"], result_attributes["end_param"]
)
def test_spline(self):
pts = [(0, 0), (0, 0.5), (1, 1)]
workplane = (
Workplane().spline(pts).close().extrude(1).edges("|Z").fillet(0.1).section()
)
plane = workplane.plane
shape = toCompound(workplane).transformShape(plane.fG)
edges = shape.Edges()
result_type, result_attributes = DxfDocument._dxf_spline(edges[0], plane)
expected_type, expected_attributes = (
"SPLINE",
{
"control_points": [
(-0.032010295564216654, 0.2020130195642037, 0.0),
(-0.078234124721739, 0.8475143728081896, 0.0),
(0.7171193004814275, 0.9728923786984539, 0.0),
],
"order": 3,
"knots": [
0.18222956891558767,
0.18222956891558767,
0.18222956891558767,
1.416096480384525,
1.416096480384525,
1.416096480384525,
],
"weights": None,
},
)
self.assertEqual(expected_type, result_type)
self.assertAlmostEqual(
expected_attributes["control_points"], result_attributes["control_points"]
)
self.assertEqual(expected_attributes["order"], result_attributes["order"])
self.assertEqual(expected_attributes["knots"], result_attributes["knots"])
self.assertEqual(expected_attributes["weights"], result_attributes["weights"])
def test_add_layer_definition(self):
dxf = DxfDocument()
dxf.add_layer("layer_1")
self.assertIn("layer_1", dxf.document.layers)
def test_add_layer_definition_with_color(self):
dxf = DxfDocument()
dxf.add_layer("layer_1", color=2)
layer = dxf.document.layers.get("layer_1")
self.assertEqual(2, layer.color)
def test_add_layer_definition_with_linetype(self):
dxf = DxfDocument(setup=True)
dxf.add_layer("layer_1", linetype="CENTER")
layer = dxf.document.layers.get("layer_1")
self.assertEqual("CENTER", layer.dxf.linetype)
def test_add_shape_to_layer(self):
line = Workplane().line(0, 10)
dxf = DxfDocument(setup=True)
default_layer_names = set()
for layer in dxf.document.layers:
default_layer_names.add(layer.dxf.name)
dxf = dxf.add_layer("layer_1").add_shape(line, "layer_1")
expected_layer_names = default_layer_names.copy()
expected_layer_names.add("layer_1")
self.assertEqual({"0", "Defpoints"}, default_layer_names)
self.assertEqual(1, len(dxf.msp))
self.assertEqual({"0", "Defpoints", "layer_1"}, expected_layer_names)
self.assertEqual("layer_1", dxf.msp[0].dxf.layer)
self.assertEqual("LINE", dxf.msp[0].dxftype())
def test_set_dxf_version(self):
dxfversion = "AC1032"
dxf_default = DxfDocument()
dxf = DxfDocument(dxfversion=dxfversion)
self.assertNotEqual(dxfversion, dxf_default.document.dxfversion)
self.assertEqual(dxfversion, dxf.document.dxfversion)
def test_set_units(self):
doc_units = 17
dxf_default = DxfDocument()
dxf = DxfDocument(doc_units=17)
self.assertNotEqual(doc_units, dxf_default.document.units)
self.assertEqual(doc_units, dxf.document.units)
def test_set_metadata(self):
metadata = {"CUSTOM_KEY": "custom value"}
dxf = DxfDocument(metadata=metadata)
self.assertEqual(
metadata["CUSTOM_KEY"], dxf.document.ezdxf_metadata().get("CUSTOM_KEY"),
)
def test_add_shape_line(self):
workplane = Workplane().line(1, 1)
dxf = DxfDocument()
dxf.add_shape(workplane)
result = dxf.msp.query("LINE")[0]
expected = ezdxf.entities.line.Line.new(
dxfattribs={"start": (0.0, 0.0, 0.0), "end": (1.0, 1.0, 0.0),},
)
self.assertEqual(expected.dxf.start, result.dxf.start)
self.assertEqual(expected.dxf.end, result.dxf.end)
def test_DxfDocument_import(self):
assert isinstance(exporters.DxfDocument(), DxfDocument)
class TestExporters(BaseTest):
def _exportBox(self, eType, stringsToFind, tolerance=0.1, angularTolerance=0.1):
"""
@ -420,3 +645,26 @@ def test_dxf_approx():
assert _check_dxf_no_spline("limit2.dxf")
assert w1.val().Area() == approx(w1_i2.val().Area(), 1e-3)
def test_dxf_text(tmpdir, testdatadir):
w1 = (
Workplane("XZ")
.box(8, 8, 1)
.faces("<Y")
.workplane()
.text(
",,", 10, -1, True, fontPath=str(Path(testdatadir, "OpenSans-Regular.ttf")),
)
)
fname = tmpdir.joinpath(f"dxf_text.dxf").resolve()
exporters.exportDXF(w1.section(), fname)
s2 = Sketch().importDXF(fname)
w2 = Workplane("XZ", origin=(0, -0.5, 0)).placeSketch(s2).extrude(-1)
assert w1.val().Volume() == approx(59.983287, 1e-2)
assert w2.val().Volume() == approx(w1.val().Volume(), 1e-2)
assert w2.intersect(w1).val().Volume() == approx(w1.val().Volume(), 1e-2)