14
14
from OCP .STEPCAFControl import STEPCAFControl_Writer
15
15
from OCP .STEPControl import STEPControl_StepModelType
16
16
from OCP .IFSelect import IFSelect_ReturnStatus
17
+ from OCP .TDF import TDF_Label
18
+ from OCP .TDataStd import TDataStd_Name
19
+ from OCP .TDocStd import TDocStd_Document
17
20
from OCP .XCAFApp import XCAFApp_Application
21
+ from OCP .XCAFDoc import XCAFDoc_DocumentTool , XCAFDoc_ColorGen
18
22
from OCP .XmlDrivers import (
19
23
XmlDrivers_DocumentStorageDriver ,
20
24
XmlDrivers_DocumentRetrievalDriver ,
28
32
29
33
from ..assembly import AssemblyProtocol , toCAF , toVTK , toFusedCAF
30
34
from ..geom import Location
35
+ from ..shapes import Shape , Compound
36
+ from ..assembly import Color
31
37
32
38
33
39
class ExportModes :
@@ -42,7 +48,7 @@ def exportAssembly(
42
48
assy : AssemblyProtocol ,
43
49
path : str ,
44
50
mode : STEPExportModeLiterals = "default" ,
45
- ** kwargs
51
+ ** kwargs ,
46
52
) -> bool :
47
53
"""
48
54
Export an assembly to a STEP file.
@@ -99,6 +105,168 @@ def exportAssembly(
99
105
return status == IFSelect_ReturnStatus .IFSelect_RetDone
100
106
101
107
108
+ def exportStepMeta (
109
+ assy : AssemblyProtocol ,
110
+ path : str ,
111
+ write_pcurves : bool = True ,
112
+ precision_mode : int = 0 ,
113
+ ) -> bool :
114
+ """
115
+ Export an assembly to a STEP file with faces tagged with names and colors. This is done as a
116
+ separate method from the main STEP export because this is not compatible with the fused mode
117
+ and also flattens the hierarchy of the STEP.
118
+
119
+ Layers are used because some software does not understand the ADVANCED_FACE entity and needs
120
+ names attached to layers instead.
121
+
122
+ :param assy: assembly
123
+ :param path: Path and filename for writing
124
+ :param write_pcurves: Enable or disable writing parametric curves to the STEP file. Default True.
125
+ If False, writes STEP file without pcurves. This decreases the size of the resulting STEP file.
126
+ :param precision_mode: Controls the uncertainty value for STEP entities. Specify -1, 0, or 1. Default 0.
127
+ See OCCT documentation.
128
+ """
129
+
130
+ pcurves = 1
131
+ if not write_pcurves :
132
+ pcurves = 0
133
+
134
+ # Initialize the XCAF document that will allow the STEP export
135
+ app = XCAFApp_Application .GetApplication_s ()
136
+ doc = TDocStd_Document (TCollection_ExtendedString ("XmlOcaf" ))
137
+ app .InitDocument (doc )
138
+
139
+ # Shape and color tools
140
+ shape_tool = XCAFDoc_DocumentTool .ShapeTool_s (doc .Main ())
141
+ color_tool = XCAFDoc_DocumentTool .ColorTool_s (doc .Main ())
142
+ layer_tool = XCAFDoc_DocumentTool .LayerTool_s (doc .Main ())
143
+
144
+ def _process_child (child : AssemblyProtocol , assy_label : TDF_Label ):
145
+ """
146
+ Process a child part which is not a subassembly.
147
+ :param child: Child part to process (we should already have filtered out subassemblies)
148
+ :param assy_label: The label for the assembly to add this part to
149
+ :return: None
150
+ """
151
+
152
+ child_items = None
153
+
154
+ # We combine these because the metadata could be stored at the parent or child level
155
+ combined_names = {** assy ._subshape_names , ** child ._subshape_names }
156
+ combined_colors = {** assy ._subshape_colors , ** child ._subshape_colors }
157
+ combined_layers = {** assy ._subshape_layers , ** child ._subshape_layers }
158
+
159
+ # Collect all of the shapes in the child object
160
+ if child .obj :
161
+ child_items = (
162
+ child .obj
163
+ if isinstance (child .obj , Shape )
164
+ else Compound .makeCompound (
165
+ s for s in child .obj .vals () if isinstance (s , Shape )
166
+ ),
167
+ child .name ,
168
+ child .loc ,
169
+ child .color ,
170
+ )
171
+
172
+ if child_items :
173
+ shape , name , loc , color = child_items
174
+
175
+ # Handle shape name, color and location
176
+ part_label = shape_tool .AddShape (shape .wrapped , False )
177
+ TDataStd_Name .Set_s (part_label , TCollection_ExtendedString (name ))
178
+ if color :
179
+ color_tool .SetColor (part_label , color .wrapped , XCAFDoc_ColorGen )
180
+ shape_tool .AddComponent (assy_label , part_label , loc .wrapped )
181
+
182
+ # If this assembly has shape metadata, add it to the shape
183
+ if (
184
+ len (combined_names ) > 0
185
+ or len (combined_colors ) > 0
186
+ or len (combined_layers ) > 0
187
+ ):
188
+ names = combined_names
189
+ colors = combined_colors
190
+ layers = combined_layers
191
+
192
+ # Step through every face in the shape, and see if any metadata needs to be attached to it
193
+ for face in shape .Faces ():
194
+ if face in names or face in shape in colors or face in layers :
195
+ # Add the face as a subshape
196
+ face_label = shape_tool .AddSubShape (part_label , face .wrapped )
197
+
198
+ # In some cases the face may not be considered part of the shape, so protect
199
+ # against that
200
+ if not face_label .IsNull ():
201
+ # Set the ADVANCED_FACE label, even though the layer holds the same data
202
+ if face in names :
203
+ TDataStd_Name .Set_s (
204
+ face_label , TCollection_ExtendedString (names [face ])
205
+ )
206
+
207
+ # Set the individual face color
208
+ if face in colors :
209
+ color_tool .SetColor (
210
+ face_label , colors [face ].wrapped , XCAFDoc_ColorGen ,
211
+ )
212
+
213
+ # Also add a layer to hold the face label data
214
+ if face in layers :
215
+ layer_label = layer_tool .AddLayer (
216
+ TCollection_ExtendedString (layers [face ])
217
+ )
218
+ layer_tool .SetLayer (face_label , layer_label )
219
+
220
+ def _process_assembly (
221
+ assy : AssemblyProtocol , parent_label : Optional [TDF_Label ] = None
222
+ ):
223
+ """
224
+ Recursively process the assembly and its children.
225
+ :param assy: Assembly to process
226
+ :param parent_label: The parent label for the assembly
227
+ :return: None
228
+ """
229
+ # Use the assembly name if the user set it
230
+ assembly_name = assy .name if assy .name else str (uuid .uuid1 ())
231
+
232
+ # Create the top level object that will hold all the subassemblies and parts
233
+ assy_label = shape_tool .NewShape ()
234
+ TDataStd_Name .Set_s (assy_label , TCollection_ExtendedString (assembly_name ))
235
+
236
+ # Handle subassemblies
237
+ if parent_label :
238
+ shape_tool .AddComponent (parent_label , assy_label , assy .loc .wrapped )
239
+
240
+ # The children may be parts or assemblies
241
+ for child in assy .children :
242
+ # Child is a part
243
+ if len (list (child .children )) == 0 :
244
+ _process_child (child , assy_label )
245
+ # Child is a subassembly
246
+ else :
247
+ _process_assembly (child , assy_label )
248
+
249
+ _process_assembly (assy )
250
+
251
+ # Update the assemblies
252
+ shape_tool .UpdateAssemblies ()
253
+
254
+ # Set up the writer and write the STEP file
255
+ session = XSControl_WorkSession ()
256
+ writer = STEPCAFControl_Writer (session , False )
257
+ Interface_Static .SetIVal_s ("write.stepcaf.subshapes.name" , 1 )
258
+ writer .SetColorMode (True )
259
+ writer .SetLayerMode (True )
260
+ writer .SetNameMode (True )
261
+ Interface_Static .SetIVal_s ("write.surfacecurve.mode" , pcurves )
262
+ Interface_Static .SetIVal_s ("write.precision.mode" , precision_mode )
263
+ writer .Transfer (doc , STEPControl_StepModelType .STEPControl_AsIs )
264
+
265
+ status = writer .Write (path )
266
+
267
+ return status == IFSelect_ReturnStatus .IFSelect_RetDone
268
+
269
+
102
270
def exportCAF (assy : AssemblyProtocol , path : str ) -> bool :
103
271
"""
104
272
Export an assembly to a OCAF xml file (internal OCCT format).
0 commit comments