diff --git a/cadquery/occ_impl/exporters/__init__.py b/cadquery/occ_impl/exporters/__init__.py
index a3e58d13..0242a9e7 100644
--- a/cadquery/occ_impl/exporters/__init__.py
+++ b/cadquery/occ_impl/exporters/__init__.py
@@ -37,16 +37,18 @@ def export(
exportType: Optional[ExportLiterals] = None,
tolerance: float = 0.1,
angularTolerance: float = 0.1,
+ opt=None,
):
"""
Export Wokrplane or Shape to file. Multiple entities are converted to compound.
-
+
:param w: Shape or Wokrplane to be exported.
:param fname: output filename.
:param exportType: the exportFormat to use. If None will be inferred from the extension. Default: None.
:param tolerance: the deflection tolerance, in model units. Default 0.1.
:param angularTolerance: the angular tolerance, in radians. Default 0.1.
+ :param opt: additional options passed to the specific exporter. Default None.
"""
shape: Shape
@@ -81,7 +83,7 @@ def export(
elif exportType == ExportTypes.SVG:
with open(fname, "w") as f:
- f.write(getSVG(shape))
+ f.write(getSVG(shape, opt))
elif exportType == ExportTypes.AMF:
tess = shape.tessellate(tolerance, angularTolerance)
@@ -125,14 +127,14 @@ def exportShape(
angularTolerance: float = 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 exportType: the exportFormat to use
- :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
- :param tolerance: the linear tolerance, in model units. Default 0.1.
- :param angularTolerance: the angular tolerance, in radians. Default 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 exportType: the exportFormat to use
+ :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
+ :param tolerance: the linear tolerance, in model units. Default 0.1.
+ :param angularTolerance: the angular tolerance, in radians. Default 0.1.
"""
def tessellate(shape, angularTolerance):
@@ -188,8 +190,8 @@ def exportShape(
@deprecate()
def readAndDeleteFile(fileName):
"""
- read data from file provided, and delete it when done
- return the contents as a string
+ Read data from file provided, and delete it when done
+ return the contents as a string
"""
res = ""
with open(fileName, "r") as f:
diff --git a/cadquery/occ_impl/exporters/svg.py b/cadquery/occ_impl/exporters/svg.py
index 9704db5b..b82fa810 100644
--- a/cadquery/occ_impl/exporters/svg.py
+++ b/cadquery/occ_impl/exporters/svg.py
@@ -11,7 +11,6 @@ from OCP.HLRAlgo import HLRAlgo_Projector
from OCP.GCPnts import GCPnts_QuasiUniformDeflection
DISCRETIZATION_TOLERANCE = 1e-3
-DEFAULT_DIR = gp_Dir(-1.75, 1.1, 5)
SVG_TEMPLATE = """
+"""
+
+# The axes indicator - needs to be replaced with something dynamic eventually
+AXES_TEMPLATE = """X
@@ -45,9 +49,7 @@ SVG_TEMPLATE = """
1 %(uom)s
-->
-
-
-"""
+ """
PATHTEMPLATE = '\t\t\t\n'
@@ -59,7 +61,7 @@ class UNITS:
def guessUnitOfMeasure(shape):
"""
- Guess the unit of measure of a shape.
+ Guess the unit of measure of a shape.
"""
bb = BoundBox._fromTopoDS(shape.wrapped)
@@ -81,7 +83,7 @@ def guessUnitOfMeasure(shape):
def makeSVGedge(e):
"""
-
+ Creates an SVG edge from a OCCT edge.
"""
cs = StringIO.StringIO()
@@ -106,7 +108,7 @@ def makeSVGedge(e):
def getPaths(visibleShapes, hiddenShapes):
"""
-
+ Collects the visible and hidden edges from the CadQuery object.
"""
hiddenPaths = []
@@ -125,10 +127,38 @@ def getPaths(visibleShapes, hiddenShapes):
def getSVG(shape, opts=None):
"""
- Export a shape to SVG
+ Export a shape to SVG text.
+
+ :param shape: A CadQuery shape object to convert to an SVG string.
+ :type Shape: Vertex, Edge, Wire, Face, Shell, Solid, or Compound.
+ :param opts: An options dictionary that influences the SVG that is output.
+ :type opts: Dictionary, keys are as follows:
+ width: Document width of the resulting image.
+ height: Document height of the resulting image.
+ marginLeft: Inset margin from the left side of the document.
+ marginTop: Inset margin from the top side of the document.
+ projectionDir: Direction the camera will view the shape from.
+ showAxes: Whether or not to show the axes indicator, which will only be
+ visible when the projectionDir is also at the default.
+ strokeWidth: Width of the line that visible edges are drawn with.
+ strokeColor: Color of the line that visible edges are drawn with.
+ hiddenColor: Color of the line that hidden edges are drawn with.
+ showHidden: Whether or not to show hidden lines.
"""
- d = {"width": 800, "height": 240, "marginLeft": 200, "marginTop": 20}
+ # Available options and their defaults
+ d = {
+ "width": 800,
+ "height": 240,
+ "marginLeft": 200,
+ "marginTop": 20,
+ "projectionDir": (-1.75, 1.1, 5),
+ "showAxes": True,
+ "strokeWidth": -1.0, # -1 = calculated based on unitScale
+ "strokeColor": (0, 0, 0), # RGB 0-255
+ "hiddenColor": (160, 160, 160), # RGB 0-255
+ "showHidden": True,
+ }
if opts:
d.update(opts)
@@ -140,11 +170,17 @@ def getSVG(shape, opts=None):
height = float(d["height"])
marginLeft = float(d["marginLeft"])
marginTop = float(d["marginTop"])
+ projectionDir = tuple(d["projectionDir"])
+ showAxes = bool(d["showAxes"])
+ strokeWidth = float(d["strokeWidth"])
+ strokeColor = tuple(d["strokeColor"])
+ hiddenColor = tuple(d["hiddenColor"])
+ showHidden = bool(d["showHidden"])
hlr = HLRBRep_Algo()
hlr.Add(shape.wrapped)
- projector = HLRAlgo_Projector(gp_Ax2(gp_Pnt(), DEFAULT_DIR))
+ projector = HLRAlgo_Projector(gp_Ax2(gp_Pnt(), gp_Dir(*projectionDir)))
hlr.Projector(projector)
hlr.Update()
@@ -190,7 +226,7 @@ def getSVG(shape, opts=None):
# get bounding box -- these are all in 2-d space
bb = Compound.makeCompound(hidden + visible).BoundingBox()
- # width pixels for x, height pixesl for y
+ # width pixels for x, height pixels for y
unitScale = min(width / bb.xlen * 0.75, height / bb.ylen * 0.75)
# compute amount to translate-- move the top left into view
@@ -199,19 +235,36 @@ def getSVG(shape, opts=None):
(0 - bb.ymax) - marginTop / unitScale,
)
- # compute paths ( again -- had to strip out freecad crap )
+ # If the user did not specify a stroke width, calculate it based on the unit scale
+ if strokeWidth == -1.0:
+ strokeWidth = 1.0 / unitScale
+
+ # compute paths
hiddenContent = ""
- for p in hiddenPaths:
- hiddenContent += PATHTEMPLATE % p
+
+ # Prevent hidden paths from being added if the user disabled them
+ if showHidden:
+ for p in hiddenPaths:
+ hiddenContent += PATHTEMPLATE % p
visibleContent = ""
for p in visiblePaths:
visibleContent += PATHTEMPLATE % p
+ # If the caller wants the axes indicator and is using the default direction, add in the indicator
+ if showAxes and projectionDir == (-1.75, 1.1, 5):
+ axesIndicator = AXES_TEMPLATE % (
+ {"unitScale": str(unitScale), "textboxY": str(height - 30), "uom": str(uom)}
+ )
+ else:
+ axesIndicator = ""
+
svg = SVG_TEMPLATE % (
{
"unitScale": str(unitScale),
- "strokeWidth": str(1.0 / unitScale),
+ "strokeWidth": str(strokeWidth),
+ "strokeColor": ",".join([str(x) for x in strokeColor]),
+ "hiddenColor": ",".join([str(x) for x in hiddenColor]),
"hiddenContent": hiddenContent,
"visibleContent": visibleContent,
"xTranslate": str(xTranslate),
@@ -220,22 +273,21 @@ def getSVG(shape, opts=None):
"height": str(height),
"textboxY": str(height - 30),
"uom": str(uom),
+ "axesIndicator": axesIndicator,
}
)
- # svg = SVG_TEMPLATE % (
- # {"content": projectedContent}
- # )
+
return svg
-def exportSVG(shape, fileName: str):
+def exportSVG(shape, fileName: str, opts=None):
"""
- accept a cadquery shape, and export it to the provided file
- 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
+ Accept a cadquery shape, and export it to the provided file
+ 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())
+ svg = getSVG(shape.val(), opts)
f = open(fileName, "w")
f.write(svg)
f.close()
diff --git a/tests/test_exporters.py b/tests/test_exporters.py
index 06125d40..bf031573 100644
--- a/tests/test_exporters.py
+++ b/tests/test_exporters.py
@@ -49,6 +49,26 @@ class TestExporters(BaseTest):
exporters.export(self._box(), "out.svg")
+ def testSVGOptions(self):
+ self._exportBox(exporters.ExportTypes.SVG, ["