Salome HOME
[CEA] Improve ShaperResults
[modules/shaper.git] / src / ConnectorPlugin / ConnectorPlugin_PublishToStudyFeature.py
1 # Copyright (C) 2014-2024  CEA, EDF
2 #
3 # This library is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU Lesser General Public
5 # License as published by the Free Software Foundation; either
6 # version 2.1 of the License, or (at your option) any later version.
7 #
8 # This library is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 # Lesser General Public License for more details.
12 #
13 # You should have received a copy of the GNU Lesser General Public
14 # License along with this library; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 #
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 #
19
20 ## @package Plugins
21 #  ExportFeature class definition
22
23 import ModelAPI
24 import ExchangeAPI
25 import EventsAPI
26 from GeomAPI import *
27 import GeomAlgoAPI
28
29 import salome
30 from salome.shaper import model
31
32 import SHAPERSTUDY_ORB
33 import SHAPERSTUDY_utils
34
35 ## @ingroup Plugins
36 #  Feature to export all shapes and groups into the GEOM module
37 class PublishToStudyFeature(ModelAPI.ModelAPI_Feature):
38
39     ## The constructor.
40     def __init__(self):
41         ModelAPI.ModelAPI_Feature.__init__(self)
42         pass
43
44     @staticmethod
45     ## Export kind. Static.
46     def ID():
47         return "PublishToStudy"
48
49     ## Returns the kind of a feature.
50     def getKind(self):
51         return PublishToStudyFeature.ID()
52
53     ## This feature is action: has no property panel and executes immediately.
54     def isAction(self):
55         return True
56
57     ## This feature has no attributes, as it is action.
58     def initAttributes(self):
59         pass
60
61     ## Exports all shapes and groups into the GEOM module.
62     def execute(self):
63         aSession = ModelAPI.ModelAPI_Session.get()
64         aPartSet = aSession.moduleDocument()
65         # check that the PartSet document current feature is the last to avoid problems with all
66         # features update
67         if aPartSet.size(model.ModelAPI_Feature.group()) > 0:
68           aLastFeature = ModelAPI.objectToFeature(aPartSet.object(model.ModelAPI_Feature.group(), aPartSet.size(model.ModelAPI_Feature.group()) - 1))
69           aCurrentFeature = aPartSet.currentFeature(True)
70           if aLastFeature.data().featureId() != aCurrentFeature.data().featureId():
71             EventsAPI.Events_InfoMessage("PublishToStudy", "Not all PartSet parts are up-to-date, nothing is published. Please, make the last PartSet feature as current.", self).send()
72             return
73         # find a shaper-study component
74         salome.salome_init(embedded=True)
75         aComponent = SHAPERSTUDY_utils.findOrCreateComponent()
76         anEngine = SHAPERSTUDY_utils.getEngine()
77         # collect all processed internal entries to break the link of unprocessed later
78         allProcessed = []
79
80         # iterate all parts and all results to publish them in SHAPER_STUDY
81         for aPartId in range(aPartSet.size(model.ModelAPI_ResultPart.group())):
82           aPartObject = aPartSet.object(model.ModelAPI_ResultPart.group(), aPartId)
83           aPartRes = ModelAPI.modelAPI_ResultPart(ModelAPI.modelAPI_Result(aPartObject))
84           aPartDoc = aPartRes.partDoc()
85           if aPartDoc is None and aPartObject is not None:
86             EventsAPI.Events_InfoMessage("PublishToStudy", "To publish to SHAPER-STUDY, activate Parts in SHAPER", self).send()
87             break
88           aPartFeatureId = aPartSet.feature(aPartRes.original()).data().featureId()
89           # Collects all features of exported results to find results of the same features and extend id.
90           # Map from feature index to index of result. If index is zero (initial), no surrfix to entry is added.
91           aFeaturesIndices = {}
92           for aResId in range(aPartDoc.size(model.ModelAPI_ResultBody.group())):
93             aResObject = aPartDoc.object(model.ModelAPI_ResultBody.group(), aResId)
94             aRes = model.objectToResult(aResObject)
95             #do not export images
96             if aRes.hasTexture() is True:
97               continue
98             aResFeatureId = str(aPartDoc.feature(aRes).data().featureId())
99             if aResFeatureId in aFeaturesIndices:
100               aFeaturesIndices[aResFeatureId] += 1
101               aResFeatureId += ":" + str(aFeaturesIndices[aResFeatureId])
102             else:
103               aFeaturesIndices[aResFeatureId] = 0
104             aSSEntry = str(aPartFeatureId) + ":" + aResFeatureId
105             aSShape = anEngine.FindOrCreateShape(aSSEntry)
106             aSShape.SetShapeByStream(aRes.shape().getShapeStream(False))
107             if not aSShape.GetSO(): # publish in case it is a new shape
108               anEngine.AddInStudy(aSShape, aRes.data().name(), None)
109             else: # restore a red reference if it was deleted
110               aDone, aSO2 = aSShape.GetSO().FindSubObject(1)
111               if aDone:
112                 aBuilder = SHAPERSTUDY_utils.getStudy().NewBuilder()
113                 aBuilder.SetName(aSShape.GetSO(),aRes.data().name())
114                 aDone, aRef = aSO2.ReferencedObject()
115                 if not aDone:
116                   aBuilder.Addreference(aSO2, aSShape.GetSO())
117             allProcessed.append(aSSEntry)
118             # Groups
119             self.processGroups(aRes, anEngine, aPartFeatureId, aSShape, False)
120             # Fields
121             self.processGroups(aRes, anEngine, aPartFeatureId, aSShape, True)
122
123         # process all SHAPER-STUDY shapes to find dead
124         aSOIter = SHAPERSTUDY_utils.getStudy().NewChildIterator(aComponent)
125         while aSOIter.More():
126           aSO = aSOIter.Value()
127           aSOIter.Next() # here because there is continue inside the loop
128           anIOR = aSO.GetIOR()
129           if len(anIOR):
130             anObj = salome.orb.string_to_object(anIOR)
131             if isinstance(anObj, SHAPERSTUDY_ORB._objref_SHAPER_Object):
132               anEntry = anObj.GetEntry()
133               if len(anEntry) == 0:
134                 continue
135               elif anEntry not in allProcessed: # found a removed shape: make it dead for the moment
136                 # remove the reference - red node
137                 aRes, aSO2 = aSO.FindSubObject(1)
138                 if aRes:
139                   aRes, aRef = aSO2.ReferencedObject()
140                   if aRes:
141                     aBuilder = SHAPERSTUDY_utils.getStudy().NewBuilder()
142                     aBuilder.RemoveReference(aSO2)
143                 # if the object is not marked as dead, mark it (#3201) to make all entries unique
144                 aDependancies = SHAPERSTUDY_utils.getStudy().FindDependances(aSO)
145                 hasLinkedMesh = False
146                 for aDep in aDependancies:
147                   aComp = aDep.GetFatherComponent()
148                   type = aComp.ComponentDataType()
149                   if type == "SMESH":
150                     hasLinkedMesh = True
151                     break
152                 if not hasLinkedMesh:
153                   aBuilder = SHAPERSTUDY_utils.getStudy().NewBuilder()
154                   aBuilder.RemoveObjectWithChildren(aSO)
155                 else:
156                   aDeadEntry = anObj.GetEntry()
157                   if not aDeadEntry.startswith("dead"):
158                     anIndex = aSO.Tag()
159                     anObj.SetEntry("dead0" + str(anIndex) + "_" + aDeadEntry)
160                     anEngine.SetDeadPixmapToDeadObject(anObj) #set crossed pixmap to distinguish dead object in GUI
161                     # also for groups
162                     aGrSOIter = SHAPERSTUDY_utils.getStudy().NewChildIterator(aSO)
163                     while aGrSOIter.More():
164                       aGroupSO = aGrSOIter.Value()
165                       aGrSOIter.Next()
166                       anIOR = aGroupSO.GetIOR()
167                       if len(anIOR):
168                         aGroup = salome.orb.string_to_object(anIOR)
169                         if isinstance(aGroup, SHAPERSTUDY_ORB._objref_SHAPER_Group) or \
170                           isinstance(aGroup, SHAPERSTUDY_ORB._objref_SHAPER_Field):
171                           if not aGroup.GetEntry().startswith("dead"):
172                             aDeadGroupEntry = "dead0" + str(anIndex) + "_" + aGroup.GetEntry()
173                             aGroup.SetEntry(aDeadGroupEntry)
174                             anEngine.SetDeadPixmapToDeadObject(aGroup) #set crossed pixmap to distinguish dead object in GUI
175
176     # Part of the "execute" method: processes the Groups of theRes result publication.
177     # If theFields is true, the same is performed for Fields.
178     def processGroups(self, theRes, theEngine, thePartFeatureId, theStudyShape, theFields):
179       allGroupsProcessed = []
180       allRefGroups = []
181       if theFields:
182         allRefGroups.append(ModelAPI.referencedFeatures(theRes, "Field", True))
183       else:
184         allRefGroups.append(ModelAPI.referencedFeatures(theRes, "Group", True))
185         allRefGroups.append(ModelAPI.referencedFeatures(theRes, "Shared_faces", True))
186       aResShape = theRes.shape()
187       aMapOfShape = GeomAPI_IndexedMapOfShape(aResShape)
188       for aRefGroups in allRefGroups:
189         for aRef in aRefGroups:
190           aGroupIndices = []
191           aGroupHasIndex = {}
192           if theFields:
193             aSelList = aRef.selectionList("selected")
194           else:
195             aSelList = aRef.selectionList("group_list")
196           aSelType = GeomAPI_Shape.shapeTypeByStr(aSelList.selectionType())
197           for aGroupRes in aRef.results():
198             aShape = aGroupRes.shape()
199             anExplorer = GeomAPI_ShapeExplorer(aShape, aSelType)
200             while anExplorer.more():
201               anId = aMapOfShape.FindIndexEqualLocations(anExplorer.current())
202               if anId > 0 and not anId in aGroupHasIndex:
203                 aGroupIndices.append(anId)
204                 aGroupHasIndex[anId] = 0
205               anExplorer.next()
206           if len(aGroupIndices): # create group
207             aGroupFeatureId = aRef.data().featureId()
208             if theFields:
209               aFieldOp = theEngine.GetIFieldOperations()
210               aGroupEntry = "field" + str(thePartFeatureId) + ":" + str(aGroupFeatureId)
211               aGroup = aFieldOp.FindField(theStudyShape, aGroupEntry)
212             else:
213               aGroupOp = theEngine.GetIGroupOperations()
214               aGroupEntry = "group" + str(thePartFeatureId) + ":" + str(aGroupFeatureId)
215               aGroup = aGroupOp.FindGroup(theStudyShape, aGroupEntry)
216             if not aGroup: # create a new
217               if theFields:
218                 aGroup = aFieldOp.CreateFieldByType(theStudyShape, aSelType)
219               else:
220                 aGroup = aGroupOp.CreateGroup(theStudyShape, aSelType)
221               aGroup.SetEntry(aGroupEntry)
222               theEngine.AddInStudy(aGroup, aRef.firstResult().data().name(), theStudyShape)
223             else:
224               aBuilder = SHAPERSTUDY_utils.getStudy().NewBuilder()
225               aBuilder.SetName(aGroup.GetSO(),aRef.firstResult().data().name())
226             aGroup.SetSelection(aGroupIndices)
227             if theFields:
228               self.fillField(aGroup, aRef, theEngine, aGroupIndices)
229             # a group takes shape from the main result
230             #aGroup.SetShapeByStream(aRef.firstResult().shape().getShapeStream(False)) # group shape
231             allGroupsProcessed.append(aGroupEntry)
232       # check all existing groups: if some does not processed, remove it from the tree
233       aSOIter = SHAPERSTUDY_utils.getStudy().NewChildIterator(theStudyShape.GetSO())
234       while aSOIter.More():
235         aSO = aSOIter.Value()
236         anIOR = aSO.GetIOR()
237         if len(anIOR):
238           anObj = salome.orb.string_to_object(anIOR)
239           if (theFields and isinstance(anObj, SHAPERSTUDY_ORB._objref_SHAPER_Field)) or \
240              (not theFields and type(anObj) == SHAPERSTUDY_ORB._objref_SHAPER_Group):
241             anEntry = anObj.GetEntry()
242             if anEntry not in allGroupsProcessed: # found a removed group => remove
243               aDependancies = SHAPERSTUDY_utils.getStudy().FindDependances(aSO)
244               hasLinkedMesh = False
245               for aDep in aDependancies:
246                 aComp = aDep.GetFatherComponent()
247                 typeComp = aComp.ComponentDataType()
248                 if typeComp == "SMESH":
249                   hasLinkedMesh = True
250                   break
251               if hasLinkedMesh:
252                 if not anEntry.startswith("dead"):
253                   anIndex = aSO.Tag()
254                   aDeadGroupEntry = "dead0" + str(anIndex) + "_" + anEntry
255                   anObj.SetEntry(aDeadGroupEntry)
256                   theEngine.SetDeadPixmapToDeadObject(anObj)
257               else:
258                 aBuilder = SHAPERSTUDY_utils.getStudy().NewBuilder()
259                 aBuilder.RemoveObject(anObj.GetSO())
260         aSOIter.Next()
261
262     # Part of the "execute" method: theFiled fields filling.
263     def fillField(self, theField, theFeature, theEngine, theSelectionIndices):
264       aTables = theFeature.tables("values")
265       aValType = aTables.type() # type of the values
266       aValTypeToSet = aValType
267       if aValType == 3: # strings do not supported by SMESH, so, make them empty boolean table
268         aValTypeToSet = 0
269       theField.SetValuesType(aValTypeToSet)
270       aNumSteps = aTables.tables() # number of steps is number of tables
271       aSteps = []
272       for aVal in range(aNumSteps):
273         aSteps.append(aVal + 1)
274       theField.SetSteps(aSteps)
275       aCompNames = []
276       aCompNamesAttr = theFeature.stringArray("components_names")
277       for anIndex in range(aCompNamesAttr.size()):
278         aCompNames.append(aCompNamesAttr.value(anIndex))
279       theField.SetComponents(aCompNames)
280       # prepare the sub-shapes indices: all values for all sub-shapes must be defined
281       aShapeOp = theEngine.GetIShapesOperations()
282       allIndices = aShapeOp.GetAllSubShapesIDs(theField.GetShape(), theField.GetSelectionType(), False)
283       # define steps
284       theField.ClearFieldSteps()
285       for aVal in range(aNumSteps):
286         aVals = []
287         for aCol in range(aTables.columns()):
288           #for aRow in range(aTables.rows()):
289           for anIndex in allIndices:
290             if anIndex in theSelectionIndices:
291               aRow = theSelectionIndices.index(anIndex) + 1 # starting from the first line
292             else:
293               aRow = 0 # default value
294             aStrVal = aTables.valueStr(aRow, aCol, aVal)
295             if aValType == 0: # boolean
296               if aStrVal == "True":
297                 aVals.append(1)
298               else:
299                 aVals.append(0)
300             elif aValType == 1: # int
301               aVals.append(int(aStrVal))
302             elif aValType == 2: # double
303               aVals.append(float(aStrVal))
304         theField.AddFieldStep(theFeature.intArray("stamps").value(aVal), aVal + 1, aVals)