1717##You should have received a copy of the GNU Lesser General Public License
1818##along with pythonOCC. If not, see <http://www.gnu.org/licenses/>.
1919
20- '''
21- TODO:
22- Hide WireExplorer...
23- BRepTools.Map3DEdges()
24- History:
25- 20-01-2009: initial version
26- 23-03-2009: completed and updated for modular pythonOCC build
27- 23-04-2009: fixed a reference issue ( fixed using ReInit )
28- '''
2920from __future__ import print_function
3021
31- import sys
32- import itertools
22+ __all__ = ['Topo' , 'WireExplorer' , 'dumpTopology' ]
3323
34- from OCC .TopAbs import *
35- from OCC .TopExp import *
36- from OCC .TopoDS import *
37- from OCC .TopTools import *
38- from OCC .BRepTools import *
39- from OCC .BRep import *
40- __all__ = ['Topo' , 'WireExplorer' ]
24+ from OCC .BRep import BRep_Tool
25+
26+ from OCC .BRepTools import BRepTools_WireExplorer
27+ from OCC .TopAbs import (TopAbs_VERTEX , TopAbs_EDGE , TopAbs_FACE , TopAbs_WIRE ,
28+ TopAbs_SHELL , TopAbs_SOLID , TopAbs_COMPOUND ,
29+ TopAbs_COMPSOLID )
30+ from OCC .TopExp import TopExp_Explorer , topexp_MapShapesAndAncestors
31+ from OCC .TopTools import (TopTools_ListOfShape ,
32+ TopTools_ListIteratorOfListOfShape ,
33+ TopTools_IndexedDataMapOfShapeListOfShape )
34+ from OCC .TopoDS import (topods , TopoDS_Wire , TopoDS_Vertex , TopoDS_Edge ,
35+ TopoDS_Face , TopoDS_Shell , TopoDS_Solid ,
36+ TopoDS_Compound , TopoDS_CompSolid , topods_Edge ,
37+ topods_Vertex , TopoDS_Iterator )
4138
4239
4340class WireExplorer (object ):
44- ''' '''
41+ '''
42+ Wire traversal
43+ '''
4544 def __init__ (self , wire ):
4645 assert isinstance (wire , TopoDS_Wire ), 'not a TopoDS_Wire'
4746 self .wire = wire
@@ -90,12 +89,52 @@ def ordered_vertices(self):
9089
9190class Topo (object ):
9291 '''
93- sketch for a pythonic topology wrapper
94- note that `myShape` should be self, which is in return a occ.TopoShape
95- with this
92+ Topology traversal
9693 '''
97- def __init__ (self , myShape ):
94+
95+ def __init__ (self , myShape , ignore_orientation = False ):
96+ """
97+
98+ implements topology traversal from any TopoDS_Shape
99+ this class lets you find how various topological entities are connected from one to another
100+ find the faces connected to an edge, find the vertices this edge is made from, get all faces connected to
101+ a vertex, and find out how many topological elements are connected from a source
102+
103+ *note* when traversing TopoDS_Wire entities, its advised to use the specialized
104+ ``WireExplorer`` class, which will return the vertices / edges in the expected order
105+
106+ :param myShape: the shape which topology will be traversed
107+
108+ :param ignore_orientation: filter out TopoDS_* entities of similar TShape but different Orientation
109+
110+ for instance, a cube has 24 edges, 4 edges for each of 6 faces
111+
112+ that results in 48 vertices, while there are only 8 vertices that have a unique
113+ geometric coordinate
114+
115+ in certain cases ( computing a graph from the topology ) its preferable to return
116+ topological entities that share similar geometry, though differ in orientation
117+ by setting the ``ignore_orientation`` variable
118+ to True, in case of a cube, just 12 edges and only 8 vertices will be returned
119+
120+ for further reference see TopoDS_Shape IsEqual / IsSame methods
121+
122+ """
98123 self .myShape = myShape
124+ self .ignore_orientation = ignore_orientation
125+
126+ # the topoFactory dicts maps topology types and functions that can
127+ # create this topology
128+ self .topoFactory = {
129+ TopAbs_VERTEX : topods .Vertex ,
130+ TopAbs_EDGE : topods .Edge ,
131+ TopAbs_FACE : topods .Face ,
132+ TopAbs_WIRE : topods .Wire ,
133+ TopAbs_SHELL : topods .Shell ,
134+ TopAbs_SOLID : topods .Solid ,
135+ TopAbs_COMPOUND : topods .Compound ,
136+ TopAbs_COMPSOLID : topods .CompSolid
137+ }
99138
100139 def _loop_topo (self , topologyType , topologicalEntity = None , topologyTypeToAvoid = None ):
101140 '''
@@ -104,15 +143,16 @@ def _loop_topo(self, topologyType, topologicalEntity=None, topologyTypeToAvoid=N
104143 for face in srf.faces:
105144 processFace(face)
106145 '''
107- topoTypes = {TopAbs_VERTEX : topods_Vertex ,
108- TopAbs_EDGE : topods_Edge ,
109- TopAbs_FACE : topods_Face ,
110- TopAbs_WIRE : topods_Wire ,
111- TopAbs_SHELL : topods_Shell ,
112- TopAbs_SOLID : topods_Solid ,
113- TopAbs_COMPOUND : topods_Compound ,
114- TopAbs_COMPSOLID : topods_CompSolid }
115- assert topologyType in topoTypes .keys (), '%s not one of %s' % (topologyType , topoTypes .keys ())
146+ topoTypes = {TopAbs_VERTEX : TopoDS_Vertex ,
147+ TopAbs_EDGE : TopoDS_Edge ,
148+ TopAbs_FACE : TopoDS_Face ,
149+ TopAbs_WIRE : TopoDS_Wire ,
150+ TopAbs_SHELL : TopoDS_Shell ,
151+ TopAbs_SOLID : TopoDS_Solid ,
152+ TopAbs_COMPOUND : TopoDS_Compound ,
153+ TopAbs_COMPSOLID : TopoDS_CompSolid }
154+
155+ assert topologyType in topoTypes .keys (), '%s not one of %s' % (topologyType , topoTypes .keys ())
116156 self .topExp = TopExp_Explorer ()
117157 # use self.myShape if nothing is specified
118158 if topologicalEntity is None and topologyTypeToAvoid is None :
@@ -122,24 +162,43 @@ def _loop_topo(self, topologyType, topologicalEntity=None, topologyTypeToAvoid=N
122162 elif topologyTypeToAvoid is None :
123163 self .topExp .Init (topologicalEntity , topologyType )
124164 elif topologyTypeToAvoid :
125- self .topExp .Init (topologicalEntity , topologyType , topologyTypeToAvoid )
165+ self .topExp .Init (topologicalEntity ,
166+ topologyType ,
167+ topologyTypeToAvoid )
126168 seq = []
127169 hashes = [] # list that stores hashes to avoid redundancy
128170 occ_seq = TopTools_ListOfShape ()
129171 while self .topExp .More ():
130172 current_item = self .topExp .Current ()
131173 current_item_hash = current_item .__hash__ ()
174+
132175 if not current_item_hash in hashes :
133176 hashes .append (current_item_hash )
134177 occ_seq .Append (current_item )
178+
135179 self .topExp .Next ()
136180 # Convert occ_seq to python list
137181 occ_iterator = TopTools_ListIteratorOfListOfShape (occ_seq )
138182 while occ_iterator .More ():
139- topo_to_add = topoTypes [topologyType ](occ_iterator .Value ())
183+ topo_to_add = self . topoFactory [topologyType ](occ_iterator .Value ())
140184 seq .append (topo_to_add )
141185 occ_iterator .Next ()
142- return iter (seq )
186+
187+ if self .ignore_orientation :
188+ # filter out those entities that share the same TShape
189+ # but do *not* share the same orientation
190+ filter_orientation_seq = []
191+ for i in seq :
192+ _present = False
193+ for j in filter_orientation_seq :
194+ if i .IsSame (j ):
195+ _present = True
196+ break
197+ if _present is False :
198+ filter_orientation_seq .append (i )
199+ return filter_orientation_seq
200+ else :
201+ return iter (seq )
143202
144203 def faces (self ):
145204 '''
@@ -252,29 +311,34 @@ def _map_shapes_and_ancestors(self, topoTypeA, topoTypeB, topologicalEntity):
252311 results = _map .FindFromKey (topologicalEntity )
253312 if results .IsEmpty ():
254313 yield None
255- topoTypes = {TopAbs_VERTEX : topods_Vertex ,
256- TopAbs_EDGE : topods_Edge ,
257- TopAbs_FACE : topods_Face ,
258- TopAbs_WIRE : topods_Wire ,
259- TopAbs_SHELL : topods_Shell ,
260- TopAbs_SOLID : topods_Solid ,
261- TopAbs_COMPOUND : topods_Compound ,
262- TopAbs_COMPSOLID : topods_CompSolid }
314+
263315 topology_iterator = TopTools_ListIteratorOfListOfShape (results )
264316 while topology_iterator .More ():
265- topo_entity = topoTypes [topoTypeB ](topology_iterator .Value ())
317+
318+ topo_entity = self .topoFactory [topoTypeB ](topology_iterator .Value ())
319+
266320 # return the entity if not in set
267321 # to assure we're not returning entities several times
268322 if not topo_entity in topo_set :
269- yield topo_entity
323+ if self .ignore_orientation :
324+ unique = True
325+ for i in topo_set :
326+ if i .IsSame (topo_entity ):
327+ unique = False
328+ break
329+ if unique :
330+ yield topo_entity
331+ else :
332+ yield topo_entity
333+
270334 topo_set .add (topo_entity )
271335 topology_iterator .Next ()
272336
273337 def _number_shapes_ancestors (self , topoTypeA , topoTypeB , topologicalEntity ):
274338 '''returns the number of shape ancestors
275339 If you want to know how many edges a faces has:
276340 _number_shapes_ancestors(self, TopAbs_EDGE, TopAbs_FACE, edg)
277- will return the number of edges a faces has
341+ will return the number of edges a faces has
278342 @param topoTypeA:
279343 @param topoTypeB:
280344 @param topologicalEntity:
@@ -291,16 +355,31 @@ def _number_shapes_ancestors(self, topoTypeA, topoTypeB, topologicalEntity):
291355 topology_iterator .Next ()
292356 return len (topo_set )
293357
294- #==== ======================================================================
295- # EDGE <-> FACE
296- #==== ======================================================================
358+ # ======================================================================
359+ # EDGE <-> FACE
360+ # ======================================================================
297361 def faces_from_edge (self , edge ):
362+ """
363+
364+ :param edge:
365+ :return:
366+ """
298367 return self ._map_shapes_and_ancestors (TopAbs_EDGE , TopAbs_FACE , edge )
299368
300369 def number_of_faces_from_edge (self , edge ):
370+ """
371+
372+ :param edge:
373+ :return:
374+ """
301375 return self ._number_shapes_ancestors (TopAbs_EDGE , TopAbs_FACE , edge )
302376
303377 def edges_from_face (self , face ):
378+ """
379+
380+ :param face:
381+ :return:
382+ """
304383 return self ._loop_topo (TopAbs_EDGE , face )
305384
306385 def number_of_edges_from_face (self , face ):
@@ -309,9 +388,9 @@ def number_of_edges_from_face(self, face):
309388 cnt += 1
310389 return cnt
311390
312- #===== ======================================================================
313- # VERTEX <-> EDGE
314- #===== ======================================================================
391+ # ======================================================================
392+ # VERTEX <-> EDGE
393+ # ======================================================================
315394 def vertices_from_edge (self , edg ):
316395 return self ._loop_topo (TopAbs_VERTEX , edg )
317396
@@ -327,9 +406,9 @@ def edges_from_vertex(self, vertex):
327406 def number_of_edges_from_vertex (self , vertex ):
328407 return self ._number_shapes_ancestors (TopAbs_VERTEX , TopAbs_EDGE , vertex )
329408
330- #===== ======================================================================
331- # WIRE <-> EDGE
332- #===== ======================================================================
409+ # ======================================================================
410+ # WIRE <-> EDGE
411+ # ======================================================================
333412 def edges_from_wire (self , wire ):
334413 return self ._loop_topo (TopAbs_EDGE , wire )
335414
@@ -342,12 +421,15 @@ def number_of_edges_from_wire(self, wire):
342421 def wires_from_edge (self , edg ):
343422 return self ._map_shapes_and_ancestors (TopAbs_EDGE , TopAbs_WIRE , edg )
344423
424+ def wires_from_vertex (self , edg ):
425+ return self ._map_shapes_and_ancestors (TopAbs_VERTEX , TopAbs_WIRE , edg )
426+
345427 def number_of_wires_from_edge (self , edg ):
346428 return self ._number_shapes_ancestors (TopAbs_EDGE , TopAbs_WIRE , edg )
347429
348- #===== ======================================================================
349- # WIRE <-> FACE
350- #===== ======================================================================
430+ # ======================================================================
431+ # WIRE <-> FACE
432+ # ======================================================================
351433 def wires_from_face (self , face ):
352434 return self ._loop_topo (TopAbs_WIRE , face )
353435
@@ -363,9 +445,9 @@ def faces_from_wire(self, wire):
363445 def number_of_faces_from_wires (self , wire ):
364446 return self ._number_shapes_ancestors (TopAbs_WIRE , TopAbs_FACE , wire )
365447
366- #===== ======================================================================
367- # VERTEX <-> FACE
368- #===== ======================================================================
448+ # ======================================================================
449+ # VERTEX <-> FACE
450+ # ======================================================================
369451 def faces_from_vertex (self , vertex ):
370452 return self ._map_shapes_and_ancestors (TopAbs_VERTEX , TopAbs_FACE , vertex )
371453
@@ -381,9 +463,9 @@ def number_of_vertices_from_face(self, face):
381463 cnt += 1
382464 return cnt
383465
384- #===== ======================================================================
385- # FACE <-> SOLID
386- #===== ======================================================================
466+ # ======================================================================
467+ # FACE <-> SOLID
468+ # ======================================================================
387469 def solids_from_face (self , face ):
388470 return self ._map_shapes_and_ancestors (TopAbs_FACE , TopAbs_SOLID , face )
389471
@@ -406,11 +488,12 @@ def dumpTopology(shape, level=0):
406488 """
407489 brt = BRep_Tool ()
408490 s = shape .ShapeType ()
409- print ("." * level , end = "" )
410- print (shapeTypeString (shape ), end = "" )
411491 if s == TopAbs_VERTEX :
412492 pnt = brt .Pnt (topods_Vertex (shape ))
413- print ("<Vertex: %s %s %s>" % (pnt .X (), pnt .Y (), pnt .Z ()))
493+ print (".." * level + "<Vertex %i: %s %s %s>" % (hash (shape ), pnt .X (), pnt .Y (), pnt .Z ()))
494+ else :
495+ print (".." * level , end = "" )
496+ print (shapeTypeString (shape ))
414497 it = TopoDS_Iterator (shape )
415498 while it .More ():
416499 shp = it .Value ()
@@ -437,4 +520,4 @@ def shapeTypeString(shape):
437520 s = "Compound."
438521 if st == TopAbs_COMPSOLID :
439522 s = "Compsolid."
440- return s + ":" + str ( shape . HashCode ( 23232232 ))
523+ return "%s: %i" % ( s , hash ( shape ))
0 commit comments