From 844220fef6e3d006be674b807099bb55f7183c0b Mon Sep 17 00:00:00 2001 From: Christophe Bourcier Date: Tue, 5 Dec 2017 08:37:33 +0100 Subject: [PATCH] Merge remote-tracking branch 'origin/cbr/export_to_geom_via_xao' --- src/ConnectorAPI/CMakeLists.txt | 7 +- src/ConnectorAPI/Test/CMakeLists.txt | 59 ++++ .../Test/CTestTestfileInstall.cmake | 31 ++ src/ConnectorAPI/Test/TestExportToGEOM.py | 145 +++++++-- .../TestExportToGEOMAllGroupsAndFields.py | 186 ++++++++++++ src/ConnectorAPI/Test/testme.py | 56 ++++ src/ConnectorAPI/Test/tests.set | 23 ++ .../ConnectorPlugin_ExportFeature.py | 279 +++--------------- src/ExchangeAPI/ExchangeAPI_Export.cpp | 104 ++++++- src/ExchangeAPI/ExchangeAPI_Export.h | 58 +++- .../ExchangePlugin_ExportFeature.cpp | 23 +- .../ExchangePlugin_ExportFeature.h | 9 +- src/ExchangePlugin/Test/Data/export_ref.xao | 37 ++- src/ExchangePlugin/Test/TestExport.py | 4 +- src/PythonAPI/model/dump/DumpAssistant.py | 3 +- 15 files changed, 732 insertions(+), 292 deletions(-) create mode 100755 src/ConnectorAPI/Test/CMakeLists.txt create mode 100644 src/ConnectorAPI/Test/CTestTestfileInstall.cmake create mode 100644 src/ConnectorAPI/Test/TestExportToGEOMAllGroupsAndFields.py create mode 100755 src/ConnectorAPI/Test/testme.py create mode 100644 src/ConnectorAPI/Test/tests.set 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/CMakeLists.txt b/src/ConnectorAPI/Test/CMakeLists.txt new file mode 100755 index 000000000..1506027b2 --- /dev/null +++ b/src/ConnectorAPI/Test/CMakeLists.txt @@ -0,0 +1,59 @@ +# Copyright (C) 2013-2016 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +INCLUDE(tests.set) + +SET(COMPONENT_NAME SHAPER) + +SET(SALOME_SHAPER_INSTALL_TESTS "${SHAPER_INSTALL_PYTHON_FILES}/test") +SET(TEST_INSTALL_DIRECTORY ${SALOME_SHAPER_INSTALL_TESTS}) + +# make test +SET(SALOME_INSTALL_LIBS "lib/salome") +# Adding KERNEL and GUI libraries to environment so that Salome launches correctly +if (WIN32) + SALOME_ACCUMULATE_ENVIRONMENT(PATH NOCHECK ${KERNEL_ROOT_DIR}/${SALOME_INSTALL_LIBS}) + SALOME_ACCUMULATE_ENVIRONMENT(PATH NOCHECK ${GUI_ROOT_DIR}/${SALOME_INSTALL_LIBS}) +else() + SALOME_ACCUMULATE_ENVIRONMENT(LD_LIBRARY_PATH NOCHECK ${KERNEL_ROOT_DIR}/${SALOME_INSTALL_LIBS}) + SALOME_ACCUMULATE_ENVIRONMENT(LD_LIBRARY_PATH NOCHECK ${GUI_ROOT_DIR}/${SALOME_INSTALL_LIBS}) +endif() +SALOME_GENERATE_TESTS_ENVIRONMENT(tests_env) + + +# -B ${CMAKE_SOURCE_DIR}/doc/salome/examples/testme.py +FOREACH(tfile ${TEST_NAMES}) + SET(TEST_NAME ${COMPONENT_NAME}_${tfile}) + ADD_TEST(NAME ${TEST_NAME} + COMMAND ${PYTHON_EXECUTABLE} -B ${CMAKE_CURRENT_SOURCE_DIR}/testme.py ${CMAKE_CURRENT_SOURCE_DIR}/${tfile}.py) + SET_TESTS_PROPERTIES(${TEST_NAME} PROPERTIES ENVIRONMENT "${tests_env}") + SET_TESTS_PROPERTIES(${TEST_NAME} PROPERTIES LABELS "${COMPONENT_NAME}") +ENDFOREACH() + +# salome test +FOREACH(tfile ${TEST_NAMES}) + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/${tfile}.py + DESTINATION ${TEST_INSTALL_DIRECTORY}) +ENDFOREACH() + +INSTALL(FILES CTestTestfileInstall.cmake + DESTINATION ${TEST_INSTALL_DIRECTORY} + RENAME CTestTestfile.cmake) + +INSTALL(FILES tests.set DESTINATION ${TEST_INSTALL_DIRECTORY}) \ No newline at end of file diff --git a/src/ConnectorAPI/Test/CTestTestfileInstall.cmake b/src/ConnectorAPI/Test/CTestTestfileInstall.cmake new file mode 100644 index 000000000..92cde25ed --- /dev/null +++ b/src/ConnectorAPI/Test/CTestTestfileInstall.cmake @@ -0,0 +1,31 @@ +# Copyright (C) 2016 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +INCLUDE(tests.set) + +SET(COMPONENT_NAME SHAPER) +SET(SALOME_TEST_DRIVER "$ENV{KERNEL_ROOT_DIR}/bin/salome/appliskel/salome_test_driver.py") +SET(TIMEOUT 300) + + +FOREACH(tfile ${TEST_NAMES} ${EXAMPLE_NAMES}) + SET(TEST_NAME ${COMPONENT_NAME}_${tfile}) + ADD_TEST(${TEST_NAME} python ${SALOME_TEST_DRIVER} ${TIMEOUT} ${tfile}.py) + SET_TESTS_PROPERTIES(${TEST_NAME} PROPERTIES LABELS "${COMPONENT_NAME}") +ENDFOREACH() diff --git a/src/ConnectorAPI/Test/TestExportToGEOM.py b/src/ConnectorAPI/Test/TestExportToGEOM.py index 90539fff4..ad6c8bd7a 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/ConnectorAPI/Test/TestExportToGEOMAllGroupsAndFields.py b/src/ConnectorAPI/Test/TestExportToGEOMAllGroupsAndFields.py new file mode 100644 index 000000000..e52df8c9f --- /dev/null +++ b/src/ConnectorAPI/Test/TestExportToGEOMAllGroupsAndFields.py @@ -0,0 +1,186 @@ +# -*- coding: utf-8 -*- + +import os +import tempfile + +import sys +import salome + +salome.salome_init() +theStudy = salome.myStudy + +from salome.shaper import model + +from salome.geom import geomBuilder +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 + +def testGroupsAndFieldsExportToGEOM(): + 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")]) + 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) + 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.selection("FACE", "Translation_2_1/Translated_Face_1")]) + Group_1.result().setName("faces_top") + Group_2 = model.addGroup(Part_1_doc, [model.selection("EDGE", "Partition_1_1_1/Modified_Face_1_4&Partition_1_1_1/Modified_Face_1_2"), model.selection("EDGE", "Partition_1_1_2/Modified_Face_2_2&Partition_1_1_2/Modified_Face_2_1"), model.selection("EDGE", "Translation_2_1/Translated_Face_4&Translation_2_1/Translated_Face_2")]) + Group_2.result().setName("edges_x") + Group_3 = model.addGroup(Part_1_doc, [model.selection("VERTEX", "Translation_1_1/Translated_Face_6&Partition_1_1_1/Modified_Face_1_4&Partition_1_1_1/Modified_Face_1_2"), model.selection("VERTEX", "Translation_2_1/Translated_Face_5&Translation_2_1/Translated_Face_4&Translation_2_1/Translated_Face_2"), model.selection("VERTEX", "Translation_2_1/Translated_Face_5&Translation_2_1/Translated_Face_3&Translation_2_1/Translated_Face_2"), model.selection("VERTEX", "Translation_1_1/Translated_Face_6&Partition_1_1_1/Modified_Face_1_3&Partition_1_1_1/Modified_Face_1_2"), model.selection("VERTEX", "Partition_1_1_2/Modified_Face_2_3&Partition_1_1_2/Modified_Face_2_1&Box_2_1/Right"), model.selection("VERTEX", "Partition_1_1_2/Modified_Face_2_1&Box_2_1/Front&Box_2_1/Right"), model.selection("VERTEX", "Partition_1_1_1/Modified_Face_1_3&Partition_1_1_1/Modified_Face_1_2&Partition_1_1_2/Modified_Face_2_4"), model.selection("VERTEX", "Translation_2_1/Translated_Face_6&Translation_2_1/Translated_Face_3&Translation_2_1/Translated_Face_2")]) + Group_3.result().setName("vertices_bottom") + Group_4 = model.addGroup(Part_1_doc, [model.selection("SOLID", "Partition_1_1_1"), model.selection("SOLID", "Translation_2_1")]) + Group_4.result().setName("solids_small") + Field_1 = model.addField(Part_1_doc, 1, "DOUBLE", 1, ["Comp 1"], [model.selection("SOLID", "Partition_1_1_1"), model.selection("SOLID", "Partition_1_1_2"), model.selection("SOLID", "Translation_2_1")]) + Field_1.result().setName("Field_solids") + Field_1.addStep(0, 0, [[0], [1], [2], [3]]) + Field_2 = model.addField(Part_1_doc, 1, "DOUBLE", 1, ["Comp 1"], [model.selection("FACE", "Partition_1_1_1/Modified_Face_1_2"), model.selection("FACE", "Partition_1_1_2/Modified_Face_2_1"), model.selection("FACE", "Translation_2_1/Translated_Face_2"), model.selection("FACE", "Partition_1_1_1/Modified_Face_1_1"), model.selection("FACE", "Box_2_1/Top"), model.selection("FACE", "Translation_2_1/Translated_Face_1")]) + Field_2.result().setName("Field_faces") + Field_2.addStep(0, 0, [[0], [1], [1], [1], [2], [2], [2]]) + Field_3 = model.addField(Part_1_doc, 1, "DOUBLE", 1, ["Comp 1"], [model.selection("EDGE", "Partition_1_1_1/Modified_Face_1_4&Partition_1_1_1/Modified_Face_1_2"), model.selection("EDGE", "Partition_1_1_2/Modified_Face_2_2&Partition_1_1_2/Modified_Face_2_1"), model.selection("EDGE", "Translation_2_1/Translated_Face_4&Translation_2_1/Translated_Face_2"), model.selection("EDGE", "Translation_1_1/Translated_Face_6&Partition_1_1_1/Modified_Face_1_2"), model.selection("EDGE", "Translation_2_1/Translated_Face_5&Translation_2_1/Translated_Face_2"), model.selection("EDGE", "Translation_2_1/Translated_Face_5&Translation_2_1/Translated_Face_1"), model.selection("EDGE", "Translation_1_1/Translated_Face_6&Partition_1_1_1/Modified_Face_1_1"), model.selection("EDGE", "Partition_1_1_2/Modified_Face_2_3&Box_2_1/Top"), model.selection("EDGE", "Box_2_1/Front&Box_2_1/Top"), model.selection("EDGE", "Partition_1_1_2/Modified_Face_2_2&Box_2_1/Top"), model.selection("EDGE", "Box_2_1/Top&Box_2_1/Right")]) + Field_3.result().setName("Field_edges") + Field_3.addStep(0, 0, [[0], [1], [1], [1], [2], [2], [2], [2], [3], [3], [3], [3]]) + Field_4 = model.addField(Part_1_doc, 1, "DOUBLE", 1, ["Comp 1"], [model.selection("VERTEX", "Translation_1_1/Translated_Face_6&Partition_1_1_1/Modified_Face_1_4&Partition_1_1_1/Modified_Face_1_2"), model.selection("VERTEX", "Translation_1_1/Translated_Face_6&Partition_1_1_1/Modified_Face_1_3&Partition_1_1_1/Modified_Face_1_2"), model.selection("VERTEX", "Translation_2_1/Translated_Face_5&Translation_2_1/Translated_Face_4&Translation_2_1/Translated_Face_2"), model.selection("VERTEX", "Translation_2_1/Translated_Face_5&Translation_2_1/Translated_Face_3&Translation_2_1/Translated_Face_2"), model.selection("VERTEX", "Translation_2_1/Translated_Face_6&Translation_2_1/Translated_Face_3&Translation_2_1/Translated_Face_2"), model.selection("VERTEX", "Partition_1_1_2/Modified_Face_2_1&Box_2_1/Front&Box_2_1/Right"), model.selection("VERTEX", "Partition_1_1_2/Modified_Face_2_3&Partition_1_1_2/Modified_Face_2_1&Box_2_1/Right"), model.selection("VERTEX", "Partition_1_1_1/Modified_Face_1_3&Partition_1_1_1/Modified_Face_1_2&Partition_1_1_2/Modified_Face_2_4"), model.selection("VERTEX", "Translation_1_1/Translated_Face_6&Partition_1_1_1/Modified_Face_1_1&Partition_1_1_1/Modified_Face_1_3"), model.selection("VERTEX", "Translation_1_1/Translated_Face_6&Partition_1_1_1/Modified_Face_1_4&Partition_1_1_1/Modified_Face_1_1"), model.selection("VERTEX", "Translation_2_1/Translated_Face_5&Translation_2_1/Translated_Face_4&Translation_2_1/Translated_Face_1"), model.selection("VERTEX", "Translation_2_1/Translated_Face_5&Translation_2_1/Translated_Face_3&Translation_2_1/Translated_Face_1"), model.selection("VERTEX", "Partition_1_1_2/Modified_Face_2_2&Partition_1_1_2/Modified_Face_2_3&Box_2_1/Top"), model.selection("VERTEX", "Partition_1_1_2/Modified_Face_2_3&Box_2_1/Top&Box_2_1/Right"), model.selection("VERTEX", "Partition_1_1_2/Modified_Face_2_2&Box_2_1/Front&Box_2_1/Top"), model.selection("VERTEX", "Box_2_1/Front&Box_2_1/Top&Box_2_1/Right")]) + Field_4.result().setName("Field_vertices") + Field_4.addStep(0, 0, [[0], [1], [1], [1], [1], [1], [1], [1], [1], [2], [2], [2], [2], [3], [3], [3], [3]]) + model.exportToGEOM(Part_1_doc) + model.do() + model.end() + +# check the groups content by the coordinates of a point on its sub-shapes +def checkGroupByCoords(group, coords, tolerance=1e-7): + for coord in coords: + p = geompy.MakeVertex(*coord) + assert geompy.MinDistance(group, p) < tolerance + pass + +## Check the result imported in GEOM +def checkResultInGEOM(): + geomObject_1 = getLastGEOMShape() + + group_1_GEOM = getSubObject(geomObject_1, 1) + assert group_1_GEOM.GetName() == 'faces_top' + assert geompy.NumberOfFaces(group_1_GEOM) == 3 + + # coordinates of the barycenters of the faces of Group_1 + coords_1 = [[-5, 5, 10], [10, 10, 20], [25, 5, 10]] + checkGroupByCoords(group_1_GEOM, coords_1) + + group_2_GEOM = getSubObject(geomObject_1, 2) + assert group_2_GEOM.GetName() == 'edges_x' + assert geompy.NumberOfEdges(group_2_GEOM) == 3 + + # coordinates of the barycenters of the edges of Group_2 + coords_2 = [[-5, 0, 0], [10, 0, 0], [25, 0, 0]] + checkGroupByCoords(group_2_GEOM, coords_2) + + group_3_GEOM = getSubObject(geomObject_1, 3) + assert group_3_GEOM.GetName() == 'vertices_bottom' + assert geompy.NumberOfSubShapes(group_3_GEOM, geompy.ShapeType["VERTEX"]) == 8 + + # coordinates of the points of of Group_3 + coords_3 = [[-10, 0, 0], [-10, 10, 0], [0, 10, 0], [0, 20, 0], [20, 20, 0], [20, 10, 0], [30, 10, 0], [30, 0, 0]] + checkGroupByCoords(group_3_GEOM, coords_3) + + group_4_GEOM = getSubObject(geomObject_1, 4) + assert group_4_GEOM.GetName() == 'solids_small' + assert geompy.NumberOfSolids(group_4_GEOM) == 2 + + # coordinates of the barycenters of the solids of Group_4 + coords_4 = [[-5, 5, 5], [25, 5, 5]] + checkGroupByCoords(group_4_GEOM, coords_4) + + field_1_GEOM = getSubObject(geomObject_1, 5) + assert field_1_GEOM.GetName() == 'Field_solids' + assert field_1_GEOM.GetStep(0).GetValues() == [1.0, 2.0, 3.0] + + field_2_GEOM = getSubObject(geomObject_1, 6) + assert field_2_GEOM.GetName() == 'Field_faces' + assert field_2_GEOM.GetStep(0).GetValues() == [0.0, 0.0, 2.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 2.0] + + field_3_GEOM = getSubObject(geomObject_1, 7) + assert field_3_GEOM.GetName() == 'Field_edges' + assert field_3_GEOM.GetStep(0).GetValues() == [0.0, 2.0, 0.0, 2.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 3.0, 3.0, 0.0, 0.0, 0.0, 0.0, 3.0, 0.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 2.0, 1.0, 0.0, 0.0, 0.0] + + field_4_GEOM = getSubObject(geomObject_1, 8) + assert field_4_GEOM.GetName() == 'Field_vertices' + assert field_4_GEOM.GetStep(0).GetValues() == [2.0, 1.0, 2.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 3.0, 3.0, 3.0, 1.0, 1.0, 3.0, 0.0, 0.0, 0.0, 1.0, 2.0, 1.0, 2.0, 1.0] + + pass + +def checkDump(): + # 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__': + # create 3 boxes with groups and fields + testGroupsAndFieldsExportToGEOM() + # check the result in GEOM + checkResultInGEOM() + # check that dump produces no error and can be reloaded + checkDump() + # check the result of the dump + checkResultInGEOM() diff --git a/src/ConnectorAPI/Test/testme.py b/src/ConnectorAPI/Test/testme.py new file mode 100755 index 000000000..4a6e17a49 --- /dev/null +++ b/src/ConnectorAPI/Test/testme.py @@ -0,0 +1,56 @@ +# Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +#!/usr/bin/env python + +import unittest, sys, os + +class SalomeSession(object): + def __init__(self, script): + import runSalome + run_script = "runSalome.py" + if sys.platform == 'win32': + module_dir = os.getenv("KERNEL_ROOT_DIR") + if module_dir: run_script = os.path.join(module_dir, "bin", "salome", run_script) + pass + sys.argv = [run_script] + sys.argv += ["--terminal"] + sys.argv += ["--modules=SHAPER,GEOM"] + sys.argv += ["%s" % script] + if sys.platform == 'win32': + main_module_path = sys.modules['__main__'].__file__ + sys.modules['__main__'].__file__ = '' + clt, d = runSalome.main() + if sys.platform == 'win32': + sys.modules['__main__'].__file__ = main_module_path + return + + def __del__(self): + port = os.getenv('NSPORT') + import killSalomeWithPort + killSalomeWithPort.killMyPort(port) + return + pass + +class MyTest(unittest.TestCase): + def testFunction(self): + SalomeSession(sys.argv[1]) + pass + +unittest.main(argv=sys.argv[:1]) diff --git a/src/ConnectorAPI/Test/tests.set b/src/ConnectorAPI/Test/tests.set new file mode 100644 index 000000000..c3126a360 --- /dev/null +++ b/src/ConnectorAPI/Test/tests.set @@ -0,0 +1,23 @@ +# Copyright (C) 2016 CEA/DEN, EDF R&D +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +# + +SET(TEST_NAMES + TestExportToGEOM + TestExportToGEOMAllGroupsAndFields +) 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/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 0cdebfdfe..5cb8cd081 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) -- 2.39.2