11
11
from OCP .GCPnts import GCPnts_QuasiUniformDeflection
12
12
13
13
DISCRETIZATION_TOLERANCE = 1e-3
14
- DEFAULT_DIR = gp_Dir (- 1.75 , 1.1 , 5 )
15
14
16
15
SVG_TEMPLATE = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
17
16
<svg
23
22
>
24
23
<g transform="scale(%(unitScale)s, -%(unitScale)s) translate(%(xTranslate)s,%(yTranslate)s)" stroke-width="%(strokeWidth)s" fill="none">
25
24
<!-- hidden lines -->
26
- <g stroke="rgb(160, 160, 160 )" fill="none" stroke-dasharray="%(strokeWidth)s,%(strokeWidth)s" >
25
+ <g stroke="rgb(%(hiddenColor)s )" fill="none" stroke-dasharray="%(strokeWidth)s,%(strokeWidth)s" >
27
26
%(hiddenContent)s
28
27
</g>
29
28
30
29
<!-- solid lines -->
31
- <g stroke="rgb(0, 0, 0 )" fill="none">
30
+ <g stroke="rgb(%(strokeColor)s )" fill="none">
32
31
%(visibleContent)s
33
32
</g>
34
33
</g>
35
- <g transform="translate(20,%(textboxY)s)" stroke="rgb(0,0,255)">
34
+ %(axesIndicator)s
35
+ </svg>
36
+ """
37
+
38
+ # The axes indicator - needs to be replaced with something dynamic eventually
39
+ AXES_TEMPLATE = """<g transform="translate(20,%(textboxY)s)" stroke="rgb(0,0,255)">
36
40
<line x1="30" y1="-30" x2="75" y2="-33" stroke-width="3" stroke="#000000" />
37
41
<text x="80" y="-30" style="stroke:#000000">X </text>
38
42
45
49
<line x1="0" y1="0" x2="%(unitScale)s" y2="0" stroke-width="3" />
46
50
<text x="0" y="20" style="stroke:#000000">1 %(uom)s </text>
47
51
-->
48
- </g>
49
- </svg>
50
- """
52
+ </g>"""
51
53
52
54
PATHTEMPLATE = '\t \t \t <path d="%s" />\n '
53
55
@@ -59,7 +61,7 @@ class UNITS:
59
61
60
62
def guessUnitOfMeasure (shape ):
61
63
"""
62
- Guess the unit of measure of a shape.
64
+ Guess the unit of measure of a shape.
63
65
"""
64
66
bb = BoundBox ._fromTopoDS (shape .wrapped )
65
67
@@ -81,7 +83,7 @@ def guessUnitOfMeasure(shape):
81
83
82
84
def makeSVGedge (e ):
83
85
"""
84
-
86
+ Creates an SVG edge from a OCCT edge.
85
87
"""
86
88
87
89
cs = StringIO .StringIO ()
@@ -106,7 +108,7 @@ def makeSVGedge(e):
106
108
107
109
def getPaths (visibleShapes , hiddenShapes ):
108
110
"""
109
-
111
+ Collects the visible and hidden edges from the CadQuery object.
110
112
"""
111
113
112
114
hiddenPaths = []
@@ -125,10 +127,38 @@ def getPaths(visibleShapes, hiddenShapes):
125
127
126
128
def getSVG (shape , opts = None ):
127
129
"""
128
- Export a shape to SVG
130
+ Export a shape to SVG text.
131
+
132
+ :param shape: A CadQuery shape object to convert to an SVG string.
133
+ :type Shape: Vertex, Edge, Wire, Face, Shell, Solid, or Compound.
134
+ :param opts: An options dictionary that influences the SVG that is output.
135
+ :type opts: Dictionary, keys are as follows:
136
+ width: Document width of the resulting image.
137
+ height: Document height of the resulting image.
138
+ marginLeft: Inset margin from the left side of the document.
139
+ marginTop: Inset margin from the top side of the document.
140
+ projectionDir: Direction the camera will view the shape from.
141
+ showAxes: Whether or not to show the axes indicator, which will only be
142
+ visible when the projectionDir is also at the default.
143
+ strokeWidth: Width of the line that visible edges are drawn with.
144
+ strokeColor: Color of the line that visible edges are drawn with.
145
+ hiddenColor: Color of the line that hidden edges are drawn with.
146
+ showHidden: Whether or not to show hidden lines.
129
147
"""
130
148
131
- d = {"width" : 800 , "height" : 240 , "marginLeft" : 200 , "marginTop" : 20 }
149
+ # Available options and their defaults
150
+ d = {
151
+ "width" : 800 ,
152
+ "height" : 240 ,
153
+ "marginLeft" : 200 ,
154
+ "marginTop" : 20 ,
155
+ "projectionDir" : (- 1.75 , 1.1 , 5 ),
156
+ "showAxes" : True ,
157
+ "strokeWidth" : - 1.0 , # -1 = calculated based on unitScale
158
+ "strokeColor" : (0 , 0 , 0 ), # RGB 0-255
159
+ "hiddenColor" : (160 , 160 , 160 ), # RGB 0-255
160
+ "showHidden" : True ,
161
+ }
132
162
133
163
if opts :
134
164
d .update (opts )
@@ -140,11 +170,17 @@ def getSVG(shape, opts=None):
140
170
height = float (d ["height" ])
141
171
marginLeft = float (d ["marginLeft" ])
142
172
marginTop = float (d ["marginTop" ])
173
+ projectionDir = tuple (d ["projectionDir" ])
174
+ showAxes = bool (d ["showAxes" ])
175
+ strokeWidth = float (d ["strokeWidth" ])
176
+ strokeColor = tuple (d ["strokeColor" ])
177
+ hiddenColor = tuple (d ["hiddenColor" ])
178
+ showHidden = bool (d ["showHidden" ])
143
179
144
180
hlr = HLRBRep_Algo ()
145
181
hlr .Add (shape .wrapped )
146
182
147
- projector = HLRAlgo_Projector (gp_Ax2 (gp_Pnt (), DEFAULT_DIR ))
183
+ projector = HLRAlgo_Projector (gp_Ax2 (gp_Pnt (), gp_Dir ( * projectionDir ) ))
148
184
149
185
hlr .Projector (projector )
150
186
hlr .Update ()
@@ -190,7 +226,7 @@ def getSVG(shape, opts=None):
190
226
# get bounding box -- these are all in 2-d space
191
227
bb = Compound .makeCompound (hidden + visible ).BoundingBox ()
192
228
193
- # width pixels for x, height pixesl for y
229
+ # width pixels for x, height pixels for y
194
230
unitScale = min (width / bb .xlen * 0.75 , height / bb .ylen * 0.75 )
195
231
196
232
# compute amount to translate-- move the top left into view
@@ -199,19 +235,36 @@ def getSVG(shape, opts=None):
199
235
(0 - bb .ymax ) - marginTop / unitScale ,
200
236
)
201
237
202
- # compute paths ( again -- had to strip out freecad crap )
238
+ # If the user did not specify a stroke width, calculate it based on the unit scale
239
+ if strokeWidth == - 1.0 :
240
+ strokeWidth = 1.0 / unitScale
241
+
242
+ # compute paths
203
243
hiddenContent = ""
204
- for p in hiddenPaths :
205
- hiddenContent += PATHTEMPLATE % p
244
+
245
+ # Prevent hidden paths from being added if the user disabled them
246
+ if showHidden :
247
+ for p in hiddenPaths :
248
+ hiddenContent += PATHTEMPLATE % p
206
249
207
250
visibleContent = ""
208
251
for p in visiblePaths :
209
252
visibleContent += PATHTEMPLATE % p
210
253
254
+ # If the caller wants the axes indicator and is using the default direction, add in the indicator
255
+ if showAxes and projectionDir == (- 1.75 , 1.1 , 5 ):
256
+ axesIndicator = AXES_TEMPLATE % (
257
+ {"unitScale" : str (unitScale ), "textboxY" : str (height - 30 ), "uom" : str (uom )}
258
+ )
259
+ else :
260
+ axesIndicator = ""
261
+
211
262
svg = SVG_TEMPLATE % (
212
263
{
213
264
"unitScale" : str (unitScale ),
214
- "strokeWidth" : str (1.0 / unitScale ),
265
+ "strokeWidth" : str (strokeWidth ),
266
+ "strokeColor" : "," .join ([str (x ) for x in strokeColor ]),
267
+ "hiddenColor" : "," .join ([str (x ) for x in hiddenColor ]),
215
268
"hiddenContent" : hiddenContent ,
216
269
"visibleContent" : visibleContent ,
217
270
"xTranslate" : str (xTranslate ),
@@ -220,22 +273,21 @@ def getSVG(shape, opts=None):
220
273
"height" : str (height ),
221
274
"textboxY" : str (height - 30 ),
222
275
"uom" : str (uom ),
276
+ "axesIndicator" : axesIndicator ,
223
277
}
224
278
)
225
- # svg = SVG_TEMPLATE % (
226
- # {"content": projectedContent}
227
- # )
279
+
228
280
return svg
229
281
230
282
231
- def exportSVG (shape , fileName : str ):
283
+ def exportSVG (shape , fileName : str , opts = None ):
232
284
"""
233
- accept a cadquery shape, and export it to the provided file
234
- TODO: should use file-like objects, not a fileName, and/or be able to return a string instead
235
- export a view of a part to svg
285
+ Accept a cadquery shape, and export it to the provided file
286
+ TODO: should use file-like objects, not a fileName, and/or be able to return a string instead
287
+ export a view of a part to svg
236
288
"""
237
289
238
- svg = getSVG (shape .val ())
290
+ svg = getSVG (shape .val (), opts )
239
291
f = open (fileName , "w" )
240
292
f .write (svg )
241
293
f .close ()
0 commit comments