From: Christophe Bourcier Date: Fri, 1 Dec 2017 15:07:31 +0000 (+0100) Subject: Implement ExportToGEOM via exportToXAO. Fix the issue #2198. X-Git-Tag: V_2.10.0RC~112^2~4 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=82beaf3018c2932ab40753f2ef7f0834b4ab43b5;p=modules%2Fshaper.git Implement ExportToGEOM via exportToXAO. Fix the issue #2198. --- diff --git a/src/ConnectorAPI/CMakeLists.txt b/src/ConnectorAPI/CMakeLists.txt index 67c29091a..99f15351a 100644 --- a/src/ConnectorAPI/CMakeLists.txt +++ b/src/ConnectorAPI/CMakeLists.txt @@ -88,9 +88,6 @@ INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/ConnectorAPI.py DESTINATION ${SHAPER_I # Tests IF(${HAVE_SALOME}) - INCLUDE(UnitTest) - - ADD_UNIT_TESTS( - TestExportToGEOM.py - ) + ENABLE_TESTING() + ADD_SUBDIRECTORY(Test) ENDIF(${HAVE_SALOME}) diff --git a/src/ConnectorAPI/Test/TestExportToGEOM.py b/src/ConnectorAPI/Test/TestExportToGEOM.py index 90539fff4..0cf34a081 100644 --- a/src/ConnectorAPI/Test/TestExportToGEOM.py +++ b/src/ConnectorAPI/Test/TestExportToGEOM.py @@ -18,30 +18,125 @@ ## email : webmaster.salome@opencascade.com ## +import salome from salome.shaper import model -model.begin() -partSet = model.moduleDocument() -Part_1 = model.addPart(partSet) -Part_1_doc = Part_1.document() -Sketch_1 = model.addSketch(Part_1_doc, model.defaultPlane("XOY")) -SketchLine_1 = Sketch_1.addLine(0, 0, 0, 50) -SketchLine_2 = Sketch_1.addLine(0, 50, 50, 50) -SketchConstraintCoincidence_1 = Sketch_1.setCoincident(SketchLine_1.endPoint(), SketchLine_2.startPoint()) -SketchLine_3 = Sketch_1.addLine(50, 50, 50, 0) -SketchConstraintCoincidence_2 = Sketch_1.setCoincident(SketchLine_2.endPoint(), SketchLine_3.startPoint()) -SketchLine_4 = Sketch_1.addLine(50, 0, 0, 0) -SketchConstraintCoincidence_3 = Sketch_1.setCoincident(SketchLine_3.endPoint(), SketchLine_4.startPoint()) -SketchConstraintCoincidence_4 = Sketch_1.setCoincident(SketchLine_4.endPoint(), SketchLine_1.startPoint()) -SketchConstraintRigid_1 = Sketch_1.setFixed(SketchLine_1.startPoint()) -SketchConstraintVertical_1 = Sketch_1.setVertical(SketchLine_1.result()) -SketchConstraintLength_1 = Sketch_1.setLength(SketchLine_4.result(), 50) -SketchConstraintLength_2 = Sketch_1.setLength(SketchLine_1.result(), 50) -SketchConstraintParallel_1 = Sketch_1.setParallel(SketchLine_1.result(), SketchLine_3.result()) -SketchConstraintParallel_2 = Sketch_1.setParallel(SketchLine_2.result(), SketchLine_4.result()) -SketchConstraintPerpendicular_1 = Sketch_1.setPerpendicular(SketchLine_1.result(), SketchLine_4.result()) -model.do() -Extrusion_1 = model.addExtrusion(Part_1_doc, [model.selection("FACE", "Sketch_1/Face-SketchLine_1r-SketchLine_2r-SketchLine_3r-SketchLine_4r")], model.selection(), 50, 0) -model.end() - -model.exportToGEOM(Part_1_doc) +from salome.geom import geomBuilder + +import os +import tempfile + +salome.salome_init(0,1) +geompy = geomBuilder.New(salome.myStudy) + +## Get the last object published in the GEOM section of the object browser +def getLastGEOMShape(): + sb = salome.myStudy.NewBuilder() + comp = salome.myStudy.FindComponent("GEOM") + obj = None + if comp: + iterator = salome.myStudy.NewChildIterator( comp ) + sobj = None + while iterator.More(): + sobj = iterator.Value() + iterator.Next() + if sobj: + obj = sobj.GetObject() + else: + raise Exception("GEOM component not found.") + return obj + +## Get the sub-object i of an object in the object browser +# Numerotation starts at 1 +def getSubObject(obj, i): + ok, sub_sobj = salome.ObjectToSObject(obj).FindSubObject(i) + if not ok: + raise Exception("No child found at %i for %s"%(i, obj.GetName())) + sub_obj = sub_sobj.GetObject() + return sub_obj + +def dumpShaper(fileName): + model.begin() + dump=model.moduleDocument().addFeature("Dump") + dump.string("file_path").setValue(fileName) + dump.string("file_format").setValue("py") + model.do() + model.end() + pass + +def getTmpFileName(ext): + tempdir = tempfile.gettempdir() + tmp_file = tempfile.NamedTemporaryFile(suffix=".%s"%ext, prefix='shaper_', dir=tempdir, delete=False) + tmp_filename = tmp_file.name + return tmp_filename + +# Create 2 boxes +# Create a group of faces +# Create a field of faces +# exportToGEOM +# Check the result +# Check the dump +def testSeveralExportsToGEOM(): + + model.begin() + partSet = model.moduleDocument() + Part_1 = model.addPart(partSet) + Part_1_doc = Part_1.document() + Box_1 = model.addBox(Part_1_doc, 10, 10, 10) + Box_2 = model.addBox(Part_1_doc, 20, 20, 20) + Translation_1 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Box_1_1")], model.selection("EDGE", "PartSet/OX"), -10) + Partition_1 = model.addPartition(Part_1_doc, [model.selection("SOLID", "Translation_1_1"), model.selection("SOLID", "Box_2_1")]) + Group_1 = model.addGroup(Part_1_doc, [model.selection("FACE", "Partition_1_1_1/Modified_Face_1_1"), model.selection("FACE", "Box_2_1/Top")]) + model.do() + model.end() + + # First export to GEOM + model.exportToGEOM(Part_1_doc) + + # Check that the GEOM object has 1 compsolid and 2 solids + geomObject_1 = getLastGEOMShape() + assert geompy.NumberOfSubShapes(geomObject_1, geompy.ShapeType["COMPSOLID"]) == 1 + assert geompy.NumberOfSolids(geomObject_1) == 2 + + # Check that the group has 2 faces + geomGroup_1 = getSubObject(geomObject_1, 1) + assert geompy.NumberOfFaces(geomGroup_1) == 2 + + # Add a third box + Box_3 = model.addBox(Part_1_doc, 10, 10, 10) + Translation_2 = model.addTranslation(Part_1_doc, [model.selection("SOLID", "Box_3_1")], model.selection("EDGE", "PartSet/OX"), 20) + + # Second export to GEOM + model.exportToGEOM(Part_1_doc) + + # Check that the GEOM object has 3 solids + geomObject_2 = getLastGEOMShape() + assert geompy.NumberOfSolids(geomObject_2) == 3 + + # Dump the salome study (only CORBA modules, SHAPER dump is not in it) + tempdir = tempfile.gettempdir() + dumpFileGeomBase = "dump_test_geom" + dumpFileGeom = os.path.join(tempdir, "%s.py"%dumpFileGeomBase) + salome.myStudy.DumpStudy(tempdir, dumpFileGeomBase, True, False) + + # Dump SHAPER + dumpFileShaper = os.path.join(tempdir, "dump_test_shaper.py") + dumpShaper(dumpFileShaper) + + # Load SHAPER dump + execfile(dumpFileShaper) + + # Load GEOM dump + execfile(dumpFileGeom) + + # Clean files + files = [dumpFileGeom, dumpFileShaper] + for f in files: + os.remove(f) + + pass + + + +if __name__ == '__main__': + testSeveralExportsToGEOM() \ No newline at end of file diff --git a/src/ConnectorPlugin/ConnectorPlugin_ExportFeature.py b/src/ConnectorPlugin/ConnectorPlugin_ExportFeature.py index aaaab43c6..b5d42602b 100644 --- a/src/ConnectorPlugin/ConnectorPlugin_ExportFeature.py +++ b/src/ConnectorPlugin/ConnectorPlugin_ExportFeature.py @@ -21,23 +21,22 @@ ## @package Plugins # ExportFeature class definition -import EventsAPI import ModelAPI -import GeomAPI -import GeomAlgoAPI +import ExchangeAPI import salome from salome.geom import geomBuilder -def getObjectIndex(theName): - aStudy = salome.myStudy - aId = 0 - aObj = aStudy.FindObjectByName(theName, "GEOM") - while len(aObj) != 0: - aId = aId + 1 - aName = theName + '_' + str(aId) - aObj = aStudy.FindObjectByName(aName, "GEOM") - return aId +from salome.shaper import model + +import os + +def getTmpFileName(ext): + import tempfile + tempdir = tempfile.gettempdir() + tmp_file = tempfile.NamedTemporaryFile(suffix=".%s"%ext, prefix='shaper_', dir=tempdir, delete=False) + tmp_filename = tmp_file.name + return tmp_filename ## @ingroup Plugins # Feature to export all shapes and groups into the GEOM module @@ -46,10 +45,7 @@ class ExportFeature(ModelAPI.ModelAPI_Feature): ## The constructor. def __init__(self): ModelAPI.ModelAPI_Feature.__init__(self) - ## Shape that will be exported (the compound if there are several exported bodies) - self.shape = None - ## BRep representation of the exported shape (a stream that will be sent to GEOM and converted to GEOM object) - self.brep = None + pass @staticmethod ## Export kind. Static. @@ -60,7 +56,7 @@ class ExportFeature(ModelAPI.ModelAPI_Feature): def getKind(self): return ExportFeature.ID() - ## This feature is action: has no property pannel and executes immideately. + ## This feature is action: has no property pannel and executes immediately. def isAction(self): return True @@ -68,222 +64,35 @@ class ExportFeature(ModelAPI.ModelAPI_Feature): def initAttributes(self): pass - ## Exports all bodies - def exportBodies(self): - global ShapeIndex - kResultBodyType = "Bodies" - aPartSize = self.Part.size(kResultBodyType) - if aPartSize == 0: - EventsAPI.Events_InfoMessage("ExportFeature","No results in the active document").send() + ## Export the results, groups and fields via XAO + def exportViaXAO(self): + tmpXAOFile = getTmpFileName("xao") + self.tmpXAOFile = tmpXAOFile + #print "Export to %s"%tmpXAOFile + exportXAO = ExchangeAPI.exportToXAO(self.Part, tmpXAOFile, "automatic_shaper_export_to_XAO") + if not os.path.exists(tmpXAOFile) or os.stat(tmpXAOFile).st_size == 0: + exportXAO.feature().setError("Error in exportToXAO. No XAO file has been created.") return - - anObjList = [self.Part.object(kResultBodyType, idx) for idx in xrange(aPartSize)] - aShapesList = GeomAlgoAPI.ShapeList() - aName = "" - for idx, anObject in enumerate(anObjList): - aResult = ModelAPI.modelAPI_Result(anObject) - aBodyResult = ModelAPI.modelAPI_ResultBody(aResult) - if not aBodyResult: - continue - aShape = aBodyResult.shape() - if aShape is not None and not aShape.isNull(): - aShapesList.append(aShape) - if len(aShapesList) == 1: - aName = aBodyResult.data().name() - - # issue 1045: create compound if there are more than one shape - if len(aShapesList) > 1: - self.shape = GeomAlgoAPI.GeomAlgoAPI_CompoundBuilder.compound(aShapesList) - aName = "ShaperResults" - elif len(aShapesList) == 1: - self.shape = aShapesList[0] - - # so, only one shape is always in the result - aDump = self.shape.getShapeStream() - # Load shape to SALOME Geom - aBrep = self.geompy.RestoreShape(aDump) - - # Make unique name - aId = getObjectIndex(aName) - if aId != 0: - aName = aName + '_' + str(aId) - - self.geompy.addToStudy(aBrep, aName) - self.brep = aBrep - - ## Exports all groups - def exportGroups(self): - # iterate all features to find groups - aFeaturesNum = self.Part.size("Features") - groupIndex = 0 - for anIndex in range(0, aFeaturesNum): - aFeature = self.Part.object("Features", anIndex) - aSelectionList = aFeature.data().selectionList("group_list") - # if a group has been found - if aSelectionList: - aFeature = ModelAPI.objectToFeature(aFeature) - if aFeature.firstResult() is not None: - aName = aFeature.firstResult().data().name() - groupIndex = groupIndex + 1 - self.createGroupFromList(aSelectionList, aName) - - ## Returns a type of the shape in the old GEOM representation - def shapeType(self, shape): - if shape.isVertex(): - return "VERTEX" - elif shape.isEdge(): - return "EDGE" - elif shape.isFace(): - return "FACE" - - return "SOLID" - - ## Creates a group by given list of selected objects and the name - # @param theSelectionList: list of selected objects - # @param theGroupName: name of the group to create - def createGroupFromList(self, theSelectionList, theGroupName): - # iterate on all selected entities of the group - # and get the corresponding ID - aSelectionNum = theSelectionList.size() - Ids = [] - groupType = "" - for aSelIndex in range(0, aSelectionNum): - aSelection = theSelectionList.value(aSelIndex) - # issue 1326: bodies that are already concealed did not exported, so groups should not be invalid - aContext = ModelAPI.modelAPI_Result(aSelection.context()) - # chcking of concealment removed because of #1799, remark #13 "aContext.isConcealed()" - if aContext is None or aContext.isDisabled(): - continue - - anID = GeomAlgoAPI.GeomAlgoAPI_CompoundBuilder.id(self.shape, aSelection.value()) - if anID == 0: - #it may be a compound of objects if movement of the group to the end - # splits the original element to several (issue 1146) - anExp = GeomAPI.GeomAPI_ShapeExplorer(aSelection.value(), GeomAPI.GeomAPI_Shape.SHAPE) - while anExp.more(): - anID = GeomAlgoAPI.GeomAlgoAPI_CompoundBuilder.id(self.shape, anExp.current()) - if anID != 0: - Ids.append(anID) - groupType = self.shapeType(anExp.current()) - anExp.next() - else: - Ids.append(anID) - groupType = self.shapeType(aSelection.value()) - - if len(Ids) <> 0: - aGroup = self.geompy.CreateGroup(self.brep, self.geompy.ShapeType[groupType]) - self.geompy.UnionIDs(aGroup,Ids) - self.geompy.addToStudyInFather(self.brep, aGroup, theGroupName) - - ## Exports all fields - def exportFields(self): - # iterate all features to find fields - aFeaturesNum = self.Part.size("Features") - fieldIndex = 0 - for anIndex in range(0, aFeaturesNum): - aFeature = self.Part.object("Features", anIndex) - aSelectionList = aFeature.data().selectionList("selected") - # if a field has been found - if aSelectionList: - aFeature = ModelAPI.objectToFeature(aFeature) - if aFeature.firstResult() is not None: - aName = aFeature.firstResult().data().name() - fieldIndex = fieldIndex + 1 - self.createFieldFromFeature(aFeature, aName) - - ## Returns a type of the shape type in the old GEOM representation - def selectionDim(self, theSelectionType): - selType=theSelectionType.lower() # more or less independed approach - if selType== "vertex": - return 0 - if selType== "edge": - return 1 - if selType== "face": - return 2 - if selType== "solid": - return 3 - return -1 - - ## Returns a type of the shape type in the GeomAPI_Shape representation - def geomAPISelectionDim(self, theSelectionType): - selType=theSelectionType.lower() # more or less independed approach - if selType== "vertex": - return GeomAPI.GeomAPI_Shape.VERTEX - if selType== "edge": - return GeomAPI.GeomAPI_Shape.EDGE - if selType== "face": - return GeomAPI.GeomAPI_Shape.FACE - if selType== "solid": - return GeomAPI.GeomAPI_Shape.SOLID - return GeomAPI.GeomAPI_Shape.SHAPE - - ## Creates a field by the field feature and the name - # @param theField: the field feature - # @param theFieldName: name of the field to create - def createFieldFromFeature(self, theField, theFieldName): - # iterate on all selected entities of the field - # and get the corresponding ID - aTables = theField.tables("values") - aSelection = theField.selectionList("selected") - - # set component names - aComps = theField.stringArray("components_names") - aCompNames = [] - aCompNum = aComps.size() - for aCompIndex in range(0, aCompNum): - aCompNames.append(aComps.value(aCompIndex)) - - #if len(Ids) <> 0: - aDim = self.selectionDim(aSelection.selectionType()) - aResField = self.geompy.CreateField(self.brep, theFieldName, aTables.type(), aDim, aCompNames) - #self.geompy.UnionIDs(theField,Ids) - self.geompy.addToStudyInFather(self.brep, aResField, theFieldName) - - # set default values to all not filled sub-shapes (fields in GEOM support only full set of subs) - Ids={} - anExp = GeomAPI.GeomAPI_ShapeExplorer(self.shape, self.geomAPISelectionDim(aSelection.selectionType())) - while anExp.more(): - anID = GeomAlgoAPI.GeomAlgoAPI_CompoundBuilder.id(self.shape, anExp.current()) - if anID != 0: - Ids[anID]=anExp.current() - anExp.next() - - SelectedIds={} - for aSelIndex in range(aSelection.size()): - selShape = aSelection.value(aSelIndex).value() - # searching for this shape in Ids - for a in Ids.items(): - if (a[1].isSame(selShape)): - SelectedIds[a[0]] = aSelIndex - - # values here are in the same order as in field - listOfValues = Ids.items() - listOfValues.sort() - # set steps - aStepsNum = aTables.tables() - for aStepIndex in range(0, aStepsNum): - aStamp = theField.intArray("stamps").value(aStepIndex) - aValues = [] - for aValId in listOfValues: - aRow = 0 # default value if not from selection - if SelectedIds.has_key(aValId[0]): # take the value from the table - aRow = SelectedIds[aValId[0]] + 1 # plus one to avoid default string - aCols = aTables.columns() - for aCol in range(0, aCols): - aVal = aTables.valueStr(aRow, aCol, aStepIndex) - if aTables.type() == 0: # bool - if aVal == "True": - aVal = True - else: - aVal = False - elif aTables.type() == 1: # int - aVal = int(aVal) - elif aTables.type() == 2: # double - aVal = float(aVal) - aValues.append(aVal) - aStep = aResField.addStep(aStepIndex + 1, aStamp, aValues) - if aStep: - self.geompy.addToStudyInFather( aResField, aStep, aStep.GetName() ) + imported, shape, subShapes, groups, fields = self.geompy.ImportXAO(tmpXAOFile) + self.geompy.addToStudy( shape, shape.GetName() ) + # add sub-shapes and groups to the object browser + for obj in subShapes + groups: + name = obj.GetName() + self.geompy.addToStudyInFather(shape, obj, name) + # add fields to the object browser + for field in fields: + name = field.GetName() + self.geompy.addToStudyInFather(shape, field, name) + # add steps to the object browser + steps = field.getSteps() + for i_step in steps: + step = field.getStep(i_step) + i_stamp = step.GetStamp() + step_name = "Step %i %i"%(i_step, i_stamp) + self.geompy.addToStudyInFather( field, step, step_name ) + # Remove the temporary file + os.remove(tmpXAOFile) + pass ## Exports all shapes and groups into the GEOM module. def execute(self): @@ -296,8 +105,6 @@ class ExportFeature(ModelAPI.ModelAPI_Feature): salome.salome_init(0,1) self.geompy = geomBuilder.New(salome.myStudy) - # Export bodies and groups - self.exportBodies() - self.exportGroups() - self.exportFields() + self.exportViaXAO() + pass diff --git a/src/ExchangeAPI/ExchangeAPI_Export.cpp b/src/ExchangeAPI/ExchangeAPI_Export.cpp index 9a49e0b64..71666dca3 100644 --- a/src/ExchangeAPI/ExchangeAPI_Export.cpp +++ b/src/ExchangeAPI/ExchangeAPI_Export.cpp @@ -20,37 +20,111 @@ #include "ExchangeAPI_Export.h" //-------------------------------------------------------------------------------------- +#include +#include #include +#include +#include //-------------------------------------------------------------------------------------- -void exportToFile(const std::shared_ptr & thePart, + +ExchangeAPI_Export::ExchangeAPI_Export(const std::shared_ptr& theFeature) +: ModelHighAPI_Interface(theFeature) +{ + initialize(); +} + +/// Constructor with values for XAO export. +ExchangeAPI_Export::ExchangeAPI_Export(const std::shared_ptr& theFeature, + const std::string & theFilePath, + const std::string & theAuthor, + const std::string & theGeometryName) +: ModelHighAPI_Interface(theFeature) +{ + initialize(); + fillAttribute("XAO", theFeature->string(ExchangePlugin_ExportFeature::EXPORT_TYPE_ID())); + fillAttribute(theFilePath, theFeature->string(ExchangePlugin_ExportFeature::XAO_FILE_PATH_ID())); + fillAttribute(theAuthor, theFeature->string(ExchangePlugin_ExportFeature::XAO_AUTHOR_ID())); + fillAttribute(theGeometryName, + theFeature->string(ExchangePlugin_ExportFeature::XAO_GEOMETRY_NAME_ID())); + fillAttribute("XAO", theFeature->string(ExchangePlugin_ExportFeature::FILE_FORMAT_ID())); + execute(); + apply(); // finish operation to make sure the export is done on the current state of the history +} + +/// Constructor with values for export in other formats than XAO. +ExchangeAPI_Export::ExchangeAPI_Export(const std::shared_ptr& theFeature, + const std::string & theFilePath, + const std::list & theSelectionList, + const std::string & theFileFormat) +: ModelHighAPI_Interface(theFeature) +{ + initialize(); + fillAttribute("Regular", theFeature->string(ExchangePlugin_ExportFeature::EXPORT_TYPE_ID())); + fillAttribute(theFilePath, theFeature->string(ExchangePlugin_ExportFeature::FILE_PATH_ID())); + fillAttribute(theSelectionList, + theFeature->selectionList(ExchangePlugin_ExportFeature::SELECTION_LIST_ID())); + fillAttribute(theFileFormat, theFeature->string(ExchangePlugin_ExportFeature::FILE_FORMAT_ID())); + execute(); + apply(); // finish operation to make sure the export is done on the current state of the history +} + +ExchangeAPI_Export::~ExchangeAPI_Export() +{ +} + + +void ExchangeAPI_Export::dump(ModelHighAPI_Dumper& theDumper) const +{ + + FeaturePtr aBase = feature(); + const std::string& aDocName = theDumper.name(aBase->document()); + + theDumper << aBase << " = model."; + + std::string exportType = aBase->string(ExchangePlugin_ExportFeature::EXPORT_TYPE_ID())->value(); + + if (exportType == "XAO") { + std::string tmpXAOFile = aBase->string(ExchangePlugin_ExportFeature::XAO_FILE_PATH_ID())->value(); + theDumper << "exportToXAO(" << aDocName << ", '" << tmpXAOFile << "'" ; + std::string theAuthor = aBase->string(ExchangePlugin_ExportFeature::XAO_AUTHOR_ID())->value(); + if (not theAuthor.empty()) + theDumper << ", '" << theAuthor << "'"; + std::string theGeometryName = aBase->string(ExchangePlugin_ExportFeature::XAO_GEOMETRY_NAME_ID())->value(); + if (not theGeometryName.empty()) + theDumper << ", '" << theGeometryName << "'"; + theDumper << ")" << std::endl; + } + else { + theDumper << "exportToFile(" << aDocName << ", " << + aBase->string(ExchangePlugin_ExportFeature::FILE_PATH_ID()) << ", " << + aBase->selectionList(ExchangePlugin_ExportFeature::SELECTION_LIST_ID()) ; + std::string theFileFormat = aBase->string(ExchangePlugin_ExportFeature::FILE_FORMAT_ID())->value(); + if (not theFileFormat.empty()) + theDumper << ", '" << theFileFormat << "'"; + theDumper << ")" << std::endl; + } +} + +ExportPtr exportToFile(const std::shared_ptr & thePart, const std::string & theFilePath, const std::list & theSelectionList, const std::string & theFileFormat) { + apply(); // finish previous operation to make sure all previous operations are done std::shared_ptr aFeature = thePart->addFeature(ExchangePlugin_ExportFeature::ID()); - fillAttribute("Regular", aFeature->string(ExchangePlugin_ExportFeature::EXPORT_TYPE_ID())); - fillAttribute(theFilePath, aFeature->string(ExchangePlugin_ExportFeature::FILE_PATH_ID())); - fillAttribute(theSelectionList, - aFeature->selectionList(ExchangePlugin_ExportFeature::SELECTION_LIST_ID())); - fillAttribute(theFileFormat, aFeature->string(ExchangePlugin_ExportFeature::FILE_FORMAT_ID())); - aFeature->execute(); + return ExportPtr(new ExchangeAPI_Export(aFeature, theFilePath, theSelectionList, theFileFormat)); } -void exportToXAO(const std::shared_ptr & thePart, +ExportPtr exportToXAO(const std::shared_ptr & thePart, const std::string & theFilePath, const std::string & theAuthor, const std::string & theGeometryName) { + apply(); // finish previous operation to make sure all previous operations are done std::shared_ptr aFeature = thePart->addFeature(ExchangePlugin_ExportFeature::ID()); - fillAttribute("XAO", aFeature->string(ExchangePlugin_ExportFeature::EXPORT_TYPE_ID())); - fillAttribute(theFilePath, aFeature->string(ExchangePlugin_ExportFeature::XAO_FILE_PATH_ID())); - fillAttribute(theAuthor, aFeature->string(ExchangePlugin_ExportFeature::XAO_AUTHOR_ID())); - fillAttribute(theGeometryName, - aFeature->string(ExchangePlugin_ExportFeature::XAO_GEOMETRY_NAME_ID())); - fillAttribute("XAO", aFeature->string(ExchangePlugin_ExportFeature::FILE_FORMAT_ID())); - aFeature->execute(); + return ExportPtr(new ExchangeAPI_Export(aFeature, theFilePath, theAuthor, theGeometryName)); } //-------------------------------------------------------------------------------------- diff --git a/src/ExchangeAPI/ExchangeAPI_Export.h b/src/ExchangeAPI/ExchangeAPI_Export.h index f10bf831c..a4b314836 100644 --- a/src/ExchangeAPI/ExchangeAPI_Export.h +++ b/src/ExchangeAPI/ExchangeAPI_Export.h @@ -34,11 +34,65 @@ //-------------------------------------------------------------------------------------- class ModelHighAPI_Selection; //-------------------------------------------------------------------------------------- + + +/// \class ExchangeAPI_Export +/// \ingroup CPPHighAPI +/// \brief Interface for Export feature. +class ExchangeAPI_Export: public ModelHighAPI_Interface +{ +public: + /// Constructor without values. + EXCHANGEAPI_EXPORT + explicit ExchangeAPI_Export(const std::shared_ptr& theFeature); + + /// Constructor with values for XAO export. + EXCHANGEAPI_EXPORT + explicit ExchangeAPI_Export(const std::shared_ptr& theFeature, + const std::string & theFilePath, + const std::string & theAuthor = std::string(), + const std::string & theGeometryName = std::string()); + + /// Constructor with values for export in other formats than XAO. + EXCHANGEAPI_EXPORT + explicit ExchangeAPI_Export(const std::shared_ptr& theFeature, + const std::string & theFilePath, + const std::list & theSelectionList, + const std::string & theFileFormat = std::string()); + + /// Destructor. + EXCHANGEAPI_EXPORT + virtual ~ExchangeAPI_Export(); + + INTERFACE_7(ExchangePlugin_ExportFeature::ID(), + exportType, ExchangePlugin_ExportFeature::EXPORT_TYPE_ID(), + ModelAPI_AttributeString, /** ExportType */, + filePath, ExchangePlugin_ExportFeature::FILE_PATH_ID(), + ModelAPI_AttributeString, /** file path */, + xaoFilePath, ExchangePlugin_ExportFeature::XAO_FILE_PATH_ID(), + ModelAPI_AttributeString, /** xao_file_path */, + fileFormat, ExchangePlugin_ExportFeature::FILE_FORMAT_ID(), + ModelAPI_AttributeString, /** file format */, + selectionList, ExchangePlugin_ExportFeature::SELECTION_LIST_ID(), + ModelAPI_AttributeString, /** selection list */, + xaoAuthor, ExchangePlugin_ExportFeature::XAO_AUTHOR_ID(), + ModelAPI_AttributeString, /** xao author */, + xaoGeometryName, ExchangePlugin_ExportFeature::XAO_GEOMETRY_NAME_ID(), + ModelAPI_AttributeString, /** xao geometry name */) + + /// Dump wrapped feature + EXCHANGEAPI_EXPORT + virtual void dump(ModelHighAPI_Dumper& theDumper) const; +}; + +/// Pointer on Export object +typedef std::shared_ptr ExportPtr; + /**\ingroup CPPHighAPI * \brief Export to file */ EXCHANGEAPI_EXPORT -void exportToFile(const std::shared_ptr & thePart, +ExportPtr exportToFile(const std::shared_ptr & thePart, const std::string & theFilePath, const std::list & theSelectionList, const std::string & theFileFormat = std::string()); @@ -47,7 +101,7 @@ void exportToFile(const std::shared_ptr & thePart, * \brief Export XAO */ EXCHANGEAPI_EXPORT -void exportToXAO(const std::shared_ptr & thePart, +ExportPtr exportToXAO(const std::shared_ptr & thePart, const std::string & theFilePath, const std::string & theAuthor = std::string(), const std::string & theGeometryName = std::string()); diff --git a/src/ExchangePlugin/ExchangePlugin_ExportFeature.cpp b/src/ExchangePlugin/ExchangePlugin_ExportFeature.cpp index 0c78b27e1..8b7f59638 100644 --- a/src/ExchangePlugin/ExchangePlugin_ExportFeature.cpp +++ b/src/ExchangePlugin/ExchangePlugin_ExportFeature.cpp @@ -225,6 +225,7 @@ void ExchangePlugin_ExportFeature::exportXAO(const std::string& theFileName) // make shape for export from all results std::list aShapes; + std::list aResults; int aBodyCount = document()->size(ModelAPI_ResultBody::group()); for (int aBodyIndex = 0; aBodyIndex < aBodyCount; ++aBodyIndex) { ResultBodyPtr aResultBody = @@ -233,6 +234,7 @@ void ExchangePlugin_ExportFeature::exportXAO(const std::string& theFileName) if (!aResultBody.get()) continue; aShapes.push_back(aResultBody->shape()); + aResults.push_back(aResultBody); } GeomShapePtr aShape = (aShapes.size() == 1) ? *aShapes.begin() @@ -246,8 +248,13 @@ void ExchangePlugin_ExportFeature::exportXAO(const std::string& theFileName) } // geometry name - std::string aGeometryName = string(ExchangePlugin_ExportFeature::XAO_GEOMETRY_NAME_ID())->value(); + if (aGeometryName.empty() and aBodyCount == 1){ + // get the name from the first result + ResultBodyPtr aResultBody = *aResults.begin(); + aGeometryName = aResultBody->data()->name(); + } + aXao.getGeometry()->setName(aGeometryName); // groups @@ -276,7 +283,12 @@ void ExchangePlugin_ExportFeature::exportXAO(const std::string& theFileName) AttributeSelectionPtr aSelection = aSelectionList->value(aSelectionIndex); // complex conversion of reference id to element index - int aReferenceID = aSelection->Id(); + // gives bad id in case the selection is done from python script + // => using GeomAlgoAPI_CompoundBuilder::id instead + // int aReferenceID_old = aSelection->Id(); + + int aReferenceID = GeomAlgoAPI_CompoundBuilder::id(aShape, aSelection->value()); + std::string aReferenceString = XAO::XaoUtils::intToString(aReferenceID); int anElementID = aXao.getGeometry()->getElementIndexByReference(aGroupDimension, aReferenceString); @@ -335,7 +347,12 @@ void ExchangePlugin_ExportFeature::exportXAO(const std::string& theFileName) AttributeSelectionPtr aSelection = aSelectionList->value(aRow - 1); // complex conversion of reference id to element index - int aReferenceID = aSelection->Id(); + // gives bad id in case the selection is done from python script + // => using GeomAlgoAPI_CompoundBuilder::id instead + //int aReferenceID_old = aSelection->Id(); + + int aReferenceID = GeomAlgoAPI_CompoundBuilder::id(aShape, aSelection->value()); + std::string aReferenceString = XAO::XaoUtils::intToString(aReferenceID); anElementID = aXao.getGeometry()->getElementIndexByReference(aFieldDimension, aReferenceString); diff --git a/src/ExchangePlugin/ExchangePlugin_ExportFeature.h b/src/ExchangePlugin/ExchangePlugin_ExportFeature.h index 754198869..f0866181c 100644 --- a/src/ExchangePlugin/ExchangePlugin_ExportFeature.h +++ b/src/ExchangePlugin/ExchangePlugin_ExportFeature.h @@ -105,12 +105,17 @@ public: /// Computes or recomputes the results EXCHANGEPLUGIN_EXPORT virtual void execute(); - /// Reimplemented from ModelAPI_Feature::isMacro(). Returns true. - EXCHANGEPLUGIN_EXPORT virtual bool isMacro() const { return true; } + /// Reimplemented from ModelAPI_Feature::isMacro(). Returns false. + // Not a macro. Otherwise, the feature will be deleted after being executed + EXCHANGEPLUGIN_EXPORT virtual bool isMacro() const { return false; } /// Reimplemented from ModelAPI_Feature::isPreviewNeeded(). Returns false. EXCHANGEPLUGIN_EXPORT virtual bool isPreviewNeeded() const { return false; } + /// Do not put in history. + /// Since it is not a macro, it is not deleted, but we don't want to see it. + bool isInHistory() { return false; } + protected: /// Performs export of the file EXCHANGEPLUGIN_EXPORT void exportFile(const std::string& theFileName, diff --git a/src/ExchangePlugin/ExchangePlugin_Tools.cpp b/src/ExchangePlugin/ExchangePlugin_Tools.cpp index 786a59d52..e60c104e2 100644 --- a/src/ExchangePlugin/ExchangePlugin_Tools.cpp +++ b/src/ExchangePlugin/ExchangePlugin_Tools.cpp @@ -34,13 +34,15 @@ std::list ExchangePlugin_Tools::split(const std::string& theString, std::string ExchangePlugin_Tools::selectionType2xaoDimension(const std::string& theType) { - if (theType == "Vertices" || theType == "vertex") + // if the selection is done in the GUI, the type is lower case + // if the selection is done in python, the type is in upper case + if (theType == "Vertices" || theType == "vertex" || theType == "VERTEX") return "vertex"; - else if (theType == "Edges" || theType == "edge") + else if (theType == "Edges" || theType == "edge" || theType == "EDGE") return "edge"; - else if (theType == "Faces" || theType == "face") + else if (theType == "Faces" || theType == "face" || theType == "FACE") return "face"; - else if (theType == "Solids" || theType == "solid") + else if (theType == "Solids" || theType == "solid" || theType == "SOLID") return "solid"; else if (theType == "Part" || theType == "part") return "part"; diff --git a/src/ExchangePlugin/Test/Data/export_ref.xao b/src/ExchangePlugin/Test/Data/export_ref.xao index 0848d6714..566bbdf05 100644 --- a/src/ExchangePlugin/Test/Data/export_ref.xao +++ b/src/ExchangePlugin/Test/Data/export_ref.xao @@ -413,5 +413,40 @@ So - + + + + + + + + + + 2 + 5 + + + 3 + 6 + + + 1 + 4 + + + 1 + 4 + + + 1 + 4 + + + 1 + 4 + + + + + diff --git a/src/ExchangePlugin/Test/TestExport.py b/src/ExchangePlugin/Test/TestExport.py index d3f88a729..696a2b0c2 100644 --- a/src/ExchangePlugin/Test/TestExport.py +++ b/src/ExchangePlugin/Test/TestExport.py @@ -144,8 +144,8 @@ def testExportXAO(): aSession.finishOperation() # Check exported file -# import filecmp -# assert filecmp.cmp("Data/export.xao", "Data/export_ref.xao") + import filecmp + assert filecmp.cmp("Data/export.xao", "Data/export_ref.xao") if __name__ == '__main__': #========================================================================= diff --git a/src/PythonAPI/model/dump/DumpAssistant.py b/src/PythonAPI/model/dump/DumpAssistant.py index f56860b04..680ae82f4 100644 --- a/src/PythonAPI/model/dump/DumpAssistant.py +++ b/src/PythonAPI/model/dump/DumpAssistant.py @@ -61,8 +61,9 @@ class DumpAssistant(ModelHighAPI.ModelHighAPI_Dumper): aFeatureKind = theFeature.getKind() if aFeatureKind in self.myFeatures: # Dump only feature created by user (in history). + # Also dump Export features (hard-coded here in order not to change the data model). # For all other features, just keep their name. - if theForce or theFeature.isInHistory(): + if theForce or theFeature.isInHistory() or aFeatureKind=="Export": self.myFeatures[aFeatureKind](theFeature).dump(self) else: self.name(theFeature)