From ef5ed77d707e6ad392684ff83d213296781ffd07 Mon Sep 17 00:00:00 2001 From: skv Date: Fri, 13 Nov 2015 09:21:54 +0300 Subject: [PATCH] 0023197: [CEA] Extract and rebuild --- doc/salome/gui/GEOM/CMakeLists.txt | 1 + doc/salome/gui/GEOM/images/extract_init.png | Bin 0 -> 4948 bytes .../gui/GEOM/images/extract_rebuild.png | Bin 0 -> 39127 bytes doc/salome/gui/GEOM/images/extract_result.png | Bin 0 -> 2941 bytes .../gui/GEOM/input/extract_and_rebuild.doc | 82 + doc/salome/gui/GEOM/input/related_docs.doc | 2 + .../gui/GEOM/input/transforming_geom_objs.doc | 2 + doc/salome/gui/GEOM/input/tui_test_all.doc | 3 + .../gui/GEOM/static/ExtractAndRebuild.pdf | Bin 0 -> 51279 bytes idl/GEOM_Gen.idl | 37 + resources/CMakeLists.txt | 1 + resources/extract.png | Bin 0 -> 1155 bytes src/GEOMAlgo/CMakeLists.txt | 2 + src/GEOMAlgo/GEOMAlgo_Extractor.cxx | 1429 +++++++++++++++++ src/GEOMAlgo/GEOMAlgo_Extractor.hxx | 363 +++++ src/GEOMGUI/GEOM_images.ts | 8 + src/GEOMGUI/GEOM_msg_en.ts | 75 + src/GEOMGUI/GEOM_msg_fr.ts | 75 + src/GEOMGUI/GEOM_msg_ja.ts | 75 + src/GEOMGUI/GeometryGUI.cxx | 4 + src/GEOMGUI/GeometryGUI_Operations.h | 1 + src/GEOMImpl/CMakeLists.txt | 1 + src/GEOMImpl/GEOMImpl_IExtract.hxx | 73 + src/GEOMImpl/GEOMImpl_IShapesOperations.cxx | 102 ++ src/GEOMImpl/GEOMImpl_IShapesOperations.hxx | 34 + src/GEOMImpl/GEOMImpl_ShapeDriver.cxx | 135 ++ src/GEOMImpl/GEOMImpl_Types.hxx | 3 + src/GEOM_I/GEOM_IShapesOperations_i.cc | 95 ++ src/GEOM_I/GEOM_IShapesOperations_i.hh | 5 + src/GEOM_SWIG/GEOM_TestAll.py | 5 + src/GEOM_SWIG/geomBuilder.py | 29 + src/OperationGUI/CMakeLists.txt | 3 + src/OperationGUI/OperationGUI.cxx | 2 + .../OperationGUI_ExtractionDlg.cxx | 1324 +++++++++++++++ src/OperationGUI/OperationGUI_ExtractionDlg.h | 115 ++ 35 files changed, 4086 insertions(+) create mode 100644 doc/salome/gui/GEOM/images/extract_init.png create mode 100644 doc/salome/gui/GEOM/images/extract_rebuild.png create mode 100644 doc/salome/gui/GEOM/images/extract_result.png create mode 100644 doc/salome/gui/GEOM/input/extract_and_rebuild.doc create mode 100644 doc/salome/gui/GEOM/static/ExtractAndRebuild.pdf create mode 100644 resources/extract.png create mode 100644 src/GEOMAlgo/GEOMAlgo_Extractor.cxx create mode 100644 src/GEOMAlgo/GEOMAlgo_Extractor.hxx create mode 100644 src/GEOMImpl/GEOMImpl_IExtract.hxx create mode 100644 src/OperationGUI/OperationGUI_ExtractionDlg.cxx create mode 100644 src/OperationGUI/OperationGUI_ExtractionDlg.h diff --git a/doc/salome/gui/GEOM/CMakeLists.txt b/doc/salome/gui/GEOM/CMakeLists.txt index f7877081b..b765fe7ae 100644 --- a/doc/salome/gui/GEOM/CMakeLists.txt +++ b/doc/salome/gui/GEOM/CMakeLists.txt @@ -66,6 +66,7 @@ INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/GEOM DESTINATION ${SALOME_INSTALL_ INSTALL(FILES images/head.png DESTINATION ${SALOME_INSTALL_DOC}/gui/GEOM) INSTALL(FILES images/head.png DESTINATION ${SALOME_INSTALL_DOC}/gui/GEOM/geompy_doc) INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/static/SALOME_BOA_PA.pdf DESTINATION ${SALOME_INSTALL_DOC}/gui/GEOM) +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/static/ExtractAndRebuild.pdf DESTINATION ${SALOME_INSTALL_DOC}/gui/GEOM) INSTALL(FILES input/geompy_migration.doc input/tui_auto_completion_documentation.doc input/tui_execution_distribution.doc DESTINATION ${SALOME_INSTALL_DOC}/gui/GEOM/input) FILE(GLOB tag_files ${CMAKE_CURRENT_BINARY_DIR}/*.tag) diff --git a/doc/salome/gui/GEOM/images/extract_init.png b/doc/salome/gui/GEOM/images/extract_init.png new file mode 100644 index 0000000000000000000000000000000000000000..d60855104f9c49242f709ecef0f5088cb5052bda GIT binary patch literal 4948 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 literal 0 HcmV?d00001 diff --git a/doc/salome/gui/GEOM/images/extract_rebuild.png b/doc/salome/gui/GEOM/images/extract_rebuild.png new file mode 100644 index 0000000000000000000000000000000000000000..f553d263d11d45ca266c211fdbb02b221215d760 GIT binary patch literal 39127 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

xw;ajoK_n!>>t9yDR?SSQq(DheU#Z1&Ft7 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 bHS+LVGW;hfJ1kN0kxnX{md}wh^80@Pf_rPC literal 0 HcmV?d00001 diff --git a/doc/salome/gui/GEOM/images/extract_result.png b/doc/salome/gui/GEOM/images/extract_result.png new file mode 100644 index 0000000000000000000000000000000000000000..4148d0d97d6df2c8ee295a359df3a3da9d15e092 GIT binary patch literal 2941 zcmXw5dpOhW8~={CcOh1({5ow#sH7uSVUt6smpN75-k1_gh03axwh@~1AwqeXWbYhe za%c{*eh$%rvDBO*Dz%MRZ8JN3zrXcf*YA(#xu5&<-1l?c_kG>ZeSMO=JYDs5*XaTP zpzrSH?4yw&0Kg2EYH3bpE9@&Z0v6@t>IhW#Z4hV_ol9)uVW=P}eR1LJ2=;BXwiVP1Ai68<_S3*t`L(Xox9!5O3$<^J%>qM-95di24 z-JK8lkw#_Vc&>|6UF~)0U_db3U zJ^C8od>RGSX%%Oz$OVW$toNea$bd)ru1<^>3D{}}lp!4W@M4j+M)N|&v}-@#W2D(* z6abM7G-H4SxJL7$PR3@+L4KMtg9GMyIg&kiUvKO*cJZ#+v>|G>80UyK-N0&0^zfDc*)9jTztLfKJO;2Dc6xwjdch!==oF)fM?Taxy?EtI| z#h)g*=+RNm)#6*I?WLynn%ktFXrX?Bs}Z)D37+J>&r}FY;e)O>KjyE`ku2QIDyn)Y zqIH-oD30EIdZ*nmb764(p!v*ZS$oH*&;i-XvK9)cYn~)V z;fCnFN`4=dG@!iQ64UYB>b~M}btw&)BC7T4zJSD1rU*@2oWZXV#4)xe=h!5+$4Qnu zP9{Oo`Hk2wdi&2}$~jLZ@%9&wtM~hcOxd0F2wvPlO%#LW8Iq4|5xn2D4xR;m51`jhEc-{rbWvn(OD_3f zUovksWzTbpPGF|7MmEn_Tt(&BK z_vH@!g}^N>A9jlIRU%(mFpfM$HT-;|h{~@q*i(*IeP||rmFpc^Q*@kTcLIwZx#53% z?FliW6Tq{DHgCryXuOS@M}u53;`AL=LEr3x>kp$~vs34|dq`_|&apJkZo z3Jf@f3D9_#;gs;I06b2~oXb>nVlTb<;IA^jqB50fx>Jdm_9a=#P?R$;DXo@XNiQxD zO@sqy!XYbUb6cM*V{sLdF-z_4uzR-lo}vzKqntOJMQybcu&FjF!4bZ*SP3ks?wdk_ zav;xkdsr*c`?*_2yY+KH|+8olan4+ zMPNs0mL{fRN~QM4#c6(@D~YskyOgCe9iyR>sLhzp0dRPiE<%hbV>PNfmM74x%SI_- zKcM!=4 z#o5-t$S!ExYaaIn<8MWL8{B52N&_`47~j=6zAg-SbVoieNvTGCuYbHvoeU#I4BDYz zSq7#z>A2HFh7VIN=Y~W8{_KiNX!H(2;j3fRPqpeiEWy@YP0Xe^sOFF+pf^5EV)1*> zLjGyM#*d`Bb^kSe^h0n3E3$f9Yj(pp_Psi`rF+>I!tUuu1x0JlaU0J|mDqR8&4LrY zxeT`2RQfQX6g6*9Ll_If25`u8=<6_oo021MtRws_<7}7e{6=5%RL&?=bEY@Ov_FcP zFG8V*IHed-@kOBMx){2DjD`7@3Rxa*QWMEtVoq0L53Ak)C}-J&_@Ohz5geZY)Eb>q zQ7euv(M9a&g5pJ+1%(k|@K(u-4N@Y`lo&`K;E`Hedv=_$)qp}*tjsL-=pn;^y z$SP8F*gvDQxgl$VRZ6SQuPN0CQ9VS?p3b8l+0Fi@r~!eQ0xPANuLyf;EM8X(9dZ=o zgiZ-C(q-PW{3EI;w9)o%P8}479qD-o9~uKLT6>S;__WOm?r{@iPGS_`M*fcth#xtB zrk-AnuwM53=uYr;{@Idw2eO!cf8o;;dP;%US73(w2HcTeNhgxau;4=XawyOjQs31y z40Nd(6Eb1+9d#^H99MQSiMnEO5#^YZ+OD03cpjUs(@#ln`WOrt5Z}al`m1Rv1s|=! z2|Y8giLoRQ{xPUKu_v0thLs^c+Jgws`|oF&BB!HbY>0tp!!RgN^j}e>^l(Q=@Zxy~ zrS|-f9C0MJ`_$4iK;ShoWJ^8hL)ug-p9me7hWvsr00!avXipdSgFNZxPC0h)mMSB0 zHJx=`4>=ycT|AMWY(nYfQ_9l6XQM|Ou&-;Kn!trzVa z*VuStvUzouNrvWgF(iFyW;^u1>2ec9vZZ3g(Q#Wrfyv{Pf=;Z2M(z;Ot{)Y(m)QUK zP0Tu`=u%uzKghqnrlYk`Ibs<0f>VSWf4rrYusW`-K-pJ9PLsRdOc3tcb)-U^A+gL& z-(NI#D|(>KQ^hn@kEVD8G^EP&Tb`=*jynfV^a0Lut)s8rccsj{?EM0J4*GXcHs|9` zri(ADf9lD$ictlUuSO>$_FraFIBMf7Ir0owMSr4_S>MMUeS}*H!H!~4&Q-YWQst~s z)-T}D5!CJ^mA|6thSG2fyKIizOM55C_E1xlF7dE)=oxOWYc``??XUO&1Yvos6C@wi z?z?QvlSn%kRrFo33imkC+5GNh>YU$=Pvs7uureB2$;im*#L{S*ycoh=kv=tIRN=zQ z!Fa0{@O)q2FuJ7^syR)))@NptT7p2@*%Dkn_wE#P+y#cpwn6f=nWhX!6I>_Ki|*GM zUrB}e<8HFO%GZ@FWas9=0p*+-dE^{)OTDj7He@ix!6}9>(Hpi%MQprt`mEPRDCE!a z+(3O*oFn^y{e_ImRH~7TNvN_hbW^j2b{TiS>x6102ifbT)-O0$1bMoMG{R~o<$}xS z&bK-*IN9UmxL54)#$z$o2gJlU@!aBMFGUdBUN<1mXoYe@k|brUN@%%^s6KtaDHMk> z^i|mk2-V&qclE&;qpWOhJQ7m#w1t(4)KZoY$v;l`2fogD??xYu*#~^8y{U9PDn_t- zinHT&*xj~T#p9vfGJJ#aUiGKUx#DO1~mwjS3HBWw>M}Kyt z%3SJHi+2uPmjY=bsz!|ir=IMyy^s!#^6&#cO#D_A`5(C4mE2QaXx*vFLmhkZc-nv1 z|MV|s{{8}LMnvX@y!?GYdbNv69nu!M+5t?PF)(ZV1fP)5dP#*o?(B)RJqP?P`F^5y zS1ILzc1=4-5&ZYu8RY+j)7m3VN02`uU8dmv#;I|$KcEYd6 VN4sC5)=*=>-NnOperations -> Extract and Rebuild. +The following dialog box appears: + +\image html extract_rebuild.png "Extract and Rebuild Dialog" + +In this dialog: +

    +
  • \b Name is a result object name.
  • +
  • Main Object is the main object selected for the operation. It is +possible to select the object in the Object Browser or in the viewer using +"selection" button.
  • +
  • Sub-shape type is the type filter for sub-shapes to be chosen +for the extraction.
  • +
  • Filtered shapes is the list of filtered sub-shape IDs available +for the extraction.
  • +
  • Shapes to extract is the list of sub-shape IDs to be extracted +grouped by shape types.
  • +
  • \b >> is the button to copy object IDs from Filtered shapes to +Shapes to extract.
  • +
  • \b << is the button to copy object IDs from Shapes to extract to +Filtered shapes.
  • +
  • Show Only Selected is the button to show objects selected in the +Filtered shapes list and hide all other objects.
  • +
  • Hide Selected is the button to hide objects selected in the +Filtered shapes list.
  • +
  • Show all sub-shapes is the button to show all objects from the +Filtered shapes list.
  • +
  • \b Rebuild is the button to perform extraction and show the operation +statistics.
  • +
  • The block of \b Statistics shows how many shapes of a certain type +are \b Removed, \b Modified and \b Added.
  • +
  • \b Apply is the button to create the result.
  • +
  • Apply and Close is the button to create the result and close +the dialog.
  • +
  • \b Close is the button to close this dialog.
  • +
  • \b Help is the button to show this help page.
  • +
+ +TUI Command: geompy.MakeExtraction(theShape, theListOfID), +
where \em theShape is the main shape, \em theListOfID is a list of +sub-shape IDs to be extracted. + +Our TUI Scripts provide you with useful example of the use of +\ref swig_MakeExtraction "Extract and Rebuild" functionality. + +More details + +If a sub-shape is extracted, all its ancestors should be modified. An ancestors +of extracted sub-shapes can be either: +- created anew without extracted sub-shapes, or +- extracted if it is not possible to create a valid shape without extracted +sub-shape. + +E.g. it is necessary to extract the vertex from the box: + +\image html extract_init.png "Extraction of the vertex from the box" + +In this case 3 ancestor edges are removed as they can't be valid without +this vertex. 3 faces that contain these edges are also removed. It is because +the wires without edges are not closed and it is not possible to create +a valid face on not closed wire. These wires should contain two remaining +edges, but they are removed as they are the part of the remaining shell. +So these wires become empty that means that they should be removed as well. + +The shell is replaced by another one that contains 3 not modified faces. As +It is not possible to construct a valid solid from not closed shell the solid +is removed. So the result of the extraction is a shell that contains 3 faces: + +\image html extract_result.png "Result shape" + +Please, refer to this document for a detailed +description of Extract and Rebuild operation. + +*/ diff --git a/doc/salome/gui/GEOM/input/related_docs.doc b/doc/salome/gui/GEOM/input/related_docs.doc index fea4ba010..2f210676c 100644 --- a/doc/salome/gui/GEOM/input/related_docs.doc +++ b/doc/salome/gui/GEOM/input/related_docs.doc @@ -7,4 +7,6 @@ that can be useful for reading. General Fuse Algorithm, Partition Algorithm, Boolean Operations Algorithm. Backgrounds. +Extract and Rebuild algorithm specification. + */ diff --git a/doc/salome/gui/GEOM/input/transforming_geom_objs.doc b/doc/salome/gui/GEOM/input/transforming_geom_objs.doc index 8ded8676a..3a1307360 100644 --- a/doc/salome/gui/GEOM/input/transforming_geom_objs.doc +++ b/doc/salome/gui/GEOM/input/transforming_geom_objs.doc @@ -27,6 +27,8 @@ special case of \b Explode operation. special case of \b Explode operation.
  • \subpage transfer_data_page "Transfer Data" operation, which copies non-topological data from one shape to another.
  • +
  • \subpage extract_and_rebuild_page "Extract and Rebuild" operation, which +extracts sub-shapes from the main shape.
  • \subpage restore_presentation_parameters_page "Restore presentation parameters". diff --git a/doc/salome/gui/GEOM/input/tui_test_all.doc b/doc/salome/gui/GEOM/input/tui_test_all.doc index 5ed1334a0..7c2ddd136 100644 --- a/doc/salome/gui/GEOM/input/tui_test_all.doc +++ b/doc/salome/gui/GEOM/input/tui_test_all.doc @@ -105,6 +105,9 @@ \until geompy.GetSubShapeEdgeSorted(Sketcher3d_2, p3, "OrderedEdges") \anchor swig_GetSubShapesWithTolerance +\until geompy.GetSubShapesWithTolerance(Box, GEOM.FACE, GEOM.CC_LE, 1.e-7, "le") + +\anchor swig_MakeExtraction \until print "DONE" */ diff --git a/doc/salome/gui/GEOM/static/ExtractAndRebuild.pdf b/doc/salome/gui/GEOM/static/ExtractAndRebuild.pdf new file mode 100644 index 0000000000000000000000000000000000000000..36e4110575f2d9406e0141b23b0d1ef0f1ab9ed9 GIT binary patch literal 51279 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 L+}X1iJtzGiVkoT* literal 0 HcmV?d00001 diff --git a/idl/GEOM_Gen.idl b/idl/GEOM_Gen.idl index be4adbbd0..777f3bcc3 100644 --- a/idl/GEOM_Gen.idl +++ b/idl/GEOM_Gen.idl @@ -2782,6 +2782,43 @@ module GEOM in comparison_condition theCondition, in double theTolerance); + /** + * This enumeration represents an extraction statistics type. It is used in + * the interface GEOM_IShapesOperations::MakeExtraction. + */ + enum ExtractionStatType + { + EST_Removed, ///< Removed sub-shapes + EST_Modified, ///< Modified sub-shapes + EST_Added ///< Newly created sub-shapes + }; + + /*! + * This structure defines a format of extraction statistics. It is used in + * the interface GEOM_IShapesOperations::MakeExtraction. + */ + struct ExtractionStat + { + ExtractionStatType type; ///< Type of extraction statistics. + ListOfLong indices; ///< Shape indices touched by this type of modification. + }; + + typedef sequence ExtractionStats; + + /*! + * \brief Return the shape that is constructed from theShape without + * extracted sub-shapes from the input list. + * + * \param theShape the original shape. + * \param theSubShapeIDs the list of sub-shape IDs to be extracted from + * the original shape. + * \param theStats the operation statistics. Output parameter. + * \return the shape without extracted sub-shapes. + */ + GEOM_Object MakeExtraction(in GEOM_Object theShape, + in ListOfLong theSubShapeIDs, + out ExtractionStats theStats); + }; // # GEOM_IBlocksOperations: diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt index b08b1d5b9..ed5809964 100755 --- a/resources/CMakeLists.txt +++ b/resources/CMakeLists.txt @@ -94,6 +94,7 @@ SET( _res_files edgeextension.png erase.png eraseall.png + extract.png extruded_boss.png extruded_cut.png facetosurface.png diff --git a/resources/extract.png b/resources/extract.png new file mode 100644 index 0000000000000000000000000000000000000000..cdbe85a83aa372ea6a754730285f7629f3145ccf GIT binary patch literal 1155 zcmV-}1bq96P)PKXQnfh-U}4UO^Ij_CGnwNi4T4R<&6*F z2hk@Jec(ZTFh2NTqDB&ocadUh8xTrMOQkKHPN#F`oH>_$S<43)Lx`yV=k@<(trg(E zBIKwVefnH@@a)?{*A7u{>kh!_W5@r6s>;UH>*3)y-qe$4PD@p-ku3|iJ%6oS`SMfh zu6_YOqkqGP3IcJK_-Poi%ak;y?RIXn9 z+He2Y`0CUzPKi z4GvLUR@p0tvWdvjWkqE&$&63|0F&Rb&XF=1Y#yL7)CkYG`N~VRwczkbMbw9fq4Xg3 zmZhj=TRfR=1vgV&xhy7JduPd-c%5C*Ww1}~Z^*RG5ahU`3$BI{ID8^s36G2k*%%&% zs@EZ;1Pg)jq7dQk9g%Nz!|#S>P!^f-o=ak4fV=WO=wo<8Bx{kAjcTuimc3kvP6w1_ zkU~Ie4K@bF#s-SzWpJ}8vOLpXL{*ZNs)u4Q#r+==d&h7YIaB2V7S4LuB!OwQ5NHhu zf$a8eq&IFLTVIC>hEQY{-dko8#LNsJ>w{e|d*s_a$<~t>A$b=Ob70aGyuJ>XBuJK) zkT;v~)`A6*H)Y8-E6FAW`@#ZneXt~(7oMD#E-3;LxaDX_9iUw9>vjd@Im$Rj9>*w* zK~qJ%G(6=4SN6O~9GS$!W*GqA2SMd-hupZgk^&(E`lkL2*|N?rcCTZfZwc!4kd`Gj zgBs6{p2Sb%Pcb)l*p-=JB4U@d03dkaWe6(M2>0wqSl=t8j=Y=*Hycj|nKRtztU;!` zVvRKYGIENqj=caA=S*cn-lUGoc!PQy7h#j{`ye>G_Zt^&f*u%$stt*-Q6*?>SNGdI z2-(`6n5!RgbG75H>}A56jD5bF`gG%#@7($XdGkF0_HPh?gX!F)cy)_e4vJ`SL`WHs zv)rnT`Ne^0w`sgD(wJP4SeLhBAFo{Yt%VD)$!7q%51-tR1(_A@-kxWZXrTv25kv#b z2z;5qq)pf|vA(yp>9-fYqV^x}g53`Ql0NV#2ml12#l4L=idSP6dYtv(Fqu@k^qz6M zD~ql<_aSHZJ^-)?z&>35C$OK4ZFwXe*tEe VwNZ}DIrIPk002ovPDHLkV1g-N85{rr literal 0 HcmV?d00001 diff --git a/src/GEOMAlgo/CMakeLists.txt b/src/GEOMAlgo/CMakeLists.txt index d2b6024ab..38126e94b 100755 --- a/src/GEOMAlgo/CMakeLists.txt +++ b/src/GEOMAlgo/CMakeLists.txt @@ -59,6 +59,7 @@ SET(GEOMAlgo_HEADERS GEOMAlgo_DataMapOfPassKeyInteger.hxx GEOMAlgo_DataMapOfShapeMapOfShape.hxx GEOMAlgo_DataMapOfShapePnt.hxx + GEOMAlgo_Extractor.hxx GEOMAlgo_FinderShapeOn.hxx GEOMAlgo_FinderShapeOn1.hxx GEOMAlgo_FinderShapeOn2.hxx @@ -123,6 +124,7 @@ SET(GEOMAlgo_SOURCES GEOMAlgo_ClsfSurf.cxx GEOMAlgo_CoupleOfShapes.cxx GEOMAlgo_FinderShapeOn2.cxx + GEOMAlgo_Extractor.cxx GEOMAlgo_GetInPlace.cxx GEOMAlgo_GetInPlace_1.cxx GEOMAlgo_GetInPlace_2.cxx diff --git a/src/GEOMAlgo/GEOMAlgo_Extractor.cxx b/src/GEOMAlgo/GEOMAlgo_Extractor.cxx new file mode 100644 index 000000000..bdbadab69 --- /dev/null +++ b/src/GEOMAlgo/GEOMAlgo_Extractor.cxx @@ -0,0 +1,1429 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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 +// + +// File: GEOMAlgo_Extractor.cxx +// Created: +// Author: Sergey KHROMOV +// + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +//======================================================================= +//function : GEOMAlgo_Extractor +//purpose : +//======================================================================= +GEOMAlgo_Extractor::GEOMAlgo_Extractor() +{ +} + +//======================================================================= +//function : ~GEOMAlgo_Extractor +//purpose : +//======================================================================= +GEOMAlgo_Extractor::~GEOMAlgo_Extractor() +{ +} + +//======================================================================= +//function : SetShape +//purpose : +//======================================================================= +void GEOMAlgo_Extractor::SetShape(const TopoDS_Shape &theShape) +{ + myShape = theShape; + myMapShapeAnc.Clear(); + clear(); +} + +//======================================================================= +//function : SetShapesToRemove +//purpose : +//======================================================================= +void GEOMAlgo_Extractor::SetShapesToRemove + (const TopTools_ListOfShape &theSubShapes) +{ + mySubShapes.Assign(theSubShapes); + clear(); +} + +//======================================================================= +//function : Perform +//purpose : +//======================================================================= +void GEOMAlgo_Extractor::Perform() +{ + clear(); + myErrorStatus = 0; + // + checkData(); + + if(myErrorStatus) { + return; + } + + if (myWarningStatus == 10) { + // The result is the same shape. Nothing is modified. + myResult = myShape; + return; + } + + // Mark sub-shapes as removed and modified. + markShapes(); + + // Process Edges. + processShapes(TopAbs_EDGE); + + // Process Wires. + processShapes(TopAbs_WIRE); + + // Process Faces. + processShapes(TopAbs_FACE); + + // Process Shells. + processShapes(TopAbs_SHELL); + + // Process Solids. + processShapes(TopAbs_SOLID); + + // Process Comp-Solids. + processShapes(TopAbs_COMPSOLID); + + // Process Compounds. + processShapes(TopAbs_COMPOUND); + + // Make the result. + myResult = makeResult(myShape); + + TopTools_MapOfShape aMapFence; + + makeHistory(myShape, aMapFence); +} + +//======================================================================= +//function : GetResult +//purpose : +//======================================================================= +const TopoDS_Shape &GEOMAlgo_Extractor::GetResult() const +{ + return myResult; +} + +//======================================================================= +//function : clear +//purpose : +//======================================================================= +void GEOMAlgo_Extractor::clear() +{ + myErrorStatus = 1; + myWarningStatus = 0; + myResult.Nullify(); + myRemoved.Clear(); + myModified.Clear(); + myNew.Clear(); + myMapRemoved.Clear(); + myMapModified.Clear(); + myMapNewShapeAnc.Clear(); +} + +//======================================================================= +//function : checkData +//purpose : +//======================================================================= +void GEOMAlgo_Extractor::checkData() +{ + if (myShape.IsNull()) { + myErrorStatus = 10; + return; + } + + if (mySubShapes.IsEmpty()) { + myWarningStatus = 10; + return; + } + + TopTools_ListIteratorOfListOfShape anIter(mySubShapes); + TopTools_IndexedMapOfShape anIndices; + TopTools_MapOfShape aMapFence; + + TopExp::MapShapes(myShape, anIndices); + + while (anIter.More()) { + const TopoDS_Shape &aSubShape = anIter.Value(); + + if (aMapFence.Add(aSubShape)) { + // Check if it is a sub-shape of the given shape. + if (!anIndices.Contains(aSubShape)) { + myErrorStatus = 11; + return; + } + + // Check if it is a main shape. + if (aSubShape.IsSame(myShape)) { + myErrorStatus = 12; + return; + } + + anIter.Next(); + } else { + // Remove duplicated index. + mySubShapes.Remove(anIter); + } + } + + if (myMapShapeAnc.IsEmpty()) { + // Fill the map of shapes - ancestors. + makeMapShapeAncestors(myShape); + } + + // Check if there are seam or degenerated edges on faces. + for (anIter.Initialize(mySubShapes); anIter.More(); anIter.Next()) { + const TopoDS_Shape &aSubShape = anIter.Value(); + + if (aSubShape.ShapeType() == TopAbs_EDGE) { + // Get the list of ancestor wires. + TopTools_ListOfShape anAncWires; + TopTools_ListIteratorOfListOfShape anAncIt; + + if (myMapShapeAnc.IsBound(aSubShape)) { + anAncIt.Initialize(myMapShapeAnc.Find(aSubShape)); + + for (; anAncIt.More(); anAncIt.Next()) { + const TopoDS_Shape &anAncShape = anAncIt.Value(); + + if (anAncShape.ShapeType() == TopAbs_WIRE) { + anAncWires.Append(anAncShape); + } + } + } + + if (!anAncWires.IsEmpty()) { + // Check the ancestor faces. + Standard_Boolean hasFaces = Standard_False; + TopoDS_Edge anEdge = TopoDS::Edge(aSubShape); + + for (anAncIt.Initialize(anAncWires); anAncIt.More(); anAncIt.Next()) { + const TopoDS_Shape &anAncShape = anAncIt.Value(); + + if (anAncShape.ShapeType() == TopAbs_FACE) { + TopoDS_Face aFace = TopoDS::Face(anAncShape); + + if (BRepTools::IsReallyClosed(anEdge, aFace)) { + // Deletion of face's seam edge is not allowed + myErrorStatus = 13; + return; + } + + hasFaces = Standard_True; + } + } + + if (hasFaces && BRep_Tool::Degenerated(anEdge)) { + myErrorStatus = 14; + return; + } + } + } + } +} + +//======================================================================= +//function : makeMapShapeAncestors +//purpose : +//======================================================================= +void GEOMAlgo_Extractor::makeMapShapeAncestors(const TopoDS_Shape &theShape) +{ + if (theShape.ShapeType() == TopAbs_VERTEX) { + // Vertex is the lowest type. It has no ancestors. + return; + } + + TopoDS_Iterator anIter(theShape); + TopTools_MapOfShape aMapFence; + + for (; anIter.More(); anIter.Next()) { + const TopoDS_Shape &aSubShape = anIter.Value(); + + if (aMapFence.Add(aSubShape)) { + // Add theShape as an ancestor shape. + if (!myMapShapeAnc.IsBound(aSubShape)) { + myMapShapeAnc.Bind(aSubShape, TopTools_ListOfShape()); + } + + myMapShapeAnc.ChangeFind(aSubShape).Append(theShape); + + // Recursively call this method for a sub-shape. + makeMapShapeAncestors(aSubShape); + } + } +} + +//======================================================================= +//function : markShapes +//purpose : +//======================================================================= +void GEOMAlgo_Extractor::markShapes() +{ + TopTools_ListIteratorOfListOfShape anIter(mySubShapes); + + // Mark sub-shapes as removed. + for (; anIter.More(); anIter.Next()) { + const TopoDS_Shape &aSubShape = anIter.Value(); + + markRemoved(aSubShape); + } + + // Mark undestors of sub-shapes as modified. + for (anIter.Initialize(mySubShapes); anIter.More(); anIter.Next()) { + const TopoDS_Shape &aSubShape = anIter.Value(); + + markAncestorsModified(aSubShape); + } +} + +//======================================================================= +//function : markRemoved +//purpose : +//======================================================================= +void GEOMAlgo_Extractor::markRemoved(const TopoDS_Shape &theShape) +{ + if (myMapRemoved.Add(theShape)) { + // Check sub-shapes. + TopoDS_Iterator anIter(theShape); + TopTools_MapOfShape aMapFence; + + for (; anIter.More(); anIter.Next()) { + const TopoDS_Shape &aSubShape = anIter.Value(); + + if (aMapFence.Add(aSubShape)) { + TopTools_ListIteratorOfListOfShape anAncIt + (myMapShapeAnc.Find(aSubShape)); + Standard_Boolean isToRm = Standard_True; + + for (; anAncIt.More(); anAncIt.Next()) { + const TopoDS_Shape &anAncShape = anAncIt.Value(); + + if (!myMapRemoved.Contains(anAncShape)) { + isToRm = Standard_False; + break; + } + } + + if (isToRm) { + // Mark sub-shape as removed. + markRemoved(aSubShape); + } + } + } + } +} + +//======================================================================= +//function : markAncestorsModified +//purpose : +//======================================================================= +void GEOMAlgo_Extractor::markAncestorsModified(const TopoDS_Shape &theShape) +{ + if (myMapShapeAnc.IsBound(theShape)) { + TopTools_ListIteratorOfListOfShape anAncIt(myMapShapeAnc.Find(theShape)); + + for (; anAncIt.More(); anAncIt.Next()) { + const TopoDS_Shape &anAncShape = anAncIt.Value(); + + if (!myMapRemoved.Contains(anAncShape) && + !myMapModified.IsBound(anAncShape)) { + // Mark anAncShape as modified. + myMapModified.Bind(anAncShape, TopTools_ListOfShape()); + + // Mark its ancestors as modified. + markAncestorsModified(anAncShape); + } + } + } +} + +//======================================================================= +//function : processShapes +//purpose : +//======================================================================= +void GEOMAlgo_Extractor::processShapes(const TopAbs_ShapeEnum &theType) +{ + TopExp_Explorer anExp(myShape, theType); + TopTools_MapOfShape aMapFence; + + for (; anExp.More(); anExp.Next()) { + TopoDS_Shape aShape = anExp.Current(); // Copy + + if (aMapFence.Add(aShape)) { + if (myMapRemoved.Contains(aShape) || + !myMapModified.IsBound(aShape)) { + // Skip removed or not modified shape. + continue; + } + + aShape.Orientation(TopAbs_FORWARD); + + switch(theType) { + case TopAbs_EDGE: + processEdge(aShape); + break; + case TopAbs_WIRE: + processWire(aShape); + break; + case TopAbs_FACE: + case TopAbs_SOLID: + processFOrSo(aShape); + break; + case TopAbs_SHELL: + case TopAbs_COMPSOLID: + processShOrCS(aShape); + break; + case TopAbs_COMPOUND: + processCompound(aShape); + break; + default: + break; + } + } + } + + if (theType == TopAbs_FACE || theType == TopAbs_SOLID) { + // Clear duplicated edges from the faces and faces from solids + removeBoundsOnFOrSo(theType); + } +} + +//======================================================================= +//function : processEdge +//purpose : +//======================================================================= +void GEOMAlgo_Extractor::processEdge(const TopoDS_Shape &theEdge) +{ + TopoDS_Iterator anIter(theEdge); + TopTools_MapOfShape aMapFence; + TopTools_ListOfShape aVtxList; + + for (; anIter.More(); anIter.Next()) { + const TopoDS_Shape &aShapeVertex = anIter.Value(); + + if (aMapFence.Add(aShapeVertex)) { + if (myMapRemoved.Contains(aShapeVertex)) { + // This vertex is removed. + const TopAbs_Orientation anOri = aShapeVertex.Orientation(); + + if (anOri == TopAbs_FORWARD || anOri == TopAbs_REVERSED) { + // This edge will disappear from the result. + return; + } + } else { + // This vertex is not removed. + aVtxList.Append(aShapeVertex); + } + } + } + + TopoDS_Shape aNewEdge = makeShape(theEdge, aVtxList); + + myMapModified.ChangeFind(theEdge).Append(aNewEdge); +} + +//======================================================================= +//function : processWire +//purpose : +//======================================================================= +void GEOMAlgo_Extractor::processWire(const TopoDS_Shape &theWire) +{ + // Get parent face for the wire. + TopoDS_Face aFace; + + if (myMapShapeAnc.IsBound(theWire)) { + TopTools_ListIteratorOfListOfShape anIter(myMapShapeAnc.Find(theWire)); + + for (; anIter.More(); anIter.Next()) { + const TopoDS_Shape &aParent = anIter.Value(); + + if (aParent.ShapeType() == TopAbs_FACE) { + aFace = TopoDS::Face(aParent.Oriented(TopAbs_FORWARD)); + break; + } + } + } + + TopoDS_Wire aWire = TopoDS::Wire(theWire); + BRepTools_WireExplorer anExp(aWire, aFace); + NCollection_List aListListEdges; + TopTools_ListOfShape aListEdges; + + for (; anExp.More(); anExp.Next()) { + const TopoDS_Edge &anEdge = anExp.Current(); + + if (myMapRemoved.Contains(anEdge)) { + // This edge is removed. + if (!aListEdges.IsEmpty()) { + aListListEdges.Append(aListEdges); + aListEdges.Clear(); + } + } else if (myMapModified.IsBound(anEdge)) { + // This edge is modified. + TopTools_ListOfShape aModifEdges; + + getModified(anEdge, aModifEdges); + + if (aModifEdges.IsEmpty()) { + // This edge is not created. + if (!aListEdges.IsEmpty()) { + aListListEdges.Append(aListEdges); + aListEdges.Clear(); + } + } else { + const TopoDS_Shape aModifEdge = oriented(aModifEdges.First(), anEdge); + + aListEdges.Append(aModifEdge); + } + } else { + // Get an edge as it is. + aListEdges.Append(anEdge); + } + } + + if (!aListEdges.IsEmpty()) { + aListListEdges.Append(aListEdges); + } + + if (!aListListEdges.IsEmpty()) { + TopTools_ListOfShape aListWires; + + makeWires(theWire, aListListEdges, aListWires); + myMapModified.ChangeFind(theWire) = aListWires; + } +} + +//======================================================================= +//function : processFOrSo +//purpose : +//======================================================================= +void GEOMAlgo_Extractor::processFOrSo(const TopoDS_Shape &theFOrSo) +{ + Standard_Boolean isToCreate = Standard_True; + TopTools_ListOfShape aClosedSubShapes; + TopTools_ListOfShape aNewShapes; + TopoDS_Shape anOuterSubShape; + TopAbs_ShapeEnum aShapeType; + TopAbs_ShapeEnum aSubShapeType; + + if (theFOrSo.ShapeType() == TopAbs_FACE) { + aShapeType = TopAbs_FACE; + aSubShapeType = TopAbs_WIRE; + anOuterSubShape = BRepTools::OuterWire(TopoDS::Face(theFOrSo)); + } else { + aShapeType = TopAbs_SOLID; + aSubShapeType = TopAbs_SHELL; + anOuterSubShape = BRepClass3d::OuterShell(TopoDS::Solid(theFOrSo)); + } + + // Process an outer sub-shape. + if (myMapRemoved.Contains(anOuterSubShape)) { + isToCreate = Standard_False; + } else if (myMapModified.IsBound(anOuterSubShape)) { + TopTools_ListOfShape aModifSubShapes; + + getModified(anOuterSubShape, aModifSubShapes); + + // Check if there is a closed direct sub-shape. + TopTools_ListIteratorOfListOfShape anIter(aModifSubShapes); + TopoDS_Shape aClosedSubShape; + + for (isToCreate = Standard_False; anIter.More(); anIter.Next()) { + const TopoDS_Shape &aSubShape = anIter.Value(); + + if (aSubShape.ShapeType() == aSubShapeType && aSubShape.Closed()) { + if (isToCreate) { + // There is another closed sub-shape. NEVERREACHED. + // No need to create a new shape. + isToCreate = Standard_False; + break; + } else { + // Remember the closed sub-shape. + isToCreate = Standard_True; + aClosedSubShape = aSubShape; + } + } + } + + if (isToCreate) { + // Add a closed sub-shape. + const TopoDS_Shape aNewSubShape = + oriented(aClosedSubShape, anOuterSubShape); + + aClosedSubShapes.Append(aNewSubShape); + } + + // Copy shapes to the list of other shapes. + for (anIter.Initialize(aModifSubShapes); anIter.More(); anIter.Next()) { + const TopoDS_Shape aNewShape = oriented(anIter.Value(), anOuterSubShape); + + if (!isToCreate || !aNewShape.IsSame(aClosedSubShape)) { + aNewShapes.Append(aNewShape); + } + } + } else { + aClosedSubShapes.Append(anOuterSubShape); + } + + // Treat holes. + TopoDS_Iterator anIter(theFOrSo); + + for (; anIter.More(); anIter.Next()) { + const TopoDS_Shape &aSubShape = anIter.Value(); + + if (aSubShape.IsSame(anOuterSubShape)) { + // Skip an outer sub-shape. + continue; + } + + if (myMapModified.IsBound(aSubShape)) { + // This is a modified sub-shape. + TopTools_ListOfShape aModifSubShapes; + + getModified(aSubShape, aModifSubShapes); + + TopTools_ListIteratorOfListOfShape anIter(aModifSubShapes); + + for (; anIter.More(); anIter.Next()) { + const TopoDS_Shape aNewShape = oriented(anIter.Value(), aSubShape); + + if (isToCreate) { + if (aNewShape.ShapeType() == aSubShapeType && aNewShape.Closed()) { + // This is a closed sub-shape. + aClosedSubShapes.Append(aNewShape); + } else { + aNewShapes.Append(aNewShape); + } + } else { + aNewShapes.Append(aNewShape); + } + } + } else if (!myMapRemoved.Contains(aSubShape)) { + // The shape is not modified. + if (isToCreate) { + aClosedSubShapes.Append(aSubShape); + } else { + aNewShapes.Append(aSubShape); + } + } + } + + if (isToCreate) { + // Create a new shape. + TopoDS_Shape aNewShape = makeShape(theFOrSo, aClosedSubShapes); + + aNewShapes.Prepend(aNewShape); + } + + if (!aNewShapes.IsEmpty()) { + // Store modified shapes. + myMapModified.ChangeFind(theFOrSo) = aNewShapes; + } +} + +//======================================================================= +//function : processShOrCS +//purpose : +//======================================================================= +void GEOMAlgo_Extractor::processShOrCS(const TopoDS_Shape &theShOrCS) +{ + // Treat sub-shapes. + TopoDS_Iterator anIter(theShOrCS); + TopTools_ListOfShape aNewSubShapes; + TopTools_ListOfShape aNewOtherShapes; + TopAbs_ShapeEnum aSubShapeType; + TopAbs_ShapeEnum aSubSubShapeType; + + if (theShOrCS.ShapeType() == TopAbs_SHELL) { + aSubShapeType = TopAbs_FACE; + aSubSubShapeType = TopAbs_EDGE; + } else { // comp-solid + aSubShapeType = TopAbs_SOLID; + aSubSubShapeType = TopAbs_FACE; + } + + for (; anIter.More(); anIter.Next()) { + const TopoDS_Shape &aSubShape = anIter.Value(); + + if (myMapModified.IsBound(aSubShape)) { + TopTools_ListOfShape aModifList; + + getModified(aSubShape, aModifList); + + // Copy shapes to the list of other shapes. + TopTools_ListIteratorOfListOfShape anIter(aModifList); + + for (; anIter.More(); anIter.Next()) { + const TopoDS_Shape aNewShape = oriented(anIter.Value(), aSubShape); + + if (aNewShape.ShapeType() == aSubShapeType) { + aNewSubShapes.Append(aNewShape); + } else { + aNewOtherShapes.Append(aNewShape); + } + } + } else if (!myMapRemoved.Contains(aSubShape)) { + // Shape is neither removed nor modified. Add it as it is. + if (aSubShape.ShapeType() == aSubShapeType) { + aNewSubShapes.Append(aSubShape); + } else { + aNewOtherShapes.Append(aSubShape); + } + } + } + + // Group sub-shapes via bounds + TopTools_ListOfShape aNewShapes; + + groupViaBounds(theShOrCS, aNewSubShapes, aNewShapes); + aNewOtherShapes.Prepend(aNewShapes); + + if (!aNewOtherShapes.IsEmpty()) { + // Store modified shapes. + myMapModified.ChangeFind(theShOrCS) = aNewOtherShapes; + } +} + +//======================================================================= +//function : processCompound +//purpose : +//======================================================================= +void GEOMAlgo_Extractor::processCompound(const TopoDS_Shape &theCompound) +{ + // Treat sub-shapes. + TopoDS_Iterator anIter(theCompound); + TopTools_ListOfShape aNewSubShapes; + + for (; anIter.More(); anIter.Next()) { + const TopoDS_Shape &aSubShape = anIter.Value(); + + if (myMapModified.IsBound(aSubShape)) { + TopTools_ListOfShape aModifList; + + getModified(aSubShape, aModifList); + + // Copy shapes to the list of other shapes. + TopTools_ListIteratorOfListOfShape anIter(aModifList); + + for (; anIter.More(); anIter.Next()) { + const TopoDS_Shape aNewShape = oriented(anIter.Value(), aSubShape); + + aNewSubShapes.Append(aNewShape); + } + } else if (!myMapRemoved.Contains(aSubShape)) { + // Shape is neither removed nor modified. Add it as it is. + aNewSubShapes.Append(aSubShape); + } + } + + if (!aNewSubShapes.IsEmpty()) { + if (aNewSubShapes.Extent() == 1) { + // Avoid creation of new compound for a single sub-shape. + myMapModified.ChangeFind(theCompound).Append(aNewSubShapes.First()); + } else { + TopoDS_Shape aNewShape = makeShape(theCompound, aNewSubShapes); + + // Store modified shapes. + myMapModified.ChangeFind(theCompound).Append(aNewShape); + } + } +} + +//======================================================================= +//function : removeBoundsOnFOrSo +//purpose : +//======================================================================= +void GEOMAlgo_Extractor::removeBoundsOnFOrSo(const TopAbs_ShapeEnum theType) +{ + // Get bounds on faces or solids. + TopExp_Explorer anExp(myShape, theType); + TopTools_MapOfShape aMapFence; + TopAbs_ShapeEnum aBoundType; + TopAbs_ShapeEnum aComplexBndType; + TopTools_IndexedMapOfShape aMapBounds; + + if (theType == TopAbs_FACE) { + aBoundType = TopAbs_EDGE; + aComplexBndType = TopAbs_WIRE; + } else { // solid + aBoundType = TopAbs_FACE; + aComplexBndType = TopAbs_SHELL; + } + + for (; anExp.More(); anExp.Next()) { + const TopoDS_Shape &aShape = anExp.Current(); + + if (aMapFence.Add(aShape)) { + if (myMapRemoved.Contains(aShape)) { + continue; + } + + if (myMapModified.IsBound(aShape)) { + TopTools_ListOfShape aNewShapes; + + getModified(aShape, aNewShapes); + + if (!aNewShapes.IsEmpty()) { + const TopoDS_Shape &aNewShape = aNewShapes.First(); + + if (aNewShape.ShapeType() == theType) { + // Get bounds from the modified shape. + TopExp::MapShapes(aNewShape, aBoundType, aMapBounds); + } + } + } else { + // Get bounds from the original shapes. + TopExp::MapShapes(aShape, aBoundType, aMapBounds); + } + } + } + + // Remove duplicated bounds from the faces or solids + aMapFence.Clear(); + + for (anExp.Init(myShape, theType); anExp.More(); anExp.Next()) { + const TopoDS_Shape &aShape = anExp.Current(); + + if (aMapFence.Add(aShape)) { + if (myMapModified.IsBound(aShape)) { + TopTools_ListOfShape &aNewShapes = + myMapModified.ChangeFind(aShape); + TopTools_ListIteratorOfListOfShape anIter(aNewShapes); + + while (anIter.More()) { + const TopoDS_Shape &aSubShape = anIter.Value(); + Standard_Boolean isToRm = Standard_False; + + if (aSubShape.ShapeType() == aBoundType) { + // edge or face + isToRm = aMapBounds.Contains(aSubShape); + } else if (aSubShape.ShapeType() == aComplexBndType) { + // wire or shell + TopTools_ListOfShape aNewBounds; + Standard_Boolean isModified; + + if (theType == TopAbs_FACE) { + isModified = removeCommonEdges(aSubShape, aMapBounds, aNewBounds); + } else { + isModified = removeCommonFaces(aSubShape, aMapBounds, aNewBounds); + } + + if (isModified) { + myMapModified.Bind(aSubShape, aNewBounds); + aNewShapes.InsertBefore(aNewBounds, anIter); + isToRm = Standard_True; // To remove unmodified bound. + } + } + + if (isToRm) { + aNewShapes.Remove(anIter); + } else { + anIter.Next(); + } + } + } + } + } +} + +//======================================================================= +//function : oriented +//purpose : +//======================================================================= +TopoDS_Shape GEOMAlgo_Extractor::oriented(const TopoDS_Shape &theShape, + const TopoDS_Shape &theContext) +{ + const TopAbs_Orientation aShapeOri = theShape.Orientation(); + const TopAbs_Orientation aContextOri = theContext.Orientation(); + TopoDS_Shape aResult = theShape; + + aResult.Orientation(TopAbs::Compose(aShapeOri, aContextOri)); + + return aResult; +} + +//======================================================================= +//function : makeShape +//purpose : +//======================================================================= +TopoDS_Shape GEOMAlgo_Extractor::makeShape + (const TopoDS_Shape &theShape, + const TopTools_ListOfShape &theSubShapes) +{ + TopoDS_Shape aResult = getShapeFromSubShapes(theShape, theSubShapes); + + if (aResult.IsNull()) { + // Create a new shape. + BRep_Builder aBuilder; + TopTools_ListIteratorOfListOfShape anIter(theSubShapes); + TopTools_MapOfShape aMapFence; + + aResult = theShape.EmptyCopied(); + aMapFence.Clear(); + + for (; anIter.More(); anIter.Next()) { + const TopoDS_Shape &aSubShape = anIter.Value(); + + if (aMapFence.Add(aSubShape)) { + aBuilder.Add(aResult, aSubShape); + + // Fill the map of new shape - ancestors. + if (!myMapNewShapeAnc.IsBound(aSubShape)) { + myMapNewShapeAnc.Bind(aSubShape, TopTools_ListOfShape()); + } + + myMapNewShapeAnc.ChangeFind(aSubShape).Append(aResult); + } + } + } + + return aResult; +} + +//======================================================================= +//function : getShapeFromSubShapes +//purpose : +//======================================================================= +TopoDS_Shape GEOMAlgo_Extractor::getShapeFromSubShapes + (const TopoDS_Shape &theShape, + const TopTools_ListOfShape &theSubShapes) +{ + // Fill the map of sub-shapes. + TopTools_ListIteratorOfListOfShape anIter(theSubShapes); + TopTools_MapOfShape aMapSubShapes; + TopoDS_Shape aFirstSubShape = theSubShapes.First(); + TopoDS_Shape aResult; + + for (; anIter.More(); anIter.Next()) { + aMapSubShapes.Add(anIter.Value()); + } + + // Check if such a shape is already created. + if (!aMapSubShapes.IsEmpty()) { + TopTools_MapIteratorOfMapOfShape aMapIt(aMapSubShapes); + Standard_Boolean isFirst = Standard_True; + TopTools_MapOfShape aMapAncs[2]; + Standard_Integer iCur = 0; + Standard_Integer iPrev = 1; + + for (; aMapIt.More(); aMapIt.Next()) { + const TopoDS_Shape &aSubShape = aMapIt.Key(); + + // Switch iCur and iPrev. + iCur = iCur ? 0 : 1; + iPrev = iPrev ? 0 : 1; + + if (myMapNewShapeAnc.IsBound(aSubShape)) { + TopTools_ListIteratorOfListOfShape + anAncIt(myMapNewShapeAnc.Find(aSubShape)); + + if (isFirst) { + // This is a first loop. Just fill the map of ancestors. + for (; anAncIt.More(); anAncIt.Next()) { + aMapAncs[iCur].Add(anAncIt.Value()); + } + } else { + // Add in aMapAnc[iCur] elements that are only in aMapAnc[iPrev]. + for (aMapAncs[iCur].Clear(); anAncIt.More(); anAncIt.Next()) { + const TopoDS_Shape &anAncestor = anAncIt.Value(); + + if (aMapAncs[iPrev].Contains(anAncestor)) { + aMapAncs[iCur].Add(anAncIt.Value()); + } + } + } + + if (aMapAncs[iCur].IsEmpty()) { + // There is no common shape. It means that + // the result should be a new shape. + aMapAncs[iCur].Clear(); + break; + } + } else { + // This is a new sub-shape. So the result shape is new. + aMapAncs[iCur].Clear(); + break; + } + } + + if (!aMapAncs[iCur].IsEmpty()) { + // Get exactly the same shape. + const TopAbs_ShapeEnum aType = theShape.ShapeType(); + + for (aMapIt.Initialize(aMapAncs[iCur]); aMapIt.More(); aMapIt.Next()) { + const TopoDS_Shape &aShape = aMapIt.Key(); + + if (aShape.ShapeType() == aType) { + // Check sub-shapes. + TopoDS_Iterator aSubShIt(aShape); + TopAbs_Orientation aNewOri = TopAbs_FORWARD; + Standard_Boolean isComposedOri = Standard_False; + + for (; aSubShIt.More(); aSubShIt.Next()) { + const TopoDS_Shape &aSubSh = aSubShIt.Value(); + + if (!aMapSubShapes.Contains(aSubSh)) { + // There are another sub-shapes in the ancestor. + break; + } + + if (!isComposedOri && aSubSh.IsSame(aFirstSubShape)) { + // Compose orientaiton. + isComposedOri = Standard_True; + aNewOri = TopAbs::Compose + (aFirstSubShape.Orientation(), aSubSh.Orientation()); + } + } + + if (!aSubShIt.More()) { + // That is the same shape. Compose the orientation. + aResult = aShape; + aResult.Orientation(aNewOri); + break; + } + } + } + } + } + + return aResult; +} + +//======================================================================= +//function : makeResult +//purpose : +//======================================================================= +TopoDS_Shape GEOMAlgo_Extractor::makeResult(const TopoDS_Shape &theShape) +{ + TopoDS_Shape aResult; + + if (!myMapRemoved.Contains(theShape)) { + if (myMapModified.IsBound(theShape)) { + // The shape is modified. + TopTools_ListOfShape aListModif; + + getModified(theShape, aListModif); + + const Standard_Integer aNbShapes = aListModif.Extent(); + + if (aNbShapes == 1) { + aResult = oriented(aListModif.First(), theShape); + } else if (aNbShapes > 1) { + // Build a result as a compound + TopTools_ListIteratorOfListOfShape anIter(aListModif); + BRep_Builder aBuilder; + TopoDS_Compound aCompound; + TopTools_MapOfShape aMapFence; + + aBuilder.MakeCompound(aCompound); + + for (; anIter.More(); anIter.Next()) { + const TopoDS_Shape aModifShape = oriented(anIter.Value(), theShape); + + if (aMapFence.Add(aModifShape)) { + aBuilder.Add(aCompound, aModifShape); + } + } + + aResult = aCompound; + } + } else { + // The result is not modified shape. + aResult = theShape; + } + } + + return aResult; +} + +//======================================================================= +//function : makeHistory +//purpose : +//======================================================================= +void GEOMAlgo_Extractor::makeHistory(const TopoDS_Shape &theShape, + TopTools_MapOfShape &theMapFence) +{ + if (theMapFence.Add(theShape)) { + Standard_Boolean isKept = Standard_True; + + if (myMapRemoved.Contains(theShape)) { + myRemoved.Append(theShape); + isKept = Standard_False; + } else if (myMapModified.IsBound(theShape)) { + TopTools_ListOfShape aListModif; + + getModified(theShape, aListModif, theShape.ShapeType()); + + Standard_Boolean isModif = !aListModif.IsEmpty(); + const TopAbs_ShapeEnum aType = theShape.ShapeType(); + + if (isModif) { + // Add the new shapes. + TopTools_ListIteratorOfListOfShape anIter(aListModif); + + // Skip the first shape. + for (anIter.Next(); anIter.More(); anIter.Next()) { + myNew.Append(anIter.Value()); + } + } + + if (isModif) { + myModified.Append(theShape); + } else { + myRemoved.Append(theShape); + } + + isKept = Standard_False; + } + + if (!isKept) { + // Collect history for children. + TopoDS_Iterator anIter(theShape); + + for (; anIter.More(); anIter.Next()) { + const TopoDS_Shape &aSubShape = anIter.Value(); + + makeHistory(aSubShape, theMapFence); + } + } + } +} + +//======================================================================= +//function : removeCommonEdges +//purpose : +//======================================================================= +Standard_Boolean GEOMAlgo_Extractor::removeCommonEdges + (const TopoDS_Shape &theWire, + const TopTools_IndexedMapOfShape &theMapEdgesToRm, + TopTools_ListOfShape &theNewWires) +{ + TopExp_Explorer anExp(theWire, TopAbs_EDGE); + NCollection_List aListListEdges; + TopTools_ListOfShape aListEdges; + Standard_Boolean isModified = Standard_False; + TopoDS_Vertex aVtx[2]; + + for (; anExp.More(); anExp.Next()) { + const TopoDS_Shape &anEdge = anExp.Current(); + + if (theMapEdgesToRm.Contains(anEdge)) { + // This edge is removed. + TopExp::Vertices(TopoDS::Edge(anEdge), aVtx[0], aVtx[1]); + + // Skip edges that have same first and last vertices. + if (aVtx[0].IsNull() || !aVtx[0].IsSame(aVtx[1])) { + if (!aListEdges.IsEmpty()) { + aListListEdges.Append(aListEdges); + aListEdges.Clear(); + } + } + + isModified = Standard_True; + } else { + aListEdges.Append(anEdge); + } + } + + if (!aListEdges.IsEmpty()) { + aListListEdges.Append(aListEdges); + } + + if (isModified && !aListListEdges.IsEmpty()) { + // Make wires. + makeWires(theWire, aListListEdges, theNewWires); + } + + return isModified; +} + +//======================================================================= +//function : removeCommonFaces +//purpose : +//======================================================================= +Standard_Boolean GEOMAlgo_Extractor::removeCommonFaces + (const TopoDS_Shape &theShell, + const TopTools_IndexedMapOfShape &theMapFacesToRm, + TopTools_ListOfShape &theNewShells) +{ + TopExp_Explorer anExp(theShell, TopAbs_FACE); + TopTools_ListOfShape aListFaces; + Standard_Boolean isModified = Standard_False; + + for (; anExp.More(); anExp.Next()) { + const TopoDS_Shape &aFace = anExp.Current(); + + if (theMapFacesToRm.Contains(aFace)) { + isModified = Standard_True; + } else { + aListFaces.Append(aFace); + } + } + + if (isModified && !aListFaces.IsEmpty()) { + // Create new shells. + groupViaBounds(theShell, aListFaces, theNewShells); + } + + return isModified; +} + +//======================================================================= +//function : makeWires +//purpose : +//======================================================================= +void GEOMAlgo_Extractor::makeWires + (const TopoDS_Shape &theWire, + NCollection_List &theListListEdges, + TopTools_ListOfShape &theWires) +{ + if (theListListEdges.Size() > 1) { + // Check if it is possible to merge first and last lists of edges. + TopoDS_Edge anEdgeFirst = TopoDS::Edge(theListListEdges.First().First()); + TopoDS_Edge anEdgeLast = TopoDS::Edge(theListListEdges.Last().Last()); + TopoDS_Vertex aCommonVtx; + + if (TopExp::CommonVertex(anEdgeFirst, anEdgeLast, aCommonVtx)) { + // Merge First and last lists of edges. + theListListEdges.First().Prepend(theListListEdges.Last()); + // Remove the last list. + NCollection_List::Iterator anIter(theListListEdges); + + for (;anIter.More(); anIter.Next()) { + if (anIter.Value().IsEmpty()) { + theListListEdges.Remove(anIter); + break; + } + } + } + } + + // Create wires. + NCollection_List::Iterator anIter(theListListEdges); + + for (;anIter.More(); anIter.Next()) { + const TopTools_ListOfShape &anEdges = anIter.Value(); + TopoDS_Shape aNewWireShape = makeShape(theWire, anEdges); + TopoDS_Wire aNewWire = TopoDS::Wire(aNewWireShape); + TopoDS_Vertex aV[2]; + + TopExp::Vertices(aNewWire, aV[0], aV[1]); + + if (!aV[0].IsNull() && !aV[1].IsNull()) { + aNewWire.Closed(aV[0].IsSame(aV[1])); + } + + theWires.Append(aNewWire); + } +} + +//======================================================================= +//function : groupViaBounds +//purpose : +//======================================================================= +void GEOMAlgo_Extractor::groupViaBounds + (const TopoDS_Shape &theShape, + const TopTools_ListOfShape &theSubShapes, + TopTools_ListOfShape &theNewShapes) +{ + const Standard_Boolean isShell = theShape.ShapeType() == TopAbs_SHELL; + TopAbs_ShapeEnum aBoundType; + + if (isShell) { + aBoundType = TopAbs_EDGE; + } else { // comp-solid + aBoundType = TopAbs_FACE; + } + + // Group connected sub-shapes. + NCollection_Sequence aGroupedSubShapes; + NCollection_Sequence aBounds; + TopTools_ListIteratorOfListOfShape anIt(theSubShapes); + Standard_Integer i; + + for (; anIt.More(); anIt.Next()) { + // Find a zone a sub-shape is connected to. + const TopoDS_Shape &aSubShape = anIt.Value(); + TColStd_MapOfInteger aMapIndices; + const Standard_Integer aNbZones = aBounds.Size(); + TopExp_Explorer anExp(aSubShape, aBoundType); + Standard_Integer j; + + for (; anExp.More(); anExp.Next()) { + const TopoDS_Shape &aSubSubShape = anExp.Current(); + + // Check each zone. + for (i = 1; i <= aNbZones; ++i) { + if (!aMapIndices.Contains(i)) { + if (aBounds.Value(i).Contains(aSubSubShape)) { + // The current sub-shape belongs to this zone. + aMapIndices.Add(i); + break; + } + } + } + } + + if (aMapIndices.IsEmpty()) { + // Create a new zone. + aGroupedSubShapes.Append(TopTools_ListOfShape()); + aBounds.Append(TopTools_MapOfShape()); + aGroupedSubShapes.ChangeLast().Append(aSubShape); + anExp.Init(aSubShape, aBoundType); + + TopTools_MapOfShape &aLastZoneBound = aBounds.ChangeLast(); + + for (; anExp.More(); anExp.Next()) { + aLastZoneBound.Add(anExp.Current()); + } + } else { + // Merge zones. Get the first zone. + for (i = 1; i <= aNbZones; ++i) { + if (aMapIndices.Contains(i)) { + break; + } + } + + // Merge other zones with the first one. + TopTools_ListOfShape &aZoneSubShapes = aGroupedSubShapes.ChangeValue(i); + TopTools_MapOfShape &aZoneBounds = aBounds.ChangeValue(i); + + for (j = i + 1; j <= aNbZones; ++j) { + if (aMapIndices.Contains(j)) { + aZoneSubShapes.Append(aGroupedSubShapes.ChangeValue(j)); + + TopTools_MapIteratorOfMapOfShape aMapIt(aBounds.Value(j)); + + for (; aMapIt.More(); aMapIt.Next()) { + aZoneBounds.Add(aMapIt.Key()); + } + } + } + + // Remove merged zones. + for (j = aNbZones; j > i; --j) { + aGroupedSubShapes.Remove(j); + aBounds.Remove(j); + } + + // Add aSubShape to merged zone. + aZoneSubShapes.Append(aSubShape); + anExp.Init(aSubShape, aBoundType); + + for (; anExp.More(); anExp.Next()) { + const TopoDS_Shape &aSubSubShape = anExp.Current(); + + if (!aZoneBounds.Add(aSubSubShape)) { + aZoneBounds.Remove(aSubSubShape); + } + } + } + } + + // Construct new shapes from sub-shapes. + const Standard_Integer aNbGroups = aGroupedSubShapes.Size(); + TopTools_ListOfShape aNewSubShapes; + + for (i = 1; i <= aNbGroups; ++i) { + const TopTools_ListOfShape &aListSubShapes = aGroupedSubShapes.Value(i); + + if (!isShell && aListSubShapes.Extent() == 1) { + // Avoid creation of comp-solid with a single solid. + aNewSubShapes.Append(aListSubShapes.First()); + } else { + TopoDS_Shape aNewShape = makeShape(theShape, aListSubShapes); + + if (aBounds.Value(i).IsEmpty()) { + // This is a closed shape. + aNewShape.Closed(Standard_True); + } + + theNewShapes.Append(aNewShape); + } + } + + // Append the list of single solids (if it is filled). + theNewShapes.Append(aNewSubShapes); +} + +//======================================================================= +//function : getModified +//purpose : +//======================================================================= +void GEOMAlgo_Extractor::getModified(const TopoDS_Shape &theShape, + TopTools_ListOfShape &theModifShapes, + const TopAbs_ShapeEnum theShapeType) +{ + // This shape is modified. + TopTools_ListIteratorOfListOfShape anIt(myMapModified.Find(theShape)); + + for (; anIt.More(); anIt.Next()) { + const TopoDS_Shape &aSubShape = anIt.Value(); + + if (theShapeType == TopAbs_SHAPE || aSubShape.ShapeType() == theShapeType) { + if (myMapModified.IsBound(aSubShape)) { + getModified(aSubShape, theModifShapes); + } else { + theModifShapes.Append(aSubShape); + } + } + } +} + + +// +// myErrorStatus : +// +// 10 -myShape=NULL +// 11 -mySubShapes contains not only sub-shapes of myShape. +// 12 -Can't remove the main shape. +// 13 -mySubShapes contains seam edges in context of faces. +// 14 -mySubShapes contains degenerated edges in context of faces. +// +// myWarningStatus : +// +// 10 -mySubShapes is empty +// diff --git a/src/GEOMAlgo/GEOMAlgo_Extractor.hxx b/src/GEOMAlgo/GEOMAlgo_Extractor.hxx new file mode 100644 index 000000000..9e9215334 --- /dev/null +++ b/src/GEOMAlgo/GEOMAlgo_Extractor.hxx @@ -0,0 +1,363 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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 +// + +// File: GEOMAlgo_Extractor.hxx +// Author: Sergey KHROMOV + +#ifndef _GEOMAlgo_Extractor_HeaderFile +#define _GEOMAlgo_Extractor_HeaderFile + + +#include + +#include +#include +#include +#include +#include + +class TopTools_IndexedMapOfShape; + + +/** + * \brief This class encapsulates an algorithm of extraction of sub-shapes + * from the main shape. + */ +class GEOMAlgo_Extractor : public GEOMAlgo_Algo +{ +public: + + /** + * \brief Empty constructor. + */ + Standard_EXPORT GEOMAlgo_Extractor(); + + /** + * \brief Virtual destructor. + */ + Standard_EXPORT virtual ~GEOMAlgo_Extractor(); + + /** + * \brief This method sets the main shape. + * + * \param theShape the main shape. + */ + Standard_EXPORT void SetShape(const TopoDS_Shape &theShape); + + /** + * \brief This method returns the main shape. + * + * \return the main shape. + */ + const TopoDS_Shape &GetShape() const + { return myShape; } + + /** + * \brief This method sets the list of sub-shapes to be removed + * from the main shape. + * + * \param theSubShapes the sub-shapes to be removed. + */ + Standard_EXPORT void SetShapesToRemove + (const TopTools_ListOfShape &theSubShapes); + + /** + * \brief This method returns the list of sub-shapes to be removed + * from the main shape. + * + * \return the list of sub-shapes to be removed. + */ + const TopTools_ListOfShape &GetShapesToRemove() const + { return mySubShapes; } + + /** + * This method performs computation of the extracted shape. + */ + Standard_EXPORT virtual void Perform(); + + /** + * This method returns the result of the algorithm. + * + * \return the result of the operation. + */ + Standard_EXPORT const TopoDS_Shape &GetResult() const; + + /** + * \brief This method returns the sub-shapes removed from the main shape. + * + * \return the list of removed sub-shapes. + */ + const TopTools_ListOfShape &GetRemoved() const + { return myRemoved; } + + /** + * \brief This method returns the sub-shapes modified in the main shape. + * + * \return the list of modified sub-shapes. + */ + const TopTools_ListOfShape &GetModified() const + { return myModified; } + + /** + * \brief This method returns the newly created sub-shapes in the result + * shape. + * + * \return the list of new sub-shapes in result. + */ + const TopTools_ListOfShape &GetNew() const + { return myNew; } + +private: + + /** + * \brief This method reinitializes the shape. + */ + void clear(); + + /** + * \brief This method checks the input data. + */ + void checkData(); + + /** + * \brief This method fills the map of shapes and ancestors for the whole + * sub-shapes of theShape. This method is recursively called up to the lowest + * level of sub-shapes i.e. vertices. + * + * \param theShape the shape. + */ + void makeMapShapeAncestors(const TopoDS_Shape &theShape); + + /** + * \brief This method marks shapes to be removed and to be modified. + */ + void markShapes(); + + /** + * \brief This method marks theShape to be removed. If it is required, it + * recursively marks its sub-shapes to be removed. + * + * \param theShape the shape. + */ + void markRemoved(const TopoDS_Shape &theShape); + + /** + * \brief This method marks ancestors of theShape to be modified. It is + * recursively called up to the level of main shape. + * + * \param theShape the shape. + */ + void markAncestorsModified(const TopoDS_Shape &theShape); + + /** + * \brief This method performs computation of modified shapes of + * the provided type. + * + * \param theType the processed shape type. + */ + void processShapes(const TopAbs_ShapeEnum &theType); + + /** + * \brief This method performs computation of a modified edge. + * + * \param theEdge the modified edge (should be forward). + */ + void processEdge(const TopoDS_Shape &theEdge); + + /** + * \brief This method performs computation of a modified wire. + * + * \param theWire the modified wire (should be forward). + */ + void processWire(const TopoDS_Shape &theWire); + + /** + * \brief This method performs computation of a modified face or solid. + * + * \param theFOrSo the modified face or solid (should be forward). + */ + void processFOrSo(const TopoDS_Shape &theFOrSo); + + /** + * \brief This method performs computation of a modified shell or comp-solid. + * + * \param theShOrCS the modified shell or comp-solid (should be forward). + */ + void processShOrCS(const TopoDS_Shape &theShOrCS); + + /** + * \brief This method performs computation of a modified compound. + * + * \param theCompound the modified compound (should be forward). + */ + void processCompound(const TopoDS_Shape &theCompound); + + /** + * \brief This method removes hanging edges (faces) built for faces (solids) + * if they lie on created faces (solids). + * + * \param theType the shape type. Should be either face or solid. + */ + void removeBoundsOnFOrSo(const TopAbs_ShapeEnum theType); + + /** + * \brief Returns theShape with an orientation composed with theContext's + * orientation. + * + * \param theShape the shape to be re-oriented. + * \param theContext the context shape. + */ + TopoDS_Shape oriented(const TopoDS_Shape &theShape, + const TopoDS_Shape &theContext); + + /** + * \brief This method makes a shape as an empty copy of theShape adding + * subshapes to it. + * + * \param theShape the shape to be copied (should be forward). + * \param theSubShapes the sub-shapes (should be oriented correctly). + * \return the modified shape. + */ + TopoDS_Shape makeShape(const TopoDS_Shape &theShape, + const TopTools_ListOfShape &theSubShapes); + + /** + * \brief This method returns the shape from the list of sub-shapes + * if there is any shape created already with these sub-shapes. + * If there is no such shape, null shape is returned. + * + * \param theShape the shape to be copied (should be forward). + * \param theSubShapes the sub-shapes (should be oriented correctly). + * \return the modified shape (or null if it is not found). + */ + TopoDS_Shape getShapeFromSubShapes(const TopoDS_Shape &theShape, + const TopTools_ListOfShape &theSubShapes); + + /** + * \brief This method makes the result for the given shape. If it is removed + * the result is a compound of its modified sub-shapes (or a single + * modified sub-shape if it in only one). + * + * \param theShape the shape. + * \return the result. + */ + TopoDS_Shape makeResult(const TopoDS_Shape &theShape); + + /** + * \brief This method fills the lists of shapes myRemoved, myModified and + * myNew with removed, modified and newly created shapes correspondingly. + * This method is called recursively for sub-shapes of the shape. + * + * \param theShape the shape. + * \param theMapFence the map of already treated shapes. + */ + void makeHistory(const TopoDS_Shape &theShape, + TopTools_MapOfShape &theMapFence); + + /** + * \brief This method removes edges that are in theMapEdgesToRm from + * theWire and re-creates one or more wires from the rest edges. theNewWires + * contains the modified wire(s). + * + * \param theWire the input wire. + * \param theMapEdgesToRm the map of edges to be extracted from theWire. + * \param theNewWires is the list of new wires. Output parameter. + * \return Standard_True if theWire is modified; Standard_False otherwise. + */ + Standard_Boolean removeCommonEdges + (const TopoDS_Shape &theWire, + const TopTools_IndexedMapOfShape &theMapEdgesToRm, + TopTools_ListOfShape &theNewWires); + + /** + * \brief This method removes faces that are in theMapFacesToRm from + * theShell and re-creates one or more shells from the rest faces. + * theNewShells contains the modified shell(s). + * + * \param theShell the input shell. + * \param theMapFacesToRm the map of faces to be extracted from theShell. + * \param theNewShells is the list of new shells. Output parameter. + * \return Standard_True if theShell is modified; Standard_False otherwise. + */ + Standard_Boolean removeCommonFaces + (const TopoDS_Shape &theShell, + const TopTools_IndexedMapOfShape &theMapFacesToRm, + TopTools_ListOfShape &theNewShells); + + /** + * \brief This method creates wires from the list of list of edges. + * + * \param theWire the input wire. + * \param theListListEdges the list of list of edges. Can be modified + * on output. + * \param theWires the list of created wires. Output parameter. + */ + void makeWires(const TopoDS_Shape &theWire, + NCollection_List &theListListEdges, + TopTools_ListOfShape &theWires); + + /** + * \brief This method collects the shapes in theShapes via common bounds. + * This method is used to group faces into shells via common edges or + * solids into compsolids via common faces. Collected lists of shapes + * are used to create new shapes from theShape that are returned in + * theNewShapes. theNewShapes is not cleared at first. + * + * \param theShape the original shape. + * \param theSubShapes the list of shapes to be connected. + * \param theNewShapes the list of newly created shapes. Output parameter. + */ + void groupViaBounds(const TopoDS_Shape &theShape, + const TopTools_ListOfShape &theSubShapes, + TopTools_ListOfShape &theNewShapes); + + /** + * \brief This method returns the list of modified shapes obtained + * from theShape. It performs recursive search in myMapModified. + * theModifShapes is not cleared at first. If theShapeType filter is equal + * to TopAbs_SHAPE (default value) all modified shapes will be returned, + * otherwise shapes of particular type will only be returned. + * + * \param theShape the shape examined. + * \param theModifShapes the list of modified shapes. Output parameter. + * \param theShapeType the shape type filter. + */ + void getModified(const TopoDS_Shape &theShape, + TopTools_ListOfShape &theModifShapes, + const TopAbs_ShapeEnum theShapeType = TopAbs_SHAPE); + +protected: + + TopoDS_Shape myShape; + TopoDS_Shape myResult; + TopTools_ListOfShape mySubShapes; + TopTools_ListOfShape myRemoved; + TopTools_ListOfShape myModified; + TopTools_ListOfShape myNew; + TopTools_DataMapOfShapeListOfShape myMapShapeAnc; + TopTools_MapOfShape myMapRemoved; + TopTools_DataMapOfShapeListOfShape myMapModified; + TopTools_DataMapOfShapeListOfShape myMapNewShapeAnc; + +}; + +#endif diff --git a/src/GEOMGUI/GEOM_images.ts b/src/GEOMGUI/GEOM_images.ts index cfcccd313..5ec53885f 100644 --- a/src/GEOMGUI/GEOM_images.ts +++ b/src/GEOMGUI/GEOM_images.ts @@ -1387,6 +1387,10 @@ ICO_TRANSFER_DATA transfer_data.png + + ICO_EXTRACTION + extract.png + ICO_IMPORT_SHAPE import.png @@ -1407,6 +1411,10 @@ ICON_DLG_TRANSFER_DATA transfer_data.png + + ICON_DLG_EXTRACTION + extract.png + ICON_DLG_SCALE_ALONG_AXES scale_along_axes.png diff --git a/src/GEOMGUI/GEOM_msg_en.ts b/src/GEOMGUI/GEOM_msg_en.ts index 7aef53a75..efb543e09 100644 --- a/src/GEOMGUI/GEOM_msg_en.ts +++ b/src/GEOMGUI/GEOM_msg_en.ts @@ -471,6 +471,10 @@ Please, select face, shell or solid and try again GEOM_COMPOUNDSOLID CompSolid + + GEOM_COMPSOLIDS + CompSolids + GEOM_COMPOUND_TITLE Create A Compound @@ -607,6 +611,10 @@ Please, select face, shell or solid and try again GEOM_EDGE Edge + + GEOM_EDGES + Edges + GEOM_EDGE_TITLE Create An Edge @@ -2024,6 +2032,10 @@ Please, select face, shell or solid and try again GEOM_SOLID Solid + + GEOM_SOLIDS + Solids + GEOM_SOLID_TITLE Solid Construction @@ -5236,6 +5248,18 @@ Please, select face, shell or solid and try again STB_TRANSFER_DATA Transfer Data + + TOP_EXTRACTION + Extract and Rebuild + + + MEN_EXTRACTION + Extract and Rebuild + + + STB_EXTRACTION + Extract and Rebuild + TOP_EXTENSION Extend Edge or Face @@ -7629,4 +7653,55 @@ Do you want to create new material? Rotation angle + + OperationGUI_ExtractionDlg + + GEOM_EXTRACT_TITLE + Extract and Rebuild + + + GEOM_EXTRACT_TYPE + Extraction type + + + GEOM_EXTRACT_INPUT_PARAMS + Input parameters + + + GEOM_EXTRACT_STATISTICS + Statistics + + + GEOM_EXTRACT_SUB_SHAPE_TYPE + Sub-shape type + + + GEOM_EXTRACT_FILTERED_SHAPES + Filtered shapes + + + GEOM_EXTRACT_SHAPES_TO_EXTRACT + Shapes to extract + + + GEOM_EXTRACT_REBUILD + Rebuild + + + GEOM_EXTRACT_REMOVED + Removed + + + GEOM_EXTRACT_MODIFIED + Modified + + + GEOM_EXTRACT_ADDED + Added + + + GEOM_EXTRACT_NAME + Extraction + + diff --git a/src/GEOMGUI/GEOM_msg_fr.ts b/src/GEOMGUI/GEOM_msg_fr.ts index a7111b94c..7db35f809 100644 --- a/src/GEOMGUI/GEOM_msg_fr.ts +++ b/src/GEOMGUI/GEOM_msg_fr.ts @@ -471,6 +471,10 @@ Choisissez une face, une coque ou un solide et essayez de nouveau GEOM_COMPOUNDSOLID Assemblage solide + + GEOM_COMPSOLIDS + CompSolids + GEOM_COMPOUND_TITLE Créer un assemblage @@ -607,6 +611,10 @@ Choisissez une face, une coque ou un solide et essayez de nouveau GEOM_EDGE Arête + + GEOM_EDGES + Edges + GEOM_EDGE_TITLE Créer une arête @@ -2016,6 +2024,10 @@ Choisissez une face, une coque ou un solide et essayez de nouveau GEOM_SOLID Solide + + GEOM_SOLIDS + Solids + GEOM_SOLID_TITLE Construction d'un solide @@ -5228,6 +5240,18 @@ Choisissez une face, une coque ou un solide et essayez de nouveau STB_TRANSFER_DATA Transfert de données + + TOP_EXTRACTION + Extract and Rebuild + + + MEN_EXTRACTION + Extract and Rebuild + + + STB_EXTRACTION + Extract and Rebuild + TOP_EXTENSION Extend Edge or Face @@ -7605,4 +7629,55 @@ Voulez-vous en créer un nouveau ? Longueur de l'angle + + OperationGUI_ExtractionDlg + + GEOM_EXTRACT_TITLE + Extract and Rebuild + + + GEOM_EXTRACT_TYPE + Extraction type + + + GEOM_EXTRACT_INPUT_PARAMS + Input parameters + + + GEOM_EXTRACT_STATISTICS + Statistics + + + GEOM_EXTRACT_SUB_SHAPE_TYPE + Sub-shape type + + + GEOM_EXTRACT_FILTERED_SHAPES + Filtered shapes + + + GEOM_EXTRACT_SHAPES_TO_EXTRACT + Shapes to extract + + + GEOM_EXTRACT_REBUILD + Rebuild + + + GEOM_EXTRACT_REMOVED + Removed + + + GEOM_EXTRACT_MODIFIED + Modified + + + GEOM_EXTRACT_ADDED + Added + + + GEOM_EXTRACT_NAME + Extraction + + diff --git a/src/GEOMGUI/GEOM_msg_ja.ts b/src/GEOMGUI/GEOM_msg_ja.ts index 0e42dfe3d..667818994 100644 --- a/src/GEOMGUI/GEOM_msg_ja.ts +++ b/src/GEOMGUI/GEOM_msg_ja.ts @@ -467,6 +467,10 @@ GEOM_COMPOUNDSOLID 固体のアセンブリ + + GEOM_COMPSOLIDS + CompSolids + GEOM_COMPOUND_TITLE コンパウンドの作成 @@ -603,6 +607,10 @@ GEOM_EDGE Edge + + GEOM_EDGES + Edges + GEOM_EDGE_TITLE エッジを作成 @@ -2019,6 +2027,10 @@ GEOM_SOLID ソリッド + + GEOM_SOLIDS + Solids + GEOM_SOLID_TITLE ソリッドの構築 @@ -5231,6 +5243,18 @@ STB_TRANSFER_DATA データ転送 + + TOP_EXTRACTION + Extract and Rebuild + + + MEN_EXTRACTION + Extract and Rebuild + + + STB_EXTRACTION + Extract and Rebuild + TOP_EXTENSION エッジまたは面の拡張 @@ -7598,4 +7622,55 @@ 回転角度 + + OperationGUI_ExtractionDlg + + GEOM_EXTRACT_TITLE + Extract and Rebuild + + + GEOM_EXTRACT_TYPE + Extraction type + + + GEOM_EXTRACT_INPUT_PARAMS + Input parameters + + + GEOM_EXTRACT_STATISTICS + Statistics + + + GEOM_EXTRACT_SUB_SHAPE_TYPE + Sub-shape type + + + GEOM_EXTRACT_FILTERED_SHAPES + Filtered shapes + + + GEOM_EXTRACT_SHAPES_TO_EXTRACT + Shapes to extract + + + GEOM_EXTRACT_REBUILD + Rebuild + + + GEOM_EXTRACT_REMOVED + Removed + + + GEOM_EXTRACT_MODIFIED + Modified + + + GEOM_EXTRACT_ADDED + Added + + + GEOM_EXTRACT_NAME + Extraction + + diff --git a/src/GEOMGUI/GeometryGUI.cxx b/src/GEOMGUI/GeometryGUI.cxx index 0d3f733a6..47aeffbf8 100644 --- a/src/GEOMGUI/GeometryGUI.cxx +++ b/src/GEOMGUI/GeometryGUI.cxx @@ -623,6 +623,7 @@ void GeometryGUI::OnGUIEvent( int id, const QVariant& theParam ) case GEOMOp::OpExtrudedBoss: // MENU OPERATION - EXTRUDED BOSS case GEOMOp::OpExtrudedCut: // MENU OPERATION - EXTRUDED CUT case GEOMOp::OpTransferData: // MENU OPERATION - TRANSFER DATA + case GEOMOp::OpExtraction: // MENU OPERATION - EXTRACT AND REBUILD libName = "OperationGUI"; break; case GEOMOp::OpSewing: // MENU REPAIR - SEWING @@ -1005,6 +1006,7 @@ void GeometryGUI::initialize( CAM_Application* app ) createGeomAction( GEOMOp::OpShapesOnShape, "GET_SHAPES_ON_SHAPE" ); createGeomAction( GEOMOp::OpSharedShapes, "GET_SHARED_SHAPES" ); createGeomAction( GEOMOp::OpTransferData, "TRANSFER_DATA" ); + createGeomAction( GEOMOp::OpExtraction, "EXTRACTION" ); createGeomAction( GEOMOp::OpExtrudedCut, "EXTRUDED_CUT" ); createGeomAction( GEOMOp::OpExtrudedBoss, "EXTRUDED_BOSS" ); createGeomAction( GEOMOp::OpFillet1d, "FILLET_1D" ); @@ -1262,6 +1264,7 @@ void GeometryGUI::initialize( CAM_Application* app ) createMenu( GEOMOp::OpShapesOnShape, operId, -1 ); createMenu( GEOMOp::OpSharedShapes, operId, -1 ); createMenu( GEOMOp::OpTransferData, operId, -1 ); + createMenu( GEOMOp::OpExtraction, operId, -1 ); createMenu( separator(), operId, -1 ); @@ -1438,6 +1441,7 @@ void GeometryGUI::initialize( CAM_Application* app ) createTool( GEOMOp::OpShapesOnShape, operTbId ); createTool( GEOMOp::OpSharedShapes, operTbId ); createTool( GEOMOp::OpTransferData, operTbId ); + createTool( GEOMOp::OpExtraction, operTbId ); int featTbId = createTool( tr( "TOOL_FEATURES" ), QString( "GEOMModification" ) ); createTool( GEOMOp::OpFillet1d, featTbId ); diff --git a/src/GEOMGUI/GeometryGUI_Operations.h b/src/GEOMGUI/GeometryGUI_Operations.h index 480b7d2fd..a80c3d093 100644 --- a/src/GEOMGUI/GeometryGUI_Operations.h +++ b/src/GEOMGUI/GeometryGUI_Operations.h @@ -166,6 +166,7 @@ namespace GEOMOp { OpExtrudedBoss = 3709, // MENU OPERATION - ETRUDED BOSS OpExtrudedCut = 3710, // MENU OPERATION - ETRUDED CUT OpTransferData = 3711, // MENU OPERATION - TRANSFER DATA + OpExtraction = 3712, // MENU OPERATION - EXTRACT AND REBUILD // RepairGUI -------------------//-------------------------------- OpSewing = 4000, // MENU REPAIR - SEWING OpSuppressFaces = 4001, // MENU REPAIR - SUPPRESS FACES diff --git a/src/GEOMImpl/CMakeLists.txt b/src/GEOMImpl/CMakeLists.txt index 5c59b2e72..9bdce7d48 100755 --- a/src/GEOMImpl/CMakeLists.txt +++ b/src/GEOMImpl/CMakeLists.txt @@ -79,6 +79,7 @@ SET(GEOMImpl_HEADERS GEOMImpl_ICircle.hxx GEOMImpl_ISpline.hxx GEOMImpl_IEllipse.hxx + GEOMImpl_IExtract.hxx GEOMImpl_IFillet.hxx GEOMImpl_IFillet1d.hxx GEOMImpl_IFillet2d.hxx diff --git a/src/GEOMImpl/GEOMImpl_IExtract.hxx b/src/GEOMImpl/GEOMImpl_IExtract.hxx new file mode 100644 index 000000000..35a2799e4 --- /dev/null +++ b/src/GEOMImpl/GEOMImpl_IExtract.hxx @@ -0,0 +1,73 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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 +// + +//NOTE: This is an intreface to a function for the Offset creation. +// +#include "GEOM_Function.hxx" +#include + +#define EXTRACT_SHAPE 1 +#define EXTRACT_IDS 2 +#define EXTRACT_REMOVED_IDS 3 +#define EXTRACT_MODIFIED_IDS 4 +#define EXTRACT_ADDED_IDS 5 + +class GEOMImpl_IExtract +{ + public: + + GEOMImpl_IExtract(Handle(GEOM_Function) theFunction): _func(theFunction) {} + + void SetShape(Handle(GEOM_Function) theShape) + { _func->SetReference(EXTRACT_SHAPE, theShape); } + + Handle(GEOM_Function) GetShape() + { return _func->GetReference(EXTRACT_SHAPE); } + + void SetSubShapeIDs(const Handle(TColStd_HArray1OfInteger)& theSubShapeIDs) + { _func->SetIntegerArray(EXTRACT_IDS, theSubShapeIDs); } + + Handle(TColStd_HArray1OfInteger) GetSubShapeIDs() + { return _func->GetIntegerArray(EXTRACT_IDS); } + + void SetRemovedIDs(const Handle(TColStd_HArray1OfInteger)& theRemovedIDs) + { _func->SetIntegerArray(EXTRACT_REMOVED_IDS, theRemovedIDs); } + + Handle(TColStd_HArray1OfInteger) GetRemovedIDs() + { return _func->GetIntegerArray(EXTRACT_REMOVED_IDS); } + + void SetModifiedIDs(const Handle(TColStd_HArray1OfInteger)& theModifiedIDs) + { _func->SetIntegerArray(EXTRACT_MODIFIED_IDS, theModifiedIDs); } + + Handle(TColStd_HArray1OfInteger) GetModifiedIDs() + { return _func->GetIntegerArray(EXTRACT_MODIFIED_IDS); } + + void SetAddedIDs(const Handle(TColStd_HArray1OfInteger)& theAddedIDs) + { _func->SetIntegerArray(EXTRACT_ADDED_IDS, theAddedIDs); } + + Handle(TColStd_HArray1OfInteger) GetAddedIDs() + { return _func->GetIntegerArray(EXTRACT_ADDED_IDS); } + + private: + + Handle(GEOM_Function) _func; +}; diff --git a/src/GEOMImpl/GEOMImpl_IShapesOperations.cxx b/src/GEOMImpl/GEOMImpl_IShapesOperations.cxx index 65716670c..c02c13f4d 100644 --- a/src/GEOMImpl/GEOMImpl_IShapesOperations.cxx +++ b/src/GEOMImpl/GEOMImpl_IShapesOperations.cxx @@ -36,6 +36,7 @@ #include "GEOMImpl_GlueDriver.hxx" #include "GEOMImpl_FillingDriver.hxx" +#include "GEOMImpl_IExtract.hxx" #include "GEOMImpl_IVector.hxx" #include "GEOMImpl_IShapes.hxx" #include "GEOMImpl_IShapeExtend.hxx" @@ -3163,6 +3164,107 @@ Handle(TColStd_HSequenceOfTransient) return aSeq; } +//============================================================================= +/*! + * MakeExtraction + */ +//============================================================================= +Handle(GEOM_Object) GEOMImpl_IShapesOperations::MakeExtraction + (const Handle(GEOM_Object) &theShape, + const Handle(TColStd_HArray1OfInteger) &theSubShapeIDs, + std::list &theStats) +{ + SetErrorCode(KO); + + if (theShape.IsNull()) { + return NULL; + } + + //Add a new Result object + Handle(GEOM_Object) aResult = + GetEngine()->AddObject(GetDocID(), GEOM_EXTRACTION); + + //Add a new Extraction function + Handle(GEOM_Function) aFunction = + aResult->AddFunction(GEOMImpl_ShapeDriver::GetID(), EXTRACTION); + + //Check if the function is set correctly + if (aFunction->GetDriverGUID() != GEOMImpl_ShapeDriver::GetID()) { + return NULL; + } + + Handle(GEOM_Function) aShape = theShape->GetLastFunction(); + + if (aShape.IsNull()) { + return NULL; + } + + GEOMImpl_IExtract aCI (aFunction); + + aCI.SetShape(aShape); + aCI.SetSubShapeIDs(theSubShapeIDs); + + //Compute the Edge value + try { + OCC_CATCH_SIGNALS; + if (!GetSolver()->ComputeFunction(aFunction)) { + SetErrorCode("Shape driver failed"); + + return NULL; + } + } + catch (Standard_Failure) { + Handle(Standard_Failure) aFail = Standard_Failure::Caught(); + SetErrorCode(aFail->GetMessageString()); + + return NULL; + } + + // Fill in statistics. + theStats.clear(); + + Handle(TColStd_HArray1OfInteger) aStatIDsArray[3] = + { aCI.GetRemovedIDs(), aCI.GetModifiedIDs(), aCI.GetAddedIDs() }; + int i; + int j; + + for (j = 0; j < 3; ++j) { + if (!aStatIDsArray[j].IsNull()) { + const int anUpperID = aStatIDsArray[j]->Upper(); + ExtractionStat aStat; + + for (i = aStatIDsArray[j]->Lower(); i <= anUpperID; ++i) { + aStat.indices.push_back(aStatIDsArray[j]->Value(i)); + } + + aStat.type = (ExtractionStatType) j; + theStats.push_back(aStat); + } + } + + //Make a Python command + GEOM::TPythonDump pd(aFunction); + + pd << aResult << " = geompy.MakeExtraction(" << theShape << ", ["; + + if (!theSubShapeIDs.IsNull()) { + const int aNbIDs = theSubShapeIDs->Upper(); + + for (i = theSubShapeIDs->Lower(); i < aNbIDs; ++i) { + pd << theSubShapeIDs->Value(i) << ", "; + } + + // Dump the last value without a comma. + pd << theSubShapeIDs->Value(i); + } + + pd << "])"; + + SetErrorCode(OK); + + return aResult; +} + //======================================================================= //function : getShapesOnSurfaceIDs /*! diff --git a/src/GEOMImpl/GEOMImpl_IShapesOperations.hxx b/src/GEOMImpl/GEOMImpl_IShapesOperations.hxx index d8e4f9fb2..38b375059 100644 --- a/src/GEOMImpl/GEOMImpl_IShapesOperations.hxx +++ b/src/GEOMImpl/GEOMImpl_IShapesOperations.hxx @@ -59,6 +59,25 @@ class GEOMImpl_IShapesOperations : public GEOM_IOperations All = Groups | Fields | SubShapes, }; + /** + * This enumeration represents an extraction statistics type. + */ + enum ExtractionStatType + { + EST_Removed, ///< Removed sub-shapes + EST_Modified, ///< Modified sub-shapes + EST_Added ///< Newly created sub-shapes + }; + + /*! + * This structure defines a format of extraction statistics. + */ + struct ExtractionStat + { + ExtractionStatType type; ///< Type of extraction statistics. + std::list indices; ///< Shape indices touched by this type of modification. + }; + Standard_EXPORT GEOMImpl_IShapesOperations(GEOM_Engine* theEngine, int theDocID); Standard_EXPORT ~GEOMImpl_IShapesOperations(); @@ -470,6 +489,21 @@ class GEOMImpl_IShapesOperations : public GEOM_IOperations const GEOMUtils::ComparisonCondition theCondition, const Standard_Real theTolerance); + /*! + * \brief Return the shape that is constructed from theShape without + * extracted sub-shapes from the input list. + * + * \param theShape the original shape. + * \param theSubShapeIDs the list of sub-shape IDs to be extracted from + * the original shape. + * \param theStats the operation statistics. Output parameter. + * \return the shape without extracted sub-shapes. + */ + Handle(GEOM_Object) MakeExtraction + (const Handle(GEOM_Object) &theShape, + const Handle(TColStd_HArray1OfInteger) &theSubShapeIDs, + std::list &theStats); + private: Handle(GEOM_Object) MakeShape (std::list theShapes, const Standard_Integer theObjectType, diff --git a/src/GEOMImpl/GEOMImpl_ShapeDriver.cxx b/src/GEOMImpl/GEOMImpl_ShapeDriver.cxx index 46bcd193e..471fd679b 100644 --- a/src/GEOMImpl/GEOMImpl_ShapeDriver.cxx +++ b/src/GEOMImpl/GEOMImpl_ShapeDriver.cxx @@ -22,6 +22,7 @@ #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include #include +#include // OCCT Includes #include @@ -109,6 +111,39 @@ #include +/** + * \brief This static function converts the list of shapes into an array + * of their IDs. If the input list is empty, null handle will be returned. + * this method doesn't check if a shape presents in theIndices map. + * + * \param theListOfShapes the list of shapes. + * \param theIndices the indexed map of shapes. + * \return the array of shape IDs. + */ +static Handle(TColStd_HArray1OfInteger) GetShapeIDs + (const TopTools_ListOfShape &theListOfShapes, + const TopTools_IndexedMapOfShape &theIndices) +{ + Handle(TColStd_HArray1OfInteger) aResult; + + if (!theListOfShapes.IsEmpty()) { + const Standard_Integer aNbShapes = theListOfShapes.Extent(); + TopTools_ListIteratorOfListOfShape anIter(theListOfShapes); + Standard_Integer i; + + aResult = new TColStd_HArray1OfInteger(1, aNbShapes); + + for (i = 1; anIter.More(); anIter.Next(), ++i) { + const TopoDS_Shape &aShape = anIter.Value(); + const Standard_Integer anIndex = theIndices.FindIndex(aShape); + + aResult->SetValue(i, anIndex); + } + } + + return aResult; +} + namespace { // check that compound includes only shapes of expected type @@ -918,6 +953,97 @@ Standard_Integer GEOMImpl_ShapeDriver::Execute(TFunction_Logbook& log) const } } } + } else if (aType == EXTRACTION) { + allowCompound = true; + + GEOMImpl_IExtract aCI(aFunction); + Handle(GEOM_Function) aRefShape = aCI.GetShape(); + TopoDS_Shape aShapeBase = aRefShape->GetValue(); + + if (aShapeBase.IsNull()) { + Standard_NullObject::Raise("Argument Shape is null"); + return 0; + } + + Handle(TColStd_HArray1OfInteger) anIDs = aCI.GetSubShapeIDs(); + TopTools_ListOfShape aListSubShapes; + TopTools_IndexedMapOfShape anIndices; + int i; + + TopExp::MapShapes(aShapeBase, anIndices); + + if (!anIDs.IsNull()) { + const int anUpperID = anIDs->Upper(); + const int aNbShapes = anIndices.Extent(); + + for (i = anIDs->Lower(); i <= anUpperID; ++i) { + const Standard_Integer anIndex = anIDs->Value(i); + + if (anIndex < 1 || anIndex > aNbShapes) { + TCollection_AsciiString aMsg(" Invalid index: "); + + aMsg += TCollection_AsciiString(anIndex); + StdFail_NotDone::Raise(aMsg.ToCString()); + return 0; + } + + const TopoDS_Shape &aSubShape = anIndices.FindKey(anIndex); + + aListSubShapes.Append(aSubShape); + } + } + + // Compute extraction. + GEOMAlgo_Extractor anExtractor; + + anExtractor.SetShape(aShapeBase); + anExtractor.SetShapesToRemove(aListSubShapes); + + anExtractor.Perform(); + + // Interprete results + Standard_Integer iErr = anExtractor.ErrorStatus(); + + // The detailed description of error codes is in GEOMAlgo_Extractor.cxx + if (iErr) { + TCollection_AsciiString aMsg(" iErr : "); + + aMsg += TCollection_AsciiString(iErr); + StdFail_NotDone::Raise(aMsg.ToCString()); + return 0; + } + + aShape = anExtractor.GetResult(); + + // Get statistics. + const TopTools_ListOfShape &aRemoved = anExtractor.GetRemoved(); + const TopTools_ListOfShape &aModified = anExtractor.GetModified(); + const TopTools_ListOfShape &aNew = anExtractor.GetNew(); + Handle(TColStd_HArray1OfInteger) aRemovedIDs = + GetShapeIDs(aRemoved, anIndices); + Handle(TColStd_HArray1OfInteger) aModifiedIDs = + GetShapeIDs(aModified, anIndices); + Handle(TColStd_HArray1OfInteger) aNewIDs; + + if (!aShape.IsNull()) { + // Get newly created sub-shapes + TopTools_IndexedMapOfShape aNewIndices; + + TopExp::MapShapes(aShape, aNewIndices); + aNewIDs = GetShapeIDs(aNew, aNewIndices); + } + + if (!aRemovedIDs.IsNull()) { + aCI.SetRemovedIDs(aRemovedIDs); + } + + if (!aModifiedIDs.IsNull()) { + aCI.SetModifiedIDs(aModifiedIDs); + } + + if (!aNewIDs.IsNull()) { + aCI.SetAddedIDs(aNewIDs); + } } else { } @@ -1880,6 +2006,15 @@ GetCreationInformation(std::string& theOperationName, AddParam(theParams, "Face", aSE.GetShape()); break; } + case EXTRACTION: + { + GEOMImpl_IExtract aCI (function); + + theOperationName = "EXTRACTION"; + AddParam(theParams, "Main Shape", aCI.GetShape()); + AddParam(theParams, "Sub-shape IDs", aCI.GetSubShapeIDs()); + break; + } default: return false; } diff --git a/src/GEOMImpl/GEOMImpl_Types.hxx b/src/GEOMImpl/GEOMImpl_Types.hxx index 393b4303c..2e58443ac 100644 --- a/src/GEOMImpl/GEOMImpl_Types.hxx +++ b/src/GEOMImpl/GEOMImpl_Types.hxx @@ -119,6 +119,8 @@ #define GEOM_TRANSFER_DATA 57 +#define GEOM_EXTRACTION 58 + //GEOM_Function types #define COPY_WITH_REF 1 @@ -316,6 +318,7 @@ #define FACE_UV 17 #define SURFACE_FROM_FACE 18 #define SOLID_FACES 19 +#define EXTRACTION 20 #define ARCHIMEDE_TYPE 1 diff --git a/src/GEOM_I/GEOM_IShapesOperations_i.cc b/src/GEOM_I/GEOM_IShapesOperations_i.cc index 7561d9a16..c6c30b228 100644 --- a/src/GEOM_I/GEOM_IShapesOperations_i.cc +++ b/src/GEOM_I/GEOM_IShapesOperations_i.cc @@ -2230,3 +2230,98 @@ GEOM::ListOfGO* GEOM_IShapesOperations_i::GetSubShapesWithTolerance return aSeq._retn(); } + +//============================================================================= +/*! + * MakeExtraction + */ +//============================================================================= +GEOM::GEOM_Object_ptr GEOM_IShapesOperations_i::MakeExtraction + (GEOM::GEOM_Object_ptr theShape, + const GEOM::ListOfLong &theSubShapeIDs, + GEOM::GEOM_IShapesOperations::ExtractionStats_out theStats) +{ + GEOM::GEOM_Object_var aGEOMObject; + + //Set a not done flag + theStats = new GEOM::GEOM_IShapesOperations::ExtractionStats; + GetOperations()->SetNotDone(); + + //Get the reference object + Handle(GEOM_Object) aShape = GetObjectImpl(theShape); + + if (aShape.IsNull()) { + return aGEOMObject._retn(); + } + + const int aNbIDs = theSubShapeIDs.length(); + + if (aNbIDs == 0) { + return aGEOMObject._retn(); + } + + int i; + Handle(TColStd_HArray1OfInteger) anArray = + new TColStd_HArray1OfInteger (1, aNbIDs); + + for (i = 0; i < aNbIDs; i++) { + anArray->SetValue(i + 1, theSubShapeIDs[i]); + } + + //Get Shapes in place of aShapeWhat + std::list aStats; + Handle(GEOM_Object) aResult = + GetOperations()->MakeExtraction(aShape, anArray, aStats); + + if (!GetOperations()->IsDone() || aResult.IsNull()) { + return aGEOMObject._retn(); + } + + // Convert statistics. + const int aNbStats = aStats.size(); + + theStats->length(aNbStats); + + // fill the local CORBA array with values from lists + std::list::const_iterator + anIt = aStats.begin(); + + for (i = 0; anIt != aStats.end(); i++, anIt++) { + GEOM::GEOM_IShapesOperations::ExtractionStat_var aResStat = + new GEOM::GEOM_IShapesOperations::ExtractionStat; + + // Copy type + switch (anIt->type) { + case GEOMImpl_IShapesOperations::EST_Removed: + aResStat->type = GEOM::GEOM_IShapesOperations::EST_Removed; + break; + case GEOMImpl_IShapesOperations::EST_Modified: + aResStat->type = GEOM::GEOM_IShapesOperations::EST_Modified; + break; + case GEOMImpl_IShapesOperations::EST_Added: + aResStat->type = GEOM::GEOM_IShapesOperations::EST_Added; + break; + default: + break; + } + + // Copy the list of IDs + std::list aIDList = anIt->indices; + GEOM::ListOfLong_var aResIDList = new GEOM::ListOfLong; + + aResIDList->length(aIDList.size()); + + std::list::iterator anIDIt = aIDList.begin(); + int j = 0; + + for (; anIDIt != aIDList.end(); j++, anIDIt++) { + aResIDList[j] = *anIDIt; + } + + aResStat->indices = aResIDList; + + theStats[i] = aResStat; + } + + return GetObject(aResult); +} diff --git a/src/GEOM_I/GEOM_IShapesOperations_i.hh b/src/GEOM_I/GEOM_IShapesOperations_i.hh index 0f36a38c3..ff9e417f0 100644 --- a/src/GEOM_I/GEOM_IShapesOperations_i.hh +++ b/src/GEOM_I/GEOM_IShapesOperations_i.hh @@ -306,6 +306,11 @@ class GEOM_I_EXPORT GEOM_IShapesOperations_i : GEOM::comparison_condition theCondition, CORBA::Double theTolerance); + GEOM::GEOM_Object_ptr MakeExtraction + (GEOM::GEOM_Object_ptr theShape, + const GEOM::ListOfLong &theSubShapeIDs, + GEOM::GEOM_IShapesOperations::ExtractionStats_out theStats); + ::GEOMImpl_IShapesOperations* GetOperations() { return (::GEOMImpl_IShapesOperations*)GetImpl(); } }; diff --git a/src/GEOM_SWIG/GEOM_TestAll.py b/src/GEOM_SWIG/GEOM_TestAll.py index ea862293f..a500a67d4 100644 --- a/src/GEOM_SWIG/GEOM_TestAll.py +++ b/src/GEOM_SWIG/GEOM_TestAll.py @@ -590,5 +590,10 @@ def TestAll (geompy, math): geompy.GetSubShapesWithTolerance(Box, GEOM.FACE, GEOM.CC_LT, 2.e-7, "lt") geompy.GetSubShapesWithTolerance(Box, GEOM.FACE, GEOM.CC_LE, 1.e-7, "le") + # MakeExtraction + geompy.MakeExtraction(Box, [13], "Ext_no_face") + geompy.MakeExtraction(Box, [18], "Ext_no_edge") + geompy.MakeExtraction(Box, [16], "Ext_no_vertex") + print "DONE" diff --git a/src/GEOM_SWIG/geomBuilder.py b/src/GEOM_SWIG/geomBuilder.py index 379105b72..5b74cabdd 100644 --- a/src/GEOM_SWIG/geomBuilder.py +++ b/src/GEOM_SWIG/geomBuilder.py @@ -6661,6 +6661,35 @@ class geomBuilder(object, GEOM._objref_GEOM_Gen): RaiseIfFailed("IsSubShapeBelongsTo", self.ShapesOp) return IsOk + ## Perform extraction of sub-shapes from the main shape. + # + # @param theShape the main shape + # @param theListOfID the list of sub-shape IDs to be extracted from + # the main shape. + # @return New GEOM.GEOM_Object, containing the shape without + # extracted sub-shapes. + # + # @ref swig_MakeExtraction "Example" + @ManageTransactions("ShapesOp") + def MakeExtraction(self, theShape, theListOfID, theName=None): + """ + Perform extraction of sub-shapes from the main shape. + + Parameters: + theShape the main shape + theListOfID the list of sub-shape IDs to be extracted from + the main shape. + + Returns + New GEOM.GEOM_Object, containing the shape without + extracted sub-shapes. + """ + # Example: see GEOM_TestAll.py + (anObj, aStat) = self.ShapesOp.MakeExtraction(theShape, theListOfID) + RaiseIfFailed("MakeExtraction", self.ShapesOp) + self._autoPublish(anObj, theName, "Extraction") + return anObj + # end of l4_decompose ## @} diff --git a/src/OperationGUI/CMakeLists.txt b/src/OperationGUI/CMakeLists.txt index 2ff65aed2..7f949c31c 100755 --- a/src/OperationGUI/CMakeLists.txt +++ b/src/OperationGUI/CMakeLists.txt @@ -73,6 +73,7 @@ SET(OperationGUI_HEADERS OperationGUI_ChamferDlg.h OperationGUI_GetShapesOnShapeDlg.h OperationGUI_GetSharedShapesDlg.h + OperationGUI_ExtractionDlg.h OperationGUI_ExtrudedFeatureDlg.h OperationGUI_ClippingDlg.h OperationGUI_TransferDataDlg.h @@ -87,6 +88,7 @@ SET(_moc_HEADERS OperationGUI_ChamferDlg.h OperationGUI_GetShapesOnShapeDlg.h OperationGUI_GetSharedShapesDlg.h + OperationGUI_ExtractionDlg.h OperationGUI_ExtrudedFeatureDlg.h OperationGUI_ClippingDlg.h OperationGUI_TransferDataDlg.h @@ -109,6 +111,7 @@ SET(OperationGUI_SOURCES OperationGUI_FilletDlg.cxx OperationGUI_Fillet1d2dDlg.cxx OperationGUI_ChamferDlg.cxx + OperationGUI_ExtractionDlg.cxx OperationGUI_ExtrudedFeatureDlg.cxx OperationGUI_ClippingDlg.cxx OperationGUI_TransferDataDlg.cxx diff --git a/src/OperationGUI/OperationGUI.cxx b/src/OperationGUI/OperationGUI.cxx index 3288921ab..96e5fc57a 100644 --- a/src/OperationGUI/OperationGUI.cxx +++ b/src/OperationGUI/OperationGUI.cxx @@ -40,6 +40,7 @@ #include "OperationGUI_GetSharedShapesDlg.h" #include "OperationGUI_ExtrudedFeatureDlg.h" // Methods EXTRUDED BOSS / CUT #include "OperationGUI_TransferDataDlg.h" +#include "OperationGUI_ExtractionDlg.h" //======================================================================= // function : OperationGUI() @@ -83,6 +84,7 @@ bool OperationGUI::OnGUIEvent (int theCommandID, SUIT_Desktop* parent) case GEOMOp::OpFillet1d: (new OperationGUI_Fillet1d2dDlg (getGeometryGUI(), parent, true))->show(); break; case GEOMOp::OpFillet2d: (new OperationGUI_Fillet1d2dDlg (getGeometryGUI(), parent, false))->show(); break; case GEOMOp::OpTransferData: (new OperationGUI_TransferDataDlg (getGeometryGUI(), parent))->show(); break; + case GEOMOp::OpExtraction: (new OperationGUI_ExtractionDlg (getGeometryGUI(), parent))->show(); break; default: app->putInfo(tr("GEOM_PRP_COMMAND").arg(theCommandID)); } diff --git a/src/OperationGUI/OperationGUI_ExtractionDlg.cxx b/src/OperationGUI/OperationGUI_ExtractionDlg.cxx new file mode 100644 index 000000000..47f2e722b --- /dev/null +++ b/src/OperationGUI/OperationGUI_ExtractionDlg.cxx @@ -0,0 +1,1324 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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 "OperationGUI_ExtractionDlg.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= 0x050300 + #include +#else + /** + * This class is named as QT class as it is introduced since Qt 5.3. + * It should not be compiled when Salome is ported on Qt 5.3. + */ + class QSignalBlocker + { + public: + QSignalBlocker(QObject *object) + : myObject (object), + myIsBlocked (object && object->signalsBlocked()) { + if (myObject) { + myObject->blockSignals(true); + } + } + + ~QSignalBlocker() { + if (myObject) { + myObject->blockSignals(myIsBlocked); + } + } + + private: + QObject *myObject; ///< Blocked object. + bool myIsBlocked; ///< Initial blocked state. + }; +#endif + + +#define ID_ROLE Qt::DisplayRole +#define TYPE_ROLE Qt::UserRole + +static const char* const TMP_STR = "TEMP"; + +static const char* const SINGLE_SHAPE_TYPE_TR_CODES [] = { + "GEOM_COMPOUND", + "GEOM_COMPOUNDSOLID", + "GEOM_SOLID", + "GEOM_SHELL", + "GEOM_FACE", + "GEOM_WIRE", + "GEOM_EDGE", + "GEOM_VERTEX" +}; + +static const char* const PLURAL_SHAPE_TYPE_TR_CODES [] = { + "GEOM_COMPOUND", // Not used + "GEOM_COMPSOLIDS", + "GEOM_SOLIDS", + "GEOM_SHELLS", + "GEOM_FACES", + "GEOM_WIREZ", + "GEOM_EDGES", + "GEOM_VERTEXES" +}; + + +/** + * This static function creates a new list widget item with given ID and + * returns it. + * + * \param theID the item ID. + * \param theListWidget the list widget. + * \return the created list widget item. + */ +static QListWidgetItem *addNewItem(const int theID, + QListWidget *theListWidget) +{ + QListWidgetItem *aResult = new QListWidgetItem; + + aResult->setData(ID_ROLE, theID); + theListWidget->addItem(aResult); + + return aResult; +} + +/** + * This static function creates a new tree widget item as a child of the input + * one with given ID and returns it. + * + * \param theID the item ID. + * \param theParentItem the parent item. + * \return the created tree widget item. + */ +static QTreeWidgetItem *addChildItem(const int theID, + QTreeWidgetItem *theParentItem) +{ + QTreeWidgetItem *aResult = new QTreeWidgetItem; + + aResult->setData(0, ID_ROLE, theID); + theParentItem->addChild(aResult); + + return aResult; +} + +/** + * This static function returns the maximal shape type of sub-shapes stored in + * the input compound. If it is not a compound, it returns TopAbs_SHAPE. + * + * \param theCompound the compound. + * \return the maximal shape type of sub-shapes stored in the input compound. + */ +static TopAbs_ShapeEnum GetMaxShapeTypeInComp(const TopoDS_Shape &theCompound) +{ + TopAbs_ShapeEnum aResult = TopAbs_SHAPE; + + if (theCompound.IsNull() || theCompound.ShapeType() != TopAbs_COMPOUND) { + return aResult; + } + + TopoDS_Iterator anIt(theCompound, Standard_False, Standard_False); + + for (; anIt.More(); anIt.Next()) { + const TopoDS_Shape &aSubShape = anIt.Value(); + + if (aSubShape.IsNull()) { + continue; + } + + // Get the sub-shape type. + TopAbs_ShapeEnum aSubType = aSubShape.ShapeType(); + + if (aSubType == TopAbs_COMPOUND) { + aSubType = GetMaxShapeTypeInComp(aSubShape); + } + + if (aSubType == TopAbs_SHAPE) { + continue; + } + + if (aResult == TopAbs_SHAPE) { + // This is an initialization. + aResult = aSubType; + } else if (aResult > aSubType) { + aResult = aSubType; + } + } + + return aResult; +} + +//================================================================================= +// class : OperationGUI_ExtractionDlg() +// purpose : +//================================================================================= +OperationGUI_ExtractionDlg::OperationGUI_ExtractionDlg + (GeometryGUI* GUI, QWidget* parent) + : GEOMBase_Skeleton (GUI, parent, false), + mySelBtn (0), + myMainShapeEdit (0), + mySubShTypeCompo (0), + myFilteredList (0), + myExtractedTree (0), + myRemovedList (0), + myModifiedList (0), + myAddedList (0), + myRebuildBtn (0), + myIsHiddenMain (false) +{ + QPixmap image0(SUIT_Session::session()->resourceMgr()->loadPixmap( + "GEOM", tr("ICON_DLG_EXTRACTION"))); + QPixmap image1(SUIT_Session::session()->resourceMgr()->loadPixmap( + "GEOM", tr("ICON_SELECT"))); + + setWindowTitle(tr("GEOM_EXTRACT_TITLE")); + + /***************************************************************/ + + mainFrame()->GroupConstructors->setTitle(tr("GEOM_EXTRACT_TYPE")); + mainFrame()->RadioButton1->setIcon( image0 ); + mainFrame()->RadioButton2->setAttribute(Qt::WA_DeleteOnClose); + mainFrame()->RadioButton2->close(); + mainFrame()->RadioButton3->setAttribute(Qt::WA_DeleteOnClose); + mainFrame()->RadioButton3->close(); + + // Create an input group. + QGroupBox *anInputGrp = new QGroupBox(tr("GEOM_EXTRACT_INPUT_PARAMS"), centralWidget()); + QGridLayout *anInputLayout = new QGridLayout(anInputGrp); + QHBoxLayout *aShapeLayout = new QHBoxLayout(anInputGrp); + QVBoxLayout *aViewBtnsLayout = new QVBoxLayout(anInputGrp); + QVBoxLayout *aMoveBtnsLayout = new QVBoxLayout(anInputGrp); + QLabel *aMainObjLbl = new QLabel(tr("GEOM_MAIN_OBJECT"), anInputGrp); + QLabel *aSubShTypeLbl = new QLabel(tr("GEOM_EXTRACT_SUB_SHAPE_TYPE"), anInputGrp); + QLabel *aFilteredLbl = new QLabel(tr("GEOM_EXTRACT_FILTERED_SHAPES"), anInputGrp); + QLabel *anExtractedLbl = new QLabel(tr("GEOM_EXTRACT_SHAPES_TO_EXTRACT"), anInputGrp); + QPushButton *aShowOnlySelBtn = new QPushButton(tr("SHOW_ONLY_SELECTED"), anInputGrp); + QPushButton *aHideSelBtn = new QPushButton(tr("HIDE_SELECTED"), anInputGrp); + QPushButton *aShowAllBtn = new QPushButton(tr("SHOW_ALL_SUB_SHAPES"), anInputGrp); + QPushButton *anAddBtn = new QPushButton(">>", anInputGrp); + QPushButton *aRemoveBtn = new QPushButton("<<", anInputGrp); + + myRebuildBtn = new QPushButton(tr("GEOM_EXTRACT_REBUILD"), anInputGrp); + mySelBtn = new QPushButton(anInputGrp); + myMainShapeEdit = new QLineEdit(anInputGrp); + mySubShTypeCompo = new QComboBox(anInputGrp); + myFilteredList = new QListWidget(anInputGrp); + myExtractedTree = new QTreeWidget(anInputGrp); + mySelBtn->setIcon(image1); + myMainShapeEdit->setReadOnly(true); + + aShapeLayout->addWidget(mySelBtn); + aShapeLayout->addWidget(myMainShapeEdit); + + aViewBtnsLayout->addStretch(); + aViewBtnsLayout->addWidget(aShowOnlySelBtn); + aViewBtnsLayout->addWidget(aHideSelBtn); + aViewBtnsLayout->addWidget(aShowAllBtn); + aViewBtnsLayout->addStretch(); + + aMoveBtnsLayout->addStretch(); + aMoveBtnsLayout->addWidget(anAddBtn); + aMoveBtnsLayout->addWidget(aRemoveBtn); + aMoveBtnsLayout->addStretch(); + + anInputLayout->setSpacing(6); + anInputLayout->setContentsMargins(9, 9, 9, 9); + anInputLayout->addWidget(aMainObjLbl, 0, 0); + anInputLayout->addLayout(aShapeLayout, 0, 1, 1, 3); + anInputLayout->addWidget(aSubShTypeLbl, 1, 0); + anInputLayout->addWidget(mySubShTypeCompo, 1, 1, 1, 3); + anInputLayout->addWidget(aFilteredLbl, 2, 1); + anInputLayout->addWidget(anExtractedLbl, 2, 3); + anInputLayout->addLayout(aViewBtnsLayout, 3, 0); + anInputLayout->addWidget(myFilteredList, 3, 1); + anInputLayout->addLayout(aMoveBtnsLayout, 3, 2); + anInputLayout->addWidget(myExtractedTree, 3, 3); + anInputLayout->addWidget(myRebuildBtn, 4, 0, 1, 4); + + // Create a statistics group. + QGroupBox *aStatGrp = new QGroupBox(tr("GEOM_EXTRACT_STATISTICS"), centralWidget()); + QGridLayout *aStatLayout = new QGridLayout(aStatGrp); + QLabel *aRemovedLbl = new QLabel(tr("GEOM_EXTRACT_REMOVED"), aStatGrp); + QLabel *aModifiedLbl = new QLabel(tr("GEOM_EXTRACT_MODIFIED"), aStatGrp); + QLabel *anAddedLbl = new QLabel(tr("GEOM_EXTRACT_ADDED"), aStatGrp); + + myRemovedList = new QListWidget(aStatGrp); + myModifiedList = new QListWidget(aStatGrp); + myAddedList = new QListWidget(aStatGrp); + + aStatLayout->setSpacing(6); + aStatLayout->setContentsMargins(9, 9, 9, 9); + aStatLayout->addWidget(aRemovedLbl, 0, 0); + aStatLayout->addWidget(aModifiedLbl, 0, 1); + aStatLayout->addWidget(anAddedLbl, 0, 2); + aStatLayout->addWidget(myRemovedList, 1, 0); + aStatLayout->addWidget(myModifiedList, 1, 1); + aStatLayout->addWidget(myAddedList, 1, 2); + + // Create a main layout. + QVBoxLayout* aLayout = new QVBoxLayout(centralWidget()); + + aLayout->setMargin(0); + aLayout->setSpacing(6); + aLayout->addWidget(anInputGrp); + aLayout->addWidget(aStatGrp); + + // signals and slots connections + connect(anAddBtn, SIGNAL(clicked()), this, SLOT(onAddExtracted())); + connect(aRemoveBtn, SIGNAL(clicked()), this, SLOT(onRemoveExtracted())); + connect(aShowOnlySelBtn, SIGNAL(clicked()), this, SLOT(showOnlySelected())); + connect(aHideSelBtn, SIGNAL(clicked()), this, SLOT(hideSelected())); + connect(aShowAllBtn, SIGNAL(clicked()), this, SLOT(showAllSelected())); + + /***************************************************************/ + myHelpFileName = "extract_and_rebuild_page.html"; + + resize(525, 600); + + /* Initialisation */ + Init(); +} + +//================================================================================= +// function : ~OperationGUI_ExtractionDlg() +// purpose : Destroys the object and frees any allocated resources +//================================================================================= +OperationGUI_ExtractionDlg::~OperationGUI_ExtractionDlg() +{ + restoreViewer(); +} + +//================================================================================= +// function : Init() +// purpose : +//================================================================================= +void OperationGUI_ExtractionDlg::Init() +{ + mySelBtn->setCheckable(true); + mySelBtn->setChecked(true); + myFilteredList->setSelectionMode(QAbstractItemView::ExtendedSelection); + myFilteredList->setSortingEnabled(true); + myExtractedTree->setHeaderHidden(true); + myExtractedTree->setSelectionMode(QAbstractItemView::ExtendedSelection); + myExtractedTree->setColumnCount(1); + myRebuildBtn->setEnabled(false); + myRemovedList->setSelectionMode(QAbstractItemView::NoSelection); + myModifiedList->setSelectionMode(QAbstractItemView::NoSelection); + myAddedList->setSelectionMode(QAbstractItemView::NoSelection); + + // Fill in the extracted tree with initial elements. + myTopItems[0] = 0; // No need to create a item for compound. + + int i; + + for (i = 1; i < 8; i++) { + myTopItems[i] = new QTreeWidgetItem; + myTopItems[i]->setText(0, tr(PLURAL_SHAPE_TYPE_TR_CODES[i])); + myTopItems[i]->setData(0, TYPE_ROLE, i); + + myExtractedTree->addTopLevelItem(myTopItems[i]); + myTopItems[i]->setHidden(true); + } + + // signals and slots connections + connect(mySelBtn, SIGNAL(clicked()), this, SLOT(SetEditCurrentArgument())); + connect(buttonOk(), SIGNAL(clicked()), this, SLOT(ClickOnOk())); + connect(buttonApply(), SIGNAL(clicked()), this, SLOT(ClickOnApply())); + connect(mySubShTypeCompo, SIGNAL(currentIndexChanged(int)), + this, SLOT(onShapeTypeChanged())); + connect(myRebuildBtn, SIGNAL(clicked()), this, SLOT(onRebuild())); + connect(myGeomGUI->getApp()->selectionMgr(), SIGNAL(currentSelectionChanged()), + this, SLOT(SelectionIntoArgument())); + connect(myFilteredList, SIGNAL(itemSelectionChanged()), + this, SLOT(onListSelectionChanged())); + connect(myExtractedTree, SIGNAL(itemSelectionChanged()), + this, SLOT(onListSelectionChanged())); + + initName(tr("GEOM_EXTRACT_NAME")); + + activateSelection(); + SelectionIntoArgument(); +} + +//================================================================================= +// function : updateSubShTypeCompo() +// purpose : +//================================================================================= +bool OperationGUI_ExtractionDlg::updateSubShTypeCompo() +{ + bool isValid = true; + int anIStart = TopAbs_COMPOUND; + const int anIEnd = TopAbs_VERTEX; + TopoDS_Shape aShape; + + if (GEOMBase::GetShape(myObj, aShape)) { + const TopAbs_ShapeEnum aType = aShape.ShapeType(); + + if (aType == TopAbs_COMPOUND) { + anIStart = GetMaxShapeTypeInComp(aShape); + isValid = anIStart != TopAbs_SHAPE; + } else { + anIStart = aType + 1; + } + } + + QSignalBlocker aBlocker(mySubShTypeCompo); + mySubShTypeCompo->clear(); + + if (isValid) { + int i; + + for (i = anIStart; i <= anIEnd; i++) { + mySubShTypeCompo->addItem(tr(SINGLE_SHAPE_TYPE_TR_CODES[i]), i); + } + + updateFilteredList(); + } + + return isValid; +} + +//================================================================================= +// function : updateFilteredList() +// purpose : +//================================================================================= +void OperationGUI_ExtractionDlg::updateFilteredList() +{ + TopoDS_Shape aShape; + QSignalBlocker aBlocker(myFilteredList); + + myFilteredList->clear(); + + if (GEOMBase::GetShape(myObj, aShape)) { + const TopAbs_ShapeEnum aType = (TopAbs_ShapeEnum) + mySubShTypeCompo->itemData(mySubShTypeCompo->currentIndex()).toInt(); + TopExp_Explorer anExp(aShape, aType); + + if (anExp.More()) { + TopTools_MapOfShape aMapFence; + + for (; anExp.More(); anExp.Next()) { + const TopoDS_Shape &aSubShape = anExp.Current(); + + if (!aSubShape.IsNull() && aMapFence.Add(aSubShape)) { + int anIndex = myIndices.FindIndex(aSubShape); + + if (!myMapExtractedIDs.Contains(anIndex)) { + addNewItem(anIndex, myFilteredList); + } + } + } + } + } +} + +//================================================================================= +// function : resetBuildData() +// purpose : +//================================================================================= +void OperationGUI_ExtractionDlg::resetBuildData(const bool isEnableBuild) +{ + // Clear result data. + myRemovedList->clear(); + myModifiedList->clear(); + myAddedList->clear(); + myRebuildBtn->setEnabled(isEnableBuild); +} + +//================================================================================= +// function : isEmptyExtracted() +// purpose : +//================================================================================= +bool OperationGUI_ExtractionDlg::isEmptyExtracted() +{ + bool isEmpty = true; + int i; + + // Check if there are sub-shapes to be extracted. + for (i = 1; i < 8; i++) { + if (!myTopItems[i]->isHidden()) { + isEmpty = false; + + break; + } + } + + return isEmpty; +} + +//================================================================================= +// function : selectMainShape +// purpose : +//================================================================================= +void OperationGUI_ExtractionDlg::selectMainShape() +{ + LightApp_SelectionMgr *aSelMgr = myGeomGUI->getApp()->selectionMgr(); + SALOME_ListIO aSelList; + + aSelMgr->selectedObjects(aSelList); + + if (aSelList.Extent() == 1) { + GEOM::GEOM_Object_var aSelObject = + GEOMBase::ConvertIOinGEOMObject(aSelList.First()); + TopoDS_Shape aSelShape; + + if (GEOMBase::GetShape(aSelObject, aSelShape)) { + const TopAbs_ShapeEnum aType = aSelShape.ShapeType(); + + // Skip verices. + if (aType != TopAbs_VERTEX) { + myObj = aSelObject; + + // Initialize map of indices. Note that myIndices should be empty. + TopExp::MapShapes(aSelShape, myIndices); + } + } + } + + if (!updateSubShTypeCompo()) { + // Invalid selected object. + myObj = GEOM::GEOM_Object::_nil(); + } + + if (!CORBA::is_nil(myObj)) { + mySelBtn->setChecked(false); + myMainShapeEdit->setEnabled(false); + myMainShapeEdit->setText(GEOMBase::GetName(myObj)); + + // Hide the main object from the viewer. + SALOME_View* aView = GEOM_Displayer::GetActiveView(); + + if (aView) { + CORBA::String_var aMainEntry = myObj->GetStudyEntry(); + Handle(SALOME_InteractiveObject) anIO = createIO(aMainEntry.in()); + + if (aView->isVisible(anIO)) { + GEOM_Displayer *aDisplayer = getDisplayer(); + + aDisplayer->Erase(myObj, false, true); + myIsHiddenMain = true; + } + } + } +} + +//================================================================================= +// function : selectSubShapes +// purpose : +//================================================================================= +void OperationGUI_ExtractionDlg::selectSubShapes() +{ + QSignalBlocker aBlocker(myFilteredList); + + // Clear current selection. + myFilteredList->clearSelection(); + + LightApp_SelectionMgr *aSelMgr = myGeomGUI->getApp()->selectionMgr(); + SALOME_ListIO aSelList; + const int aCurType = + mySubShTypeCompo->itemData(mySubShTypeCompo->currentIndex()).toInt(); + + aSelMgr->selectedObjects(aSelList); + + // try to find out and process the global selection + // (of not published objects and of published sub-shapes) + SALOME_ListIteratorOfListIO anIter(aSelList); + + for (; anIter.More(); anIter.Next()) { + Handle(SALOME_InteractiveObject) anIObj = anIter.Value(); + QString anEntry = anIObj->getEntry(); + QStringList aParts = anEntry.split("_"); + int aSubShapeId = -1; + + if (!aParts.isEmpty()) { + if (aParts.first() == TMP_STR) { + bool isOk = false; + const int anIndex = aParts.last().toInt(&isOk); + + if (isOk && anIndex > 0) { + // This is a sub-shape. + aSubShapeId = anIndex; + } + } + } + + if (aSubShapeId < 0) { + // This is a published shape. + GEOM::GEOM_Object_var aSelObject = + GEOMBase::ConvertIOinGEOMObject(anIObj); + TopoDS_Shape aSelShape; + + if (GEOMBase::GetShape(aSelObject, aSelShape)) { + + if (aSelShape.ShapeType() == aCurType) { + const int anIndex = myIndices.FindIndex(aSelShape); + + if (anIndex > 0) { + // This is a sub-shape. Select it in the filtered list. + aSubShapeId = anIndex; + } + } + } + } + + // Select a list widget item by Id. + if (aSubShapeId > 0) { + QString anIdText = QString("%1").arg(aSubShapeId); + QList aFound = + myFilteredList->findItems(anIdText, Qt::MatchExactly); + + foreach (QListWidgetItem *anItem, aFound) { + anItem->setSelected(true); + } + } + } +} + +//================================================================================= +// function : ClickOnOk() +// purpose : +//================================================================================= +void OperationGUI_ExtractionDlg::ClickOnOk() +{ + if (ClickOnApply()) { + ClickOnCancel(); + } +} + +//================================================================================= +// function : ClickOnApply() +// purpose : +//================================================================================= +bool OperationGUI_ExtractionDlg::ClickOnApply() +{ + if (!onAccept()) { + return false; + } + + initName(); + + return true; +} + +//================================================================================= +// function : onShapeTypeChanged +// purpose : +//================================================================================= +void OperationGUI_ExtractionDlg::onShapeTypeChanged() +{ + updateFilteredList(); + eraseAll(); +} + +//================================================================================= +// function : onAddExtracted +// purpose : +//================================================================================= +void OperationGUI_ExtractionDlg::onAddExtracted() +{ + QList aListSelected = myFilteredList->selectedItems(); + + if (aListSelected.empty()) { + return; + } + + const int aShapeType = + mySubShTypeCompo->itemData(mySubShTypeCompo->currentIndex()).toInt(); + bool isTreeUpdated = false; + + foreach (QListWidgetItem *anItem, aListSelected) { + const int anIndex = anItem->data(ID_ROLE).toInt(); + + if (myMapExtractedIDs.Add(anIndex)) { + addChildItem(anIndex, myTopItems[aShapeType]); + isTreeUpdated = true; + } + + // Remove anItem from the list. + myFilteredList->removeItemWidget(anItem); + delete anItem; + } + + if (isTreeUpdated) { + myTopItems[aShapeType]->sortChildren(0, Qt::AscendingOrder); + + // Reset build data + resetBuildData(true); + } + + myFilteredList->clearSelection(); + myTopItems[aShapeType]->setHidden(false); +} + +//================================================================================= +// function : onRemoveExtracted +// purpose : +//================================================================================= +void OperationGUI_ExtractionDlg::onRemoveExtracted() +{ + QList aListSelected = myExtractedTree->selectedItems(); + + if (aListSelected.empty()) { + return; + } + + const int aShapeType = + mySubShTypeCompo->itemData(mySubShTypeCompo->currentIndex()).toInt(); + QSet aSetFence; + bool isTreeUpdated = false; + + foreach (QTreeWidgetItem *anItem, aListSelected) { + if (!aSetFence.contains(anItem)) { + aSetFence.insert(anItem); + + QTreeWidgetItem *aParent = anItem->parent(); + + if (aParent) { + const int anIndex = anItem->data(0, ID_ROLE).toInt(); + // This is a ID item. Remove the ID from myMapExtractedIDs. + if (myMapExtractedIDs.Remove(anIndex)) { + // The item is not removed yet. Get parent index. + const int aParentIndex = aParent->data(0, TYPE_ROLE).toInt(); + + if (aShapeType == aParentIndex) { + // Create an item in the filtered list. + addNewItem(anIndex, myFilteredList); + } + + aParent->removeChild(anItem); + delete anItem; + isTreeUpdated = true; + + // Hilde an empty parent item. + if (aParent->childCount() == 0) { + aParent->setHidden(true); + } + } + } else { + // This is a top level item. Remove all its children. + QList aChildItems = anItem->takeChildren(); + const int anIndex = anItem->data(0, TYPE_ROLE).toInt(); + + // Remove IDs from myMapExtractedIDs. + foreach (QTreeWidgetItem *aChild, aChildItems) { + if (!aSetFence.contains(aChild)) { + aSetFence.insert(aChild); + + const int aChildIndex = aChild->data(0, ID_ROLE).toInt(); + + if (myMapExtractedIDs.Remove(aChildIndex)) { + if (aShapeType == anIndex) { + // Create items in the filtered list. + addNewItem(aChildIndex, myFilteredList); + } + + delete aChild; + isTreeUpdated = true; + } + } + } + + // Hilde an empty item. + anItem->setHidden(true); + } + } + } + + myExtractedTree->clearSelection(); + + if (isTreeUpdated) { + // Reset build data + const bool isEnableRebuild = !isEmptyExtracted(); + + resetBuildData(isEnableRebuild); + } +} + +//================================================================================= +// function : onListSelectionChanged +// purpose : +//================================================================================= +void OperationGUI_ExtractionDlg::onListSelectionChanged() +{ + SALOME_ListIO anIOList; + QList aListSel = myFilteredList->selectedItems(); + QList aTreeSel = myExtractedTree->selectedItems(); + + // Collect selected items from myFilteredList + foreach (QListWidgetItem *anItem, aListSel) { + const int anIndex = anItem->data(ID_ROLE).toInt(); + + if (myMapDisplayedIDs.Contains(anIndex)) { + // Collect only displayed sub-shapes for selection in the viewer. + QString anEntry = getSubShapeEntry(anIndex); + Handle(SALOME_InteractiveObject) anIO = + createIO(anEntry.toLatin1().data()); + + anIOList.Append(anIO); + } + } + + // Collect selected items from myExtractedTree + foreach (QTreeWidgetItem *anItem, aTreeSel) { + if (anItem->parent()) { + // This is a ID item. + const int anIndex = anItem->data(0, ID_ROLE).toInt(); + + if (myMapDisplayedIDs.Contains(anIndex)) { + // Collect only displayed sub-shapes for selection in the viewer. + QString anEntry = getSubShapeEntry(anIndex); + Handle(SALOME_InteractiveObject) anIO = + createIO(anEntry.toLatin1().data()); + + anIOList.Append(anIO); + } + } + } + + // Select object in viewer. + LightApp_SelectionMgr *aSelMgr = myGeomGUI->getApp()->selectionMgr(); + + aSelMgr->setSelectedObjects(anIOList); +} + +//================================================================================= +// function : showOnlySelected +// purpose : +//================================================================================= +void OperationGUI_ExtractionDlg::showOnlySelected() +{ + TColStd_MapOfInteger aMapIDsSelected; + TColStd_MapOfInteger aMapIDsToDisplay; + const int aNbItems = myFilteredList->count(); + int i; + QSet aSelEntry; + + // Get sub-shape IDs to be displayed. + for (i = 0; i < aNbItems; ++i) { + QListWidgetItem *anItem = myFilteredList->item(i); + const int anIndex = anItem->data(ID_ROLE).toInt(); + + if (anItem->isSelected()) { + aMapIDsSelected.Add(anIndex); + aSelEntry.insert(getSubShapeEntry(anIndex)); + + if (!myMapDisplayedIDs.Contains(anIndex)) { + aMapIDsToDisplay.Add(anIndex); + } + } + } + + // Get sub-shape IDs to be erased. + TColStd_MapOfInteger aMapIDsToHide; + TColStd_MapIteratorOfMapOfInteger anIter(myMapDisplayedIDs); + + for (; anIter.More(); anIter.Next()) { + const int anIndex = anIter.Key(); + + if (!aMapIDsSelected.Contains(anIndex)) { + aMapIDsToHide.Add(anIndex); + } + } + + // Display sub-shapes. + for (anIter.Initialize(aMapIDsToDisplay); anIter.More(); anIter.Next()) { + displaySubShape(anIter.Key()); + } + + // Hide sub-shapes. + for (anIter.Initialize(aMapIDsToHide); anIter.More(); anIter.Next()) { + eraseSubShape(anIter.Key()); + } + + // Hide all objects except already displayed sub-shapes. + SALOME_ListIO aDisplayed; + SALOME_View *aView = GEOM_Displayer::GetActiveView(); + + if (aView) { + aView->GetVisible(aDisplayed); + } + + SALOME_ListIteratorOfListIO aDispIt(aDisplayed); + GEOM_Displayer *aDisplayer = getDisplayer(); + + for (; aDispIt.More(); aDispIt.Next()) { + Handle(SALOME_InteractiveObject) anIO = aDispIt.Value(); + + if (!aSelEntry.contains(anIO->getEntry())) { + aDisplayer->Erase(anIO, false, false); + } + } + + onListSelectionChanged(); + aDisplayer->UpdateViewer(); +} + +//================================================================================= +// function : hideSelected +// purpose : +//================================================================================= +void OperationGUI_ExtractionDlg::hideSelected() +{ + QList aListSelected = myFilteredList->selectedItems(); + + foreach (QListWidgetItem *anItem, aListSelected) { + const int anIndex = anItem->data(ID_ROLE).toInt(); + + eraseSubShape(anIndex); + } + + getDisplayer()->UpdateViewer(); +} + +//================================================================================= +// function : showAllSelected +// purpose : +//================================================================================= +void OperationGUI_ExtractionDlg::showAllSelected() +{ + const int aNbItems = myFilteredList->count(); + int i; + + for (i = 0; i < aNbItems; ++i) { + QListWidgetItem *anItem = myFilteredList->item(i); + const int anIndex = anItem->data(ID_ROLE).toInt(); + + displaySubShape(anIndex); + } + + onListSelectionChanged(); + getDisplayer()->UpdateViewer(); +} + +//================================================================================= +// function : onRebuild +// purpose : +//================================================================================= +void OperationGUI_ExtractionDlg::onRebuild() +{ + GEOM::GEOM_Object_var aResShape; + GEOM::GEOM_IShapesOperations::ExtractionStats aStats; + + if (!getResult(aResShape.out(), aStats)) { + resetBuildData(false); + return; + } + + TopoDS_Shape anOldShape; + TopoDS_Shape aNewShape; + TopTools_IndexedMapOfShape aNewIndices; + + if (!GEOMBase::GetShape(aResShape, aNewShape)) { + resetBuildData(false); + return; + } + + TopExp::MapShapes(aNewShape, aNewIndices); + + const int aNbStat = aStats.length(); + int i; + + for (i = 0; i < aNbStat; ++i) { + // Compute number of sub-shapes of each type. + const int aNbSubShapes = aStats[i].indices.length(); + int aNbShapes [] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int j; + + TopTools_IndexedMapOfShape *aMapShapes = + (aStats[i].type == GEOM::GEOM_IShapesOperations::EST_Added) ? + &aNewIndices : &myIndices; + + for (j = 0; j < aNbSubShapes; ++j) { + const int anIndex = aStats[i].indices[j]; + + if (anIndex < 1 || anIndex > aMapShapes->Extent()) { + resetBuildData(false); + return; + } + + const TopoDS_Shape &aSubShape = aMapShapes->FindKey(anIndex); + + aNbShapes[aSubShape.ShapeType()]++; + } + + // Fill the statistics. + QListWidget *aListWidget = 0; + + switch (aStats[i].type) { + case GEOM::GEOM_IShapesOperations::EST_Removed: + aListWidget = myRemovedList; + break; + case GEOM::GEOM_IShapesOperations::EST_Modified: + aListWidget = myModifiedList; + break; + case GEOM::GEOM_IShapesOperations::EST_Added: + aListWidget = myAddedList; + break; + default: + resetBuildData(false); + return; + } + + QStringList aStrList; + + for (j = 0; j < 8; ++j) { + if (aNbShapes[j] >= 1) { + const char *aShapeType = aNbShapes[j] == 1 ? + SINGLE_SHAPE_TYPE_TR_CODES[j] : PLURAL_SHAPE_TYPE_TR_CODES[j]; + + aStrList.append(QString("%1 %2").arg(aNbShapes[j]).arg(tr(aShapeType))); + } + } + + aListWidget->addItems(aStrList); + } + + myRebuildBtn->setEnabled(false); +} + +//================================================================================= +// function : SelectionIntoArgument +// purpose : +//================================================================================= +void OperationGUI_ExtractionDlg::SelectionIntoArgument() +{ + if (myMainShapeEdit->isEnabled()) { + // Selection of main object + selectMainShape(); + } else { + // Selection of filtered shapes + selectSubShapes(); + } +} + +//================================================================================= +// function : SetEditCurrentArgument +// purpose : +//================================================================================= +void OperationGUI_ExtractionDlg::SetEditCurrentArgument() +{ + QSignalBlocker aBlockerList(myFilteredList); + QSignalBlocker aBlockerTree(myExtractedTree); + + restoreViewer(); + myObj = GEOM::GEOM_Object::_nil(); + myMainShapeEdit->setEnabled(true); + myMainShapeEdit->setText(""); + myMainShapeEdit->setFocus(); + + updateSubShTypeCompo(); + + myFilteredList->clear(); + myRemovedList->clear(); + myModifiedList->clear(); + myAddedList->clear(); + myIndices.Clear(); + + // Clear myExtractedTree. + int i; + + for (i = 1; i < 8; i++) { + QList aListItems = myTopItems[i]->takeChildren(); + + foreach (QTreeWidgetItem *anItem, aListItems) { + delete anItem; + } + + myTopItems[i]->setHidden(true); + } + + myExtractedTree->clearSelection(); + + myMapExtractedIDs.Clear(); + + // Update viewer + eraseAll(); +} + +//================================================================================= +// function : ActivateThisDialog() +// purpose : +//================================================================================= +void OperationGUI_ExtractionDlg::ActivateThisDialog() +{ + GEOMBase_Skeleton::ActivateThisDialog(); + + LightApp_SelectionMgr* aSel = myGeomGUI->getApp()->selectionMgr(); + + if (aSel) { + connect(aSel, SIGNAL(currentSelectionChanged()), + this, SLOT(SelectionIntoArgument())); + } + + activateSelection(); +} + +//================================================================================= +// function : activateSelection +// purpose : activate selection of all shapes +//================================================================================= +void OperationGUI_ExtractionDlg::activateSelection() +{ + globalSelection(GEOM_ALLSHAPES); +} + +//================================================================================= +// function : enterEvent() +// purpose : +//================================================================================= +void OperationGUI_ExtractionDlg::enterEvent(QEvent *) +{ + if (!mainFrame()->GroupConstructors->isEnabled()) { + ActivateThisDialog(); + } +} + +//================================================================================= +// function : getResult +// purpose : +//================================================================================= +bool OperationGUI_ExtractionDlg::getResult + (GEOM::GEOM_Object_ptr &theResult, + GEOM::GEOM_IShapesOperations::ExtractionStats &theStats) +{ + if (myObj->_is_nil()) { + return false; + } + + // Get IDs of extracted shapes. + int i; + int aNbShapes = 0; + + for (i = 1; i < 8; i++) { + aNbShapes += myTopItems[i]->childCount(); + } + + if (aNbShapes == 0) { + return false; + } + + GEOM::ListOfLong_var aSubShapeIDs = new GEOM::ListOfLong; + int j; + int jCur; + + aSubShapeIDs->length(aNbShapes); + + for (jCur = 0, i = 1; i < 8; ++i) { + aNbShapes = myTopItems[i]->childCount(); + + for (j = 0; j < aNbShapes; ++j, ++jCur) { + aSubShapeIDs[jCur] = myTopItems[i]->child(j)->data(0, ID_ROLE).toInt(); + } + } + + GEOM::GEOM_IShapesOperations_var anOper = + GEOM::GEOM_IShapesOperations::_narrow(getOperation()); + + try { + GEOM::GEOM_Object_var anObj; + GEOM::GEOM_IShapesOperations::ExtractionStats_var aStats; + + anObj = anOper->MakeExtraction(myObj, aSubShapeIDs, aStats); + + if (anOper->IsDone() && aStats->length() > 0) { + theStats = aStats; + } + + if (!CORBA::is_nil(anObj)) { + theResult = anObj._retn(); + } + } + catch (const SALOME::SALOME_Exception& e) { + SalomeApp_Tools::QtCatchCorbaException(e); + return false; + } + + return anOper->IsDone(); +} + +//================================================================================= +// function : isValid +// purpose : +//================================================================================= +bool OperationGUI_ExtractionDlg::isValid(QString &) +{ + bool isOk = !myObj->_is_nil() && !isEmptyExtracted(); + + return isOk; +} + +//================================================================================= +// function : createOperation +// purpose : +//================================================================================= +GEOM::GEOM_IOperations_ptr OperationGUI_ExtractionDlg::createOperation() +{ + return getGeomEngine()->GetIShapesOperations(getStudyId()); +} + +//================================================================================= +// function : execute +// purpose : +//================================================================================= +bool OperationGUI_ExtractionDlg::execute(ObjectList &objects) +{ + GEOM::GEOM_Object_var aResShape; + GEOM::GEOM_IShapesOperations::ExtractionStats aStats; + + if (!getResult(aResShape.out(), aStats)) { + return false; + } + + if (!aResShape->_is_nil()) { + objects.push_back(aResShape._retn()); + } + + return true; +} + +//================================================================================= +// function : getSubShapeEntry +// purpose : +//================================================================================= +QString OperationGUI_ExtractionDlg::getSubShapeEntry(const int theId) +{ + CORBA::String_var aMainEntry = myObj->GetStudyEntry(); + QString anEntry = QString("%1_").arg(TMP_STR) + + aMainEntry.in() + QString("_%1").arg(theId); + + return anEntry; +} + +//================================================================================= +// function : createIO +// purpose : +//================================================================================= +Handle_SALOME_InteractiveObject OperationGUI_ExtractionDlg::createIO + (const char *theEntry) +{ + Handle(SALOME_InteractiveObject) anIO = new SALOME_InteractiveObject + (theEntry, "GEOM", "TEMP_IO"); + + return anIO; +} + +//================================================================================= +// function : displaySubShape +// purpose : +//================================================================================= +void OperationGUI_ExtractionDlg::displaySubShape(const int theId) +{ + if (theId < 1 || theId > myIndices.Extent()) { + return; + } + + // Create a presentation + const TopoDS_Shape &aSubShape = myIndices.FindKey(theId); + QString anEntry = getSubShapeEntry(theId); + SALOME_View *aView = GEOM_Displayer::GetActiveView(); + GEOM_Displayer *aDisplayer = getDisplayer(); + SALOME_Prs *aPrs = aDisplayer->buildSubshapePresentation + (aSubShape, anEntry, aView); + + if (aPrs) { + if (aView) { + aView->Display(aDisplayer, aPrs); + } + + delete aPrs; + + myMapDisplayedIDs.Add(theId); + } +} + +//================================================================================= +// function : eraseSubShape +// purpose : +//================================================================================= +void OperationGUI_ExtractionDlg::eraseSubShape(const int theId) +{ + QString anEntry = getSubShapeEntry(theId); + Handle(SALOME_InteractiveObject) anIO = + createIO(anEntry.toLatin1().data()); + + getDisplayer()->Erase(anIO, false, false); + myMapDisplayedIDs.Remove(theId); +} + +//================================================================================= +// function : eraseAll +// purpose : +//================================================================================= +void OperationGUI_ExtractionDlg::eraseAll() +{ + TColStd_MapIteratorOfMapOfInteger anIter(myMapDisplayedIDs); + + for (; anIter.More(); anIter.Next()) { + eraseSubShape(anIter.Key()); + } + + myMapDisplayedIDs.Clear(); + getDisplayer()->UpdateViewer(); +} + +//================================================================================= +// function : restoreViewer +// purpose : +//================================================================================= +void OperationGUI_ExtractionDlg::restoreViewer() +{ + if (!CORBA::is_nil(myObj)) { + if (myIsHiddenMain) { + getDisplayer()->Display(myObj, false); + myIsHiddenMain = false; + } + + eraseAll(); + } +} diff --git a/src/OperationGUI/OperationGUI_ExtractionDlg.h b/src/OperationGUI/OperationGUI_ExtractionDlg.h new file mode 100644 index 000000000..f10592aa5 --- /dev/null +++ b/src/OperationGUI/OperationGUI_ExtractionDlg.h @@ -0,0 +1,115 @@ +// Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// 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 +// + +#ifndef OPERATIONGUI_EXTRACTIONDLG_H +#define OPERATIONGUI_EXTRACTIONDLG_H + +#include + +#include +#include + +class QComboBox; +class QListWidget; +class QTreeWidget; +class QTreeWidgetItem; + + +//================================================================================= +// class : OperationGUI_ExtractionDlg +// purpose : +//================================================================================= +class OperationGUI_ExtractionDlg : public GEOMBase_Skeleton +{ + + Q_OBJECT + +public: + + OperationGUI_ExtractionDlg( GeometryGUI*, QWidget* ); + ~OperationGUI_ExtractionDlg(); + +protected: + + // redefined from GEOMBase_Helper and MeasureGUI_Skeleton + virtual GEOM::GEOM_IOperations_ptr createOperation(); + virtual bool execute(ObjectList &); + virtual void activateSelection(); + virtual bool isValid( QString& ); + +private slots: + + void SelectionIntoArgument(); + void ClickOnOk(); + bool ClickOnApply(); + void onShapeTypeChanged(); + void onAddExtracted(); + void onRemoveExtracted(); + void onListSelectionChanged(); + void showOnlySelected(); + void hideSelected(); + void showAllSelected(); + void onRebuild(); + void ActivateThisDialog(); + void SetEditCurrentArgument(); + +private: + + void Init(); + bool updateSubShTypeCompo(); + void updateFilteredList(); + void resetBuildData(const bool isEnableBuild); + bool isEmptyExtracted(); + void selectMainShape(); + void selectSubShapes(); + void enterEvent(QEvent *); + bool getResult + (GEOM::GEOM_Object_ptr &theResult, + GEOM::GEOM_IShapesOperations::ExtractionStats &theStats); + QString getSubShapeEntry(const int theId); + Handle_SALOME_InteractiveObject createIO(const char *theEntry); + void displaySubShape(const int theId); + void eraseSubShape(const int theId); + void eraseAll(); + void restoreViewer(); + +private: + + GEOM::GEOM_Object_var myObj; + QPushButton *mySelBtn; + QLineEdit *myMainShapeEdit; + QComboBox *mySubShTypeCompo; + QListWidget *myFilteredList; + QTreeWidget *myExtractedTree; + QListWidget *myRemovedList; + QListWidget *myModifiedList; + QListWidget *myAddedList; + QTreeWidgetItem *myTopItems[8]; + QPushButton *myRebuildBtn; + TColStd_MapOfInteger myMapExtractedIDs; + bool myIsHiddenMain; + TColStd_MapOfInteger myMapDisplayedIDs; + TopTools_IndexedMapOfShape myIndices; + +}; + +#endif // OPERATIONGUI_EXTRACTIONDLG_H -- 2.30.2