From e184734288441d2816e0a6f3f89e09f52ab717eb Mon Sep 17 00:00:00 2001 From: jfa Date: Fri, 27 Jul 2012 11:32:14 +0000 Subject: [PATCH] IMPs 0021476 (fuse edges) and 0021477 (suppress a vertex from a wire for fillet1d). --- doc/salome/gui/GEOM/images/fillet1d_2.png | Bin 11354 -> 25130 bytes .../gui/GEOM/images/fuse_collinear_edges.png | Bin 0 -> 19313 bytes .../gui/GEOM/input/fillet1d_operation.doc | 6 +- .../gui/GEOM/input/fuse_edges_operation.doc | 23 ++ .../gui/GEOM/input/repairing_operations.doc | 3 + .../GEOM/input/tui_repairing_operations.doc | 52 +++ idl/GEOM_Gen.idl | 19 +- resources/Makefile.am | 1 + resources/fuse_collinear_edges.png | Bin 0 -> 562 bytes src/GEOMGUI/GEOM_images.ts | 8 + src/GEOMGUI/GEOM_msg_en.ts | 28 ++ src/GEOMGUI/GeometryGUI.cxx | 3 + src/GEOMGUI/GeometryGUI_Operations.h | 1 + src/GEOMImpl/GEOMImpl_Fillet1d.cxx | 3 +- src/GEOMImpl/GEOMImpl_Fillet1d.hxx | 251 ++++++------- src/GEOMImpl/GEOMImpl_Fillet1dDriver.cxx | 195 +++++++--- src/GEOMImpl/GEOMImpl_Fillet1dDriver.hxx | 73 ++-- src/GEOMImpl/GEOMImpl_HealingDriver.cxx | 276 +++++++++++++- src/GEOMImpl/GEOMImpl_HealingDriver.hxx | 82 +++-- src/GEOMImpl/GEOMImpl_IFillet1d.hxx | 10 +- src/GEOMImpl/GEOMImpl_IHealing.hxx | 34 +- src/GEOMImpl/GEOMImpl_IHealingOperations.cxx | 83 ++++- src/GEOMImpl/GEOMImpl_IHealingOperations.hxx | 5 +- src/GEOMImpl/GEOMImpl_ILocalOperations.cxx | 5 +- src/GEOMImpl/GEOMImpl_ILocalOperations.hxx | 3 +- src/GEOMImpl/GEOMImpl_IShapesOperations.cxx | 16 +- src/GEOMImpl/GEOMImpl_ShapeDriver.cxx | 216 +++++------ src/GEOMImpl/GEOMImpl_ShapeDriver.hxx | 67 ++-- src/GEOMImpl/GEOMImpl_Types.hxx | 1 + src/GEOM_I/GEOM_IHealingOperations_i.cc | 38 +- src/GEOM_I/GEOM_IHealingOperations_i.hh | 5 +- src/GEOM_I/GEOM_ILocalOperations_i.cc | 6 +- src/GEOM_I/GEOM_ILocalOperations_i.hh | 4 +- src/GEOM_I_Superv/GEOM_Superv_i.cc | 7 +- src/GEOM_I_Superv/GEOM_Superv_i.hh | 2 +- src/GEOM_SWIG/GEOM_TestHealing.py | 52 ++- src/GEOM_SWIG/geompyDC.py | 41 ++- .../OperationGUI_Fillet1d2dDlg.cxx | 18 +- src/OperationGUI/OperationGUI_Fillet1d2dDlg.h | 7 +- src/RepairGUI/Makefile.am | 14 +- src/RepairGUI/RepairGUI.cxx | 8 +- src/RepairGUI/RepairGUI_FuseEdgesDlg.cxx | 347 ++++++++++++++++++ src/RepairGUI/RepairGUI_FuseEdgesDlg.h | 67 ++++ 43 files changed, 1616 insertions(+), 464 deletions(-) create mode 100644 doc/salome/gui/GEOM/images/fuse_collinear_edges.png create mode 100644 doc/salome/gui/GEOM/input/fuse_edges_operation.doc create mode 100644 resources/fuse_collinear_edges.png create mode 100644 src/RepairGUI/RepairGUI_FuseEdgesDlg.cxx create mode 100644 src/RepairGUI/RepairGUI_FuseEdgesDlg.h diff --git a/doc/salome/gui/GEOM/images/fillet1d_2.png b/doc/salome/gui/GEOM/images/fillet1d_2.png index e9da08fa397faba23f410edcecd43b6bb09a59eb..053053370e833f1f0253721bee624a84e1f5340a 100644 GIT binary patch literal 25130 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 literal 11354 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 z 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 literal 0 HcmV?d00001 diff --git a/doc/salome/gui/GEOM/input/fillet1d_operation.doc b/doc/salome/gui/GEOM/input/fillet1d_operation.doc index 861a6ae93..322b7c965 100644 --- a/doc/salome/gui/GEOM/input/fillet1d_operation.doc +++ b/doc/salome/gui/GEOM/input/fillet1d_operation.doc @@ -17,9 +17,13 @@ vertexes on this wire in the OCC Viewer and define the \b Radius of the Fillet. \b Note: This Operation Works for the Wires with Planar Edges only. +\b Note: Use option Fuse collinear edges to allow bigger radius + to allow ignoring of possible secant points, i.e. the + joints of collinear edges, thus processing such edges as one. + TUI Command: geompy.MakeFillet1D(Shape, R, ListVertexes) \n Arguments: Name + 1 shape + empty list or several vertexes + 1 value (Fillet radius). -\n Advanced options \ref preview_anchor "Preview" +\n Advanced options: \ref preview_anchor "Preview" Examples: diff --git a/doc/salome/gui/GEOM/input/fuse_edges_operation.doc b/doc/salome/gui/GEOM/input/fuse_edges_operation.doc new file mode 100644 index 000000000..7924f73eb --- /dev/null +++ b/doc/salome/gui/GEOM/input/fuse_edges_operation.doc @@ -0,0 +1,23 @@ +/*! + +\page fuse_edges_operation_page Fuse Collinear Edges within a Wire + +\n To Fuse Collinear Edges within a Wire in the Main Menu +select Repair - > Fuse Collinear Edges within a Wire. +\n This operation removes selected vertices from a given wire in case +if adjacent edges are C1 continuous. The function takes a list of +vertices to suppress as a parameter. If the list is empty, all +vertices in a wire are assumed. + +\n Arguments: Name + a wire + a list of vertices (can be empty). + +\image html fuse_collinear_edges.png + +\n TUI Command: geompy.FuseCollinearEdgesWithinWire(wire, vertices), +where wire is a wire and vertices is a list of +vertices to be suppressed. + +Our TUI Scripts provide you with useful examples of the +\ref tui_fuse_collinear_edges "Fuse Collinear Edges" functionality usage. + +*/ diff --git a/doc/salome/gui/GEOM/input/repairing_operations.doc b/doc/salome/gui/GEOM/input/repairing_operations.doc index 96ff03ae0..8fd31bab1 100644 --- a/doc/salome/gui/GEOM/input/repairing_operations.doc +++ b/doc/salome/gui/GEOM/input/repairing_operations.doc @@ -27,6 +27,9 @@ splits an edge in two. reverses the normals of the selected faces.
  • \subpage remove_extra_edges_operation_page "Remove extra edges" - removes seam and degenerated edges from the given shape.
  • +
  • \subpage fuse_edges_operation_page "Fuse Collinear Edges within a Wire" - +removes selected vertices from a given wire in case if adjacent edges +are C1 continuous.
  • */ diff --git a/doc/salome/gui/GEOM/input/tui_repairing_operations.doc b/doc/salome/gui/GEOM/input/tui_repairing_operations.doc index 4b1f067cc..0ce8b63db 100644 --- a/doc/salome/gui/GEOM/input/tui_repairing_operations.doc +++ b/doc/salome/gui/GEOM/input/tui_repairing_operations.doc @@ -391,5 +391,57 @@ for point in edge_points: salome.sg.updateObjBrowser(1) \endcode +\anchor tui_fuse_collinear_edges +

    Fuse Collinear Edges within a Wire

    + +\code +import geompy +import salome + +# create vertices +p1 = geompy.MakeVertex(0, 0, 0) +p2 = geompy.MakeVertex(70, 0, 0) +p3 = geompy.MakeVertex(70, 50, 0) +p4 = geompy.MakeVertex(70, 80, 0) +p5 = geompy.MakeVertex(50, 80, 0) +p6 = geompy.MakeVertex(20, 80, 0) +p7 = geompy.MakeVertex(0, 80, 0) +p8 = geompy.MakeVertex(0, 30, 0) + +points = [p1, p2, p3, p4, p5, p6, p7, p8] + +# make a wire +wire_1 = geompy.MakePolyline(points, True) + +# suppress some vertices in the wire +wire_2 = geompy.FuseCollinearEdgesWithinWire(wire_1, [p3]) +wire_3 = geompy.FuseCollinearEdgesWithinWire(wire_1, [p5, p6]) + +# suppress all suitable vertices in the wire +wire_4 = geompy.FuseCollinearEdgesWithinWire(wire_1, []) + +wires = [wire_1, wire_2, wire_3, wire_4] + +# add objects in the study +ii = 1 +for point in points: + geompy.addToStudy(point, "p%d"%ii) + ii = ii + 1 + pass + +ii = 1 +for wire in wires: + geompy.addToStudy(wire, "wire_%d"%ii) + wire_points = geompy.SubShapeAllSortedCentres(wire, geompy.ShapeType["VERTEX"]) + jj = 1 + for point in wire_points: + geompy.addToStudyInFather(wire, point, "point_%d"%jj) + jj = jj + 1 + pass + ii = ii + 1 + pass + +salome.sg.updateObjBrowser(1) +\endcode */ diff --git a/idl/GEOM_Gen.idl b/idl/GEOM_Gen.idl index a3819178c..f3a41ad16 100644 --- a/idl/GEOM_Gen.idl +++ b/idl/GEOM_Gen.idl @@ -2905,11 +2905,18 @@ module GEOM * GEOM_IShapesOperations.GetSubShapeIndex(). * \note The list of vertices coudl be empty, in this case fillet fill be done * at all vertices in given wire + * \param doIgnoreSecantVertices If FALSE, fillet radius is always limited + * by the length of the edges, nearest to the fillet vertex. + * But sometimes the next edge is C1 continuous with the one, nearest to + * the fillet point, and such two (or more) edges can be united to allow + * bigger radius. Set this flag to TRUE to allow collinear edges union, + * thus ignoring the secant vertex (vertices). * \return New GEOM_Object, containing the result shape. */ GEOM_Object MakeFillet1D (in GEOM_Object theShape, in double theR, - in ListOfLong theVertexes); + in ListOfLong theVertexes, + in boolean doIgnoreSecantVertices); /*! * \brief Perform a symmetric chamfer on all edges of the given shape. @@ -3120,6 +3127,16 @@ module GEOM GEOM_Object DivideEdge (in GEOM_Object theObject, in short theEdgeIndex, in double theValue, in boolean isByParameter); + /*! + * \brief Suppress the vertices in the wire in case if adjacent edges are C1 continuous. + * \param theWire Wire to minimize the number of C1 continuous edges in. + * \param theVertices A list of vertices to suppress. If the list + * is empty, all vertices in a wire will be assumed. + * \return New GEOM_Object with modified wire. + */ + GEOM_Object FuseCollinearEdgesWithinWire (in GEOM_Object theWire, + in ListOfGO theVertices); + /*! * \brief Get a list of wires (wrapped in GEOM_Object-s), * that constitute a free boundary of the given shape. diff --git a/resources/Makefile.am b/resources/Makefile.am index 6ec86cabe..d5aab779c 100644 --- a/resources/Makefile.am +++ b/resources/Makefile.am @@ -98,6 +98,7 @@ filletwire.png \ filletface.png \ filling.png \ fuse.png \ +fuse_collinear_edges.png \ geometry.png \ import_picture.png \ limit_tolerance.png \ diff --git a/resources/fuse_collinear_edges.png b/resources/fuse_collinear_edges.png new file mode 100644 index 0000000000000000000000000000000000000000..52d8ff8daa9e75bf4df166dc4f3ef8841831e68f GIT binary patch literal 562 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VXMsm#F#`j)FbFd;%$g$s6l6(v z^mSxl*w|O|J8&|PuaN8!}1{rUgjp4pVqfx)nmLriSie+3_2W=5tC&Y8)xFMO15(KHlLXpnG> zOG`(hX@Nn_1*e*KZD9}xvO)Bi&9DFNI9|X#27z#X#ePWQxz@e~dZ_nx5 z42&(0oQyu1v~6c(;yCJ-miFww0*`OAOiN71l6ykazN!DCzvva=}hDp{|3kfYu4_Y-nv`*4XXIWct=wKR00=)3bcP z6;HbYq+-Mv&Q>uBIP|yb=^94GcL6;a99U2%Jwx4rgnUfg=jAsT0Pgg&ebxsLQ05fC3eEICON_DLG_REMOVE_EXTRA_EDGES remove_extra_edges.png + + ICON_DLG_FUSE_EDGES + fuse_collinear_edges.png + ICON_DLG_CHECKSHAPE check.png @@ -979,6 +983,10 @@ ICO_REMOVE_EXTRA_EDGES remove_extra_edges.png + + ICO_FUSE_EDGES + fuse_collinear_edges.png + ICO_NORMALE normale.png diff --git a/src/GEOMGUI/GEOM_msg_en.ts b/src/GEOMGUI/GEOM_msg_en.ts index 1da62ff22..cf4c13db2 100644 --- a/src/GEOMGUI/GEOM_msg_en.ts +++ b/src/GEOMGUI/GEOM_msg_en.ts @@ -710,6 +710,10 @@ Please, select face, shell or solid and try again GEOM_FILLET_1D Fillet 1D + + GEOM_FILLET_1D_IGNORE_SECANT + Fuse collinear edges to allow bigger radius + GEOM_FILLET_ABORT Fillet can't be computed with radius %1 @@ -4306,6 +4310,30 @@ Please, select face, shell or solid and try again STB_REMOVE_EXTRA_EDGES Remove extra edges + + GEOM_FUSE_EDGES_TITLE + Fuse Collinear Edges within a Wire + + + GEOM_FUSE_EDGES + Fuse edges + + + FUSE_EDGES_NEW_OBJ_NAME + FuseEdges + + + TOP_FUSE_EDGES + Fuse collinear edges within a wire + + + MEN_FUSE_EDGES + Fuse Collinear Edges within a Wire + + + STB_FUSE_EDGES + Fuse collinear edges within a wire + TOP_NORMALE Normal to a face diff --git a/src/GEOMGUI/GeometryGUI.cxx b/src/GEOMGUI/GeometryGUI.cxx index 1ae3b2977..23694246d 100644 --- a/src/GEOMGUI/GeometryGUI.cxx +++ b/src/GEOMGUI/GeometryGUI.cxx @@ -535,6 +535,7 @@ void GeometryGUI::OnGUIEvent( int id ) case GEOMOp::OpGlueEdges: // MENU REPAIR - GLUE EDGES case GEOMOp::OpLimitTolerance: // MENU REPAIR - LIMIT TOLERANCE case GEOMOp::OpRemoveExtraEdges: // MENU REPAIR - REMOVE EXTRA EDGES + case GEOMOp::OpFuseEdges: // MENU REPAIR - FUSE COLLINEAR EDGES libName = "RepairGUI"; break; case GEOMOp::OpProperties: // MENU MEASURE - PROPERTIES @@ -813,6 +814,7 @@ void GeometryGUI::initialize( CAM_Application* app ) createGeomAction( GEOMOp::OpFreeFaces, "CHECK_FREE_FACES" ); createGeomAction( GEOMOp::OpOrientation, "CHANGE_ORIENTATION" ); createGeomAction( GEOMOp::OpRemoveExtraEdges, "REMOVE_EXTRA_EDGES" ); + createGeomAction( GEOMOp::OpFuseEdges, "FUSE_EDGES" ); createGeomAction( GEOMOp::OpPointCoordinates, "POINT_COORDS" ); createGeomAction( GEOMOp::OpProperties, "BASIC_PROPS" ); @@ -1032,6 +1034,7 @@ void GeometryGUI::initialize( CAM_Application* app ) //createMenu( GEOMOp::OpFreeFaces, repairId, -1 ); createMenu( GEOMOp::OpOrientation, repairId, -1 ); createMenu( GEOMOp::OpRemoveExtraEdges, repairId, -1 ); + createMenu( GEOMOp::OpFuseEdges, repairId, -1 ); int measurId = createMenu( tr( "MEN_MEASURES" ), -1, -1, 10 ); createMenu( GEOMOp::OpPointCoordinates, measurId, -1 ); diff --git a/src/GEOMGUI/GeometryGUI_Operations.h b/src/GEOMGUI/GeometryGUI_Operations.h index 77e51f1c3..3d8a41d4e 100644 --- a/src/GEOMGUI/GeometryGUI_Operations.h +++ b/src/GEOMGUI/GeometryGUI_Operations.h @@ -157,6 +157,7 @@ namespace GEOMOp { OpRemoveExtraEdges = 4011, // MENU REPAIR - REMOVE EXTRA EDGES OpLimitTolerance = 4012, // MENU REPAIR - LIMIT TOLERANCE OpGlueEdges = 4013, // MENU REPAIR - GLUE EDGES + OpFuseEdges = 4014, // MENU REPAIR - FUSE COLLINEAR EDGES // MeasureGUI ----------------//-------------------------------- OpProperties = 5000, // MENU MEASURES - PROPERTIES OpCenterMass = 5001, // MENU MEASURES - CENTRE OF MASS diff --git a/src/GEOMImpl/GEOMImpl_Fillet1d.cxx b/src/GEOMImpl/GEOMImpl_Fillet1d.cxx index c4780bd33..0a648ad2c 100644 --- a/src/GEOMImpl/GEOMImpl_Fillet1d.cxx +++ b/src/GEOMImpl/GEOMImpl_Fillet1d.cxx @@ -15,7 +15,6 @@ // 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 : GEOMImpl_Fillet1d.cxx // Module : GEOMImpl @@ -401,7 +400,7 @@ void GEOMImpl_Fillet1d::performNewton(GEOMImpl_Fillet1dPoint* theLeft, else { if (fabs(aB) > fabs(aDet * 1000000.)) - { // possible floating point operations accurancy errors + { // possible floating point operations accuracy errors processPoint(theLeft, theRight, theLeft->GetParam() + aDX / 2.0); // linear division otherwise } else diff --git a/src/GEOMImpl/GEOMImpl_Fillet1d.hxx b/src/GEOMImpl/GEOMImpl_Fillet1d.hxx index 1fb32c489..679e3fc70 100644 --- a/src/GEOMImpl/GEOMImpl_Fillet1d.hxx +++ b/src/GEOMImpl/GEOMImpl_Fillet1d.hxx @@ -15,128 +15,129 @@ // 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 : GEOMImpl_Fillet1d.hxx -// Module : GEOMImpl -// -#ifndef _GEOMImpl_Fillet1d_HeaderFile -#define _GEOMImpl_Fillet1d_HeaderFile - -#include -#include -#include - -#include -#include -#include -#include - -class GEOMImpl_Fillet1dPoint; - -/** -* GEOMImpl_Fillet1d is 1D fillet algorithm on two planar edges with given radius -*/ - -class GEOMImpl_Fillet1d -{ -public: - //! Constructor - //! The fillet 1D algorithm initialise by two edges and plane - Standard_EXPORT GEOMImpl_Fillet1d(const TopoDS_Edge& theEdge1, - const TopoDS_Edge& theEdge2, - const gp_Pln& thePlane); - //! Makes fillet with given radius - //! @returns Standard_True, if at least one result computed - Standard_EXPORT Standard_Boolean Perform(const Standard_Real theRadius); - //! Returns result fillet edge and modified edges as out parameters - Standard_EXPORT TopoDS_Edge Result(const gp_Pnt& thePoint, TopoDS_Edge& theEdge1, TopoDS_Edge& theEdge2); - -private: - //! private methods - void fillPoint(GEOMImpl_Fillet1dPoint*); - void fillDiff(GEOMImpl_Fillet1dPoint*, Standard_Real, Standard_Boolean); - void performNewton(GEOMImpl_Fillet1dPoint*, GEOMImpl_Fillet1dPoint*); - Standard_Boolean processPoint(GEOMImpl_Fillet1dPoint*, GEOMImpl_Fillet1dPoint*, Standard_Real); - - -private: - //! private fields - TopoDS_Edge myEdge1, myEdge2; - Handle(Geom_Plane) myPlane; - Handle(Geom2d_Curve) myCurve1, myCurve2; - Standard_Real myStart1, myEnd1, myStart2, myEnd2, myRadius; - TColStd_ListOfReal myResultParams; - TColStd_SequenceOfInteger myResultOrientation; - Standard_Boolean myStartSide, myEdgesExchnged; - Standard_Integer myDegreeOfRecursion; -}; - - -/** -* GEOMImpl_Fillet1dPoint is an internal class for 1D fillet algorithm -* to store and compare computed solutions on edges -*/ - -class GEOMImpl_Fillet1dPoint -{ -public: - //! Puiblic methods - - //! Constructor - Standard_EXPORT GEOMImpl_Fillet1dPoint(Standard_Real theParam) - {myParam = theParam;} - - //! Make copy of point - //!WARNING: Copies only field values: myParam, myV, myD, myValid - Standard_EXPORT GEOMImpl_Fillet1dPoint* Copy(); // warning: this is not the full copy! - - //! Set/Get parameter - Standard_EXPORT inline void SetParam(Standard_Real theParam) - {myParam = theParam;} - Standard_EXPORT inline Standard_Real GetParam() const - {return myParam;} - Standard_EXPORT inline void SetParam2(const Standard_Real theParam2) - {myParam2 = theParam2;} - Standard_EXPORT inline Standard_Real GetParam2() - { return myParam2 ; } - - //! Returns validity - Standard_EXPORT inline Standard_Boolean IsValid(int theIndex) - {return (Standard_Boolean)myValid.Value(theIndex);} - - //! Get values - Standard_EXPORT inline Standard_Integer GetNBValues() {return myV.Length();} - Standard_EXPORT inline Standard_Real GetValue(Standard_Integer theIndex) - {return myV.Value(theIndex);} - Standard_EXPORT inline Standard_Real GetDiff(Standard_Integer theIndex) - {return myD.Value(theIndex);} - Standard_EXPORT inline Standard_Integer GetNear(Standard_Integer theIndex) - {return myNear.Value(theIndex);} - - //! Set/Get center point - Standard_EXPORT inline void SetCenter(const gp_Pnt2d thePoint) - {myCenter = thePoint;} - Standard_EXPORT inline const gp_Pnt2d GetCenter() - {return myCenter;} - - Standard_EXPORT void AddValue(Standard_Real theValue, Standard_Boolean theIsValid); - - //! compute difference between this and given point - Standard_EXPORT Standard_Boolean ComputeDifference(GEOMImpl_Fillet1dPoint*); - Standard_EXPORT void FilterPoints(GEOMImpl_Fillet1dPoint*); - - //! Check is point contains solution and returns the index of them if any - Standard_EXPORT Standard_Integer HasSolution(Standard_Real theRadius); - //! Remove solution by index - void RemoveSolution(Standard_Integer theIndex); - -private: - //! Private fields - gp_Pnt2d myCenter; - Standard_Real myParam, myParam2; - TColStd_SequenceOfReal myV, myD; - TColStd_SequenceOfInteger myValid, myNear; -}; - -#endif + +// File : GEOMImpl_Fillet1d.hxx +// Module : GEOMImpl + +#ifndef _GEOMImpl_Fillet1d_HeaderFile +#define _GEOMImpl_Fillet1d_HeaderFile + +#include + +#include +#include + +#include + +#include +#include +#include + +class GEOMImpl_Fillet1dPoint; + +/** +* GEOMImpl_Fillet1d is 1D fillet algorithm on two planar edges with given radius +*/ + +class GEOMImpl_Fillet1d +{ +public: + //! Constructor + //! The fillet 1D algorithm is initialised by two edges and plane + Standard_EXPORT GEOMImpl_Fillet1d(const TopoDS_Edge& theEdge1, + const TopoDS_Edge& theEdge2, + const gp_Pln& thePlane); + //! Makes fillet with given radius + //! @returns Standard_True, if at least one result computed + Standard_EXPORT Standard_Boolean Perform(const Standard_Real theRadius); + + //! Returns result fillet edge and modified edges as out parameters + Standard_EXPORT TopoDS_Edge Result(const gp_Pnt& thePoint, TopoDS_Edge& theEdge1, TopoDS_Edge& theEdge2); + +private: + //! private methods + void fillPoint(GEOMImpl_Fillet1dPoint*); + void fillDiff(GEOMImpl_Fillet1dPoint*, Standard_Real, Standard_Boolean); + void performNewton(GEOMImpl_Fillet1dPoint*, GEOMImpl_Fillet1dPoint*); + Standard_Boolean processPoint(GEOMImpl_Fillet1dPoint*, GEOMImpl_Fillet1dPoint*, Standard_Real); + +private: + //! private fields + TopoDS_Edge myEdge1, myEdge2; + Handle(Geom_Plane) myPlane; + Handle(Geom2d_Curve) myCurve1, myCurve2; + Standard_Real myStart1, myEnd1, myStart2, myEnd2, myRadius; + TColStd_ListOfReal myResultParams; + TColStd_SequenceOfInteger myResultOrientation; + Standard_Boolean myStartSide, myEdgesExchnged; + Standard_Integer myDegreeOfRecursion; +}; + + +/** +* GEOMImpl_Fillet1dPoint is an internal class for 1D fillet algorithm +* to store and compare computed solutions on edges +*/ + +class GEOMImpl_Fillet1dPoint +{ +public: + //! Puiblic methods + + //! Constructor + Standard_EXPORT GEOMImpl_Fillet1dPoint(Standard_Real theParam) + {myParam = theParam;} + + //! Make copy of point + //!WARNING: Copies only field values: myParam, myV, myD, myValid + Standard_EXPORT GEOMImpl_Fillet1dPoint* Copy(); // warning: this is not the full copy! + + //! Set/Get parameter + Standard_EXPORT inline void SetParam(Standard_Real theParam) + {myParam = theParam;} + Standard_EXPORT inline Standard_Real GetParam() const + {return myParam;} + Standard_EXPORT inline void SetParam2(const Standard_Real theParam2) + {myParam2 = theParam2;} + Standard_EXPORT inline Standard_Real GetParam2() + { return myParam2 ; } + + //! Returns validity + Standard_EXPORT inline Standard_Boolean IsValid(int theIndex) + {return (Standard_Boolean)myValid.Value(theIndex);} + + //! Get values + Standard_EXPORT inline Standard_Integer GetNBValues() {return myV.Length();} + Standard_EXPORT inline Standard_Real GetValue(Standard_Integer theIndex) + {return myV.Value(theIndex);} + Standard_EXPORT inline Standard_Real GetDiff(Standard_Integer theIndex) + {return myD.Value(theIndex);} + Standard_EXPORT inline Standard_Integer GetNear(Standard_Integer theIndex) + {return myNear.Value(theIndex);} + + //! Set/Get center point + Standard_EXPORT inline void SetCenter(const gp_Pnt2d thePoint) + {myCenter = thePoint;} + Standard_EXPORT inline const gp_Pnt2d GetCenter() + {return myCenter;} + + Standard_EXPORT void AddValue(Standard_Real theValue, Standard_Boolean theIsValid); + + //! compute difference between this and given point + Standard_EXPORT Standard_Boolean ComputeDifference(GEOMImpl_Fillet1dPoint*); + Standard_EXPORT void FilterPoints(GEOMImpl_Fillet1dPoint*); + + //! Checks if point contains solution and returns the index of it if any + Standard_EXPORT Standard_Integer HasSolution(Standard_Real theRadius); + //! Remove solution by index + void RemoveSolution(Standard_Integer theIndex); + +private: + //! Private fields + gp_Pnt2d myCenter; + Standard_Real myParam, myParam2; + TColStd_SequenceOfReal myV, myD; + TColStd_SequenceOfInteger myValid, myNear; +}; + +#endif diff --git a/src/GEOMImpl/GEOMImpl_Fillet1dDriver.cxx b/src/GEOMImpl/GEOMImpl_Fillet1dDriver.cxx index bd99d94d7..f781f228e 100644 --- a/src/GEOMImpl/GEOMImpl_Fillet1dDriver.cxx +++ b/src/GEOMImpl/GEOMImpl_Fillet1dDriver.cxx @@ -15,7 +15,6 @@ // 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 @@ -24,21 +23,10 @@ #include #include #include -#include #include +#include #include -#include -#include -#include - -#include -#include -#include -#include - -#include - #include #include #include @@ -54,6 +42,18 @@ #include #include #include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include //======================================================================= //function : GetID @@ -161,32 +161,112 @@ Standard_Integer GEOMImpl_Fillet1dDriver::Execute(TFunction_Logbook& log) const TopoDS_Wire aWire = TopoDS::Wire(aShape); - double rad = aCI.GetR(); + bool doIgnoreSecantPoints = aCI.GetFlag(); - if ( rad < Precision::Confusion()) + double rad = aCI.GetR(); + if (rad < Precision::Confusion()) return 0; // collect vertices for make fillet TopTools_ListOfShape aVertexList; - TopTools_MapOfShape mapShape; + TopTools_IndexedMapOfShape anIndices; + TopExp::MapShapes(aWire, anIndices); int aLen = aCI.GetLength(); - if ( aLen > 0 ) { - for (int ind = 1; ind <= aLen; ind++) { - TopoDS_Shape aShapeVertex; - if (GEOMImpl_ILocalOperations::GetSubShape - (aWire, aCI.GetVertex(ind), aShapeVertex)) - if (mapShape.Add(aShapeVertex)) - aVertexList.Append( aShapeVertex ); + if (aLen > 0) { + for (int ii = 1; ii <= aLen; ii++) { + int ind = aCI.GetVertex(ii); + if (1 <= ind && ind <= anIndices.Extent()) { + TopoDS_Shape aShapeVertex = anIndices.FindKey(ind); + if (aShapeVertex.ShapeType() == TopAbs_VERTEX) + aVertexList.Append(aShapeVertex); + } } - } else { // get all vertices from wire - TopExp_Explorer anExp( aWire, TopAbs_VERTEX ); - for ( ; anExp.More(); anExp.Next() ) { + } + else { // get all vertices from wire + TopTools_MapOfShape mapShape; + TopExp_Explorer anExp (aWire, TopAbs_VERTEX); + for (; anExp.More(); anExp.Next()) { if (mapShape.Add(anExp.Current())) - aVertexList.Append( anExp.Current() ); + aVertexList.Append(anExp.Current()); } } if (aVertexList.IsEmpty()) - Standard_ConstructionError::Raise("Invalid input no vertices to make fillet"); + Standard_ConstructionError::Raise("Invalid input: no vertices to make fillet"); + + // at first we try to make fillet on the initial wire (without edges fusing) + bool isFinalPass = !doIgnoreSecantPoints; + TopoDS_Wire aResult; + bool isAllStepsOk = MakeFillet(aWire, aVertexList, rad, isFinalPass, aResult); + + // try to fuse collinear edges to allow bigger radius + if (!isFinalPass && !isAllStepsOk) { + // 1. Fuse + TopoDS_Shape aShapeNew; + Handle(TColStd_HSequenceOfTransient) aVerts; + GEOMImpl_HealingDriver::FuseCollinearEdges(aWire, aVerts, aShapeNew); + TopoDS_Wire aWireNew = TopoDS::Wire(aShapeNew); + + // 2. Rebuild the list of vertices (by coincidence) + Standard_Real tol, tolMax = Precision::Confusion(); + for (TopExp_Explorer ExV (aWireNew, TopAbs_VERTEX); ExV.More(); ExV.Next()) { + TopoDS_Vertex Vertex = TopoDS::Vertex(ExV.Current()); + tol = BRep_Tool::Tolerance(Vertex); + if (tol > tolMax) + tolMax = tol; + } + + TopTools_ListOfShape aVertexListNew; + TopTools_IndexedMapOfShape anIndicesNew; + TopExp::MapShapes(aWireNew, anIndicesNew); + TopTools_ListIteratorOfListOfShape anIt (aVertexList); + for (; anIt.More(); anIt.Next()) { + TopoDS_Vertex aV = TopoDS::Vertex(anIt.Value()); + if (anIndicesNew.Contains(aV)) + aVertexListNew.Append(aV); + else { + // try to find by coords in the new wire + gp_Pnt aP = BRep_Tool::Pnt(aV); + + bool isFound = false; + TopTools_MapOfShape mapShape; + TopExp_Explorer exp (aWireNew, TopAbs_VERTEX); + for (; exp.More() && !isFound; exp.Next()) { + if (mapShape.Add(exp.Current())) { + TopoDS_Vertex aVi = TopoDS::Vertex(exp.Current()); + gp_Pnt aPi = BRep_Tool::Pnt(aVi); + if (aPi.Distance(aP) < tolMax) { + aVertexListNew.Append(aVi); + isFound = true; + } + } + } + } + } + + // 3. Repeat the fillet algorithm + isFinalPass = true; + MakeFillet(aWireNew, aVertexListNew, rad, isFinalPass, aResult); + } + + aFunction->SetValue(aResult); + log.SetTouched(Label()); + + return 1; +} + +//======================================================================= +//function : MakeFillet +//purpose : +//======================================================================= +bool GEOMImpl_Fillet1dDriver::MakeFillet(const TopoDS_Wire& aWire, + const TopTools_ListOfShape& aVertexList, + const Standard_Real rad, + bool isFinalPass, + TopoDS_Wire& aResult) const +{ + // this variable is needed to break execution + // in case of fillet failure and try to fuse edges + bool isAllStepsOk = true; //INFO: this algorithm implemented in assumption that user can select both // vertices of some edges to make fillet. In this case we should remember @@ -220,54 +300,69 @@ Standard_Integer GEOMImpl_Fillet1dDriver::Execute(TFunction_Logbook& log) const if ( !takePlane(anEdge1, anEdge2, aV, aPlane) ) continue; // seems edges does not belong to same plane or parallel (fillet can not be build) - GEOMImpl_Fillet1d aFilletAlgo(anEdge1, anEdge2, aPlane); - if ( !aFilletAlgo.Perform(rad) ) - continue; // can not create fillet with given radius + GEOMImpl_Fillet1d aFilletAlgo (anEdge1, anEdge2, aPlane); + if (!aFilletAlgo.Perform(rad)) { + if (isFinalPass) + continue; // can not create fillet with given radius + else { + isAllStepsOk = false; + break; // can not create fillet with given radius + } + } // take fillet result in given vertex TopoDS_Edge aModifE1, aModifE2; TopoDS_Edge aNewE = aFilletAlgo.Result(BRep_Tool::Pnt(aV), aModifE1, aModifE2); - if (aNewE.IsNull()) - continue; // no result found + if (aNewE.IsNull()) { + if (isFinalPass) + continue; // no result found + else { + isAllStepsOk = false; + break; // no result found + } + } // add new created edges and take modified edges - aListOfNewEdge.Append( aNewE ); + aListOfNewEdge.Append(aNewE); - // check if face edges modified, - // if yes, than map to original edges (from vertex-edges list), because edges can be modified before + // check if wire edges modified, + // if yes, then map to original edges (from vertex-edges list), because edges can be modified before if (aModifE1.IsNull() || !anEdge1.IsSame( aModifE1 )) addEdgeRelation( anEdgeToEdgeMap, TopoDS::Edge(aVertexEdges.First()), aModifE1 ); if (aModifE2.IsNull() || !anEdge2.IsSame( aModifE2 )) addEdgeRelation( anEdgeToEdgeMap, TopoDS::Edge(aVertexEdges.Last()), aModifE2 ); } - if ( anEdgeToEdgeMap.IsEmpty() && aListOfNewEdge.IsEmpty() ) { - StdFail_NotDone::Raise("1D Fillet can't be computed on the given shape with the given radius"); - return 0; + if (anEdgeToEdgeMap.IsEmpty() && aListOfNewEdge.IsEmpty()) { + if (isFinalPass) + StdFail_NotDone::Raise("1D Fillet can't be computed on the given shape with the given radius"); + else + isAllStepsOk = false; } + if (!isAllStepsOk) + return false; + // create new wire instead of original - for ( TopExp_Explorer anExp( aWire, TopAbs_EDGE ); anExp.More(); anExp.Next() ) { + for (TopExp_Explorer anExp (aWire, TopAbs_EDGE); anExp.More(); anExp.Next()) { TopoDS_Shape anEdge = anExp.Current(); - if ( !anEdgeToEdgeMap.IsBound( anEdge ) ) - aListOfNewEdge.Append( anEdge ); - else if (!anEdgeToEdgeMap.Find( anEdge ).IsNull()) - aListOfNewEdge.Append( anEdgeToEdgeMap.Find( anEdge ) ); + if (!anEdgeToEdgeMap.IsBound(anEdge)) + aListOfNewEdge.Append(anEdge); + else if (!anEdgeToEdgeMap.Find(anEdge).IsNull()) + aListOfNewEdge.Append(anEdgeToEdgeMap.Find(anEdge)); } - GEOMImpl_IShapesOperations::SortShapes( aListOfNewEdge ); + GEOMImpl_IShapesOperations::SortShapes(aListOfNewEdge); BRepBuilderAPI_MakeWire aWireTool; - aWireTool.Add( aListOfNewEdge ); + aWireTool.Add(aListOfNewEdge); aWireTool.Build(); if (!aWireTool.IsDone()) return 0; - aWire = aWireTool.Wire(); - aFunction->SetValue(aWire); - log.SetTouched(Label()); + aResult = aWireTool.Wire(); - return 1; + return isAllStepsOk; } diff --git a/src/GEOMImpl/GEOMImpl_Fillet1dDriver.hxx b/src/GEOMImpl/GEOMImpl_Fillet1dDriver.hxx index 87b9ce897..dc8b9da89 100644 --- a/src/GEOMImpl/GEOMImpl_Fillet1dDriver.hxx +++ b/src/GEOMImpl/GEOMImpl_Fillet1dDriver.hxx @@ -15,11 +15,10 @@ // 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 : GEOMImpl_Fillet1dDriver.ixx // Module : GEOMImpl -// + #ifndef _GEOMImpl_Fillet1dDriver_HeaderFile #define _GEOMImpl_Fillet1dDriver_HeaderFile @@ -48,6 +47,9 @@ #include #endif +#include +#include + class Standard_Transient; class Handle_Standard_Type; class Handle(TFunction_Driver); @@ -123,37 +125,42 @@ class GEOMImpl_Fillet1dDriver : public TFunction_Driver { public: - inline void* operator new(size_t,void* anAddress) - { - return anAddress; - } - inline void* operator new(size_t size) - { - return Standard::Allocate(size); - } - inline void operator delete(void *anAddress) - { - if (anAddress) Standard::Free((Standard_Address&)anAddress); - } - - // Methods PUBLIC - // -Standard_EXPORT GEOMImpl_Fillet1dDriver(); -Standard_EXPORT virtual Standard_Integer Execute(TFunction_Logbook& log) const; -Standard_EXPORT virtual void Validate(TFunction_Logbook&) const {} -Standard_EXPORT Standard_Boolean MustExecute(const TFunction_Logbook&) const { return Standard_True; } -Standard_EXPORT static const Standard_GUID& GetID(); -Standard_EXPORT ~GEOMImpl_Fillet1dDriver() {}; - - - // Type management - // -Standard_EXPORT friend Handle_Standard_Type& GEOMImpl_Fillet1dDriver_Type_(); -Standard_EXPORT const Handle(Standard_Type)& DynamicType() const - { return STANDARD_TYPE(GEOMImpl_Fillet1dDriver) ; } -Standard_EXPORT Standard_Boolean IsKind(const Handle(Standard_Type)& AType) const - { return (STANDARD_TYPE(GEOMImpl_Fillet1dDriver) == AType || TFunction_Driver::IsKind(AType)); } - + inline void* operator new(size_t,void* anAddress) + { + return anAddress; + } + inline void* operator new(size_t size) + { + return Standard::Allocate(size); + } + inline void operator delete(void *anAddress) + { + if (anAddress) Standard::Free((Standard_Address&)anAddress); + } + + // Methods PUBLIC + // + Standard_EXPORT GEOMImpl_Fillet1dDriver(); + Standard_EXPORT ~GEOMImpl_Fillet1dDriver() {}; + + Standard_EXPORT static const Standard_GUID& GetID(); + + Standard_EXPORT virtual Standard_Integer Execute(TFunction_Logbook& log) const; + Standard_EXPORT virtual void Validate(TFunction_Logbook&) const {} + Standard_EXPORT Standard_Boolean MustExecute(const TFunction_Logbook&) const { return Standard_True; } + + // Type management + // + Standard_EXPORT friend Handle_Standard_Type& GEOMImpl_Fillet1dDriver_Type_(); + Standard_EXPORT const Handle(Standard_Type)& DynamicType() const + { return STANDARD_TYPE(GEOMImpl_Fillet1dDriver) ; } + Standard_EXPORT Standard_Boolean IsKind(const Handle(Standard_Type)& AType) const + { return (STANDARD_TYPE(GEOMImpl_Fillet1dDriver) == AType || TFunction_Driver::IsKind(AType)); } + +private: + + Standard_EXPORT bool MakeFillet(const TopoDS_Wire&, const TopTools_ListOfShape&, + const Standard_Real, bool, TopoDS_Wire&) const; }; diff --git a/src/GEOMImpl/GEOMImpl_HealingDriver.cxx b/src/GEOMImpl/GEOMImpl_HealingDriver.cxx index f21bc14f9..7771033fb 100644 --- a/src/GEOMImpl/GEOMImpl_HealingDriver.cxx +++ b/src/GEOMImpl/GEOMImpl_HealingDriver.cxx @@ -18,7 +18,6 @@ // 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 @@ -28,6 +27,7 @@ #include #include +#include #include #include @@ -38,22 +38,30 @@ #include #include +#include + +#include +#include + #include +#include +#include +#include #include +#include #include #include #include +#include +#include #include -#include -#include -#include -#include #include #include +#include //======================================================================= //function : raiseNotDoneExeption @@ -135,6 +143,12 @@ Standard_Integer GEOMImpl_HealingDriver::Execute(TFunction_Logbook& log) const case LIMIT_TOLERANCE: LimitTolerance(&HI, anOriginalShape, aShape); break; + case FUSE_COLLINEAR_EDGES: + { + Handle(TColStd_HSequenceOfTransient) aVerts = HI.GetShapes(); + FuseCollinearEdges(anOriginalShape, aVerts, aShape); + } + break; default: return 0; } @@ -505,6 +519,258 @@ void GEOMImpl_HealingDriver::LimitTolerance (GEOMImpl_IHealing* theHI, StdFail_NotDone::Raise("Non valid shape result"); } +//======================================================================= +//function : FuseCollinearEdges +//purpose : +//======================================================================= +void GEOMImpl_HealingDriver::FuseCollinearEdges (const TopoDS_Shape& theOriginalShape, + const Handle(TColStd_HSequenceOfTransient)& aVerts, + TopoDS_Shape& theOutShape) +{ + if (theOriginalShape.ShapeType() != TopAbs_WIRE) + Standard_TypeMismatch::Raise("Not a wire is given"); + + // Tolerances + Standard_Real AngTol = Precision::Angular(); + Standard_Real LinTol = Precision::Confusion(); + Standard_Real tol; + for (TopExp_Explorer ExV (theOriginalShape, TopAbs_VERTEX); ExV.More(); ExV.Next()) { + TopoDS_Vertex Vertex = TopoDS::Vertex(ExV.Current()); + tol = BRep_Tool::Tolerance(Vertex); + if (tol > LinTol) + LinTol = tol; + } + + // 1. Make a copy to prevent the original shape changes. + TopoDS_Shape aWire; + TColStd_IndexedDataMapOfTransientTransient aMapTShapes; + TNaming_CopyShape::CopyTool(theOriginalShape, aMapTShapes, aWire); + TopoDS_Wire theWire = TopoDS::Wire(aWire); + + // 2. Sub-shapes of the wire + TopTools_MapOfShape aMapToRemove; + + TopTools_IndexedMapOfShape anOldIndices; + TopExp::MapShapes(theOriginalShape, anOldIndices); + + TopTools_IndexedMapOfShape aNewIndices; + TopExp::MapShapes(theWire, aNewIndices); + + // 3. Collect vertices of the wire, same or equal to the given vertices + bool removeAll = false; + if (aVerts.IsNull() || aVerts->Length() < 1) + removeAll = true; + + if (!removeAll) { + for (unsigned int ind = 1; ind <= aVerts->Length(); ind++) { + Handle(GEOM_Function) aRefShape = Handle(GEOM_Function)::DownCast(aVerts->Value(ind)); + TopoDS_Shape aShape_i = aRefShape->GetValue(); + if (aShape_i.IsNull()) + Standard_NullObject::Raise("Null vertex given"); + if (aShape_i.ShapeType() != TopAbs_VERTEX) + Standard_TypeMismatch::Raise("Shape to suppress is not a vertex"); + + // find vertices shared with the initial wire + if (anOldIndices.Contains(aShape_i)) { + aMapToRemove.Add(aNewIndices.FindKey(anOldIndices.FindIndex(aShape_i))); + } else { + // try to find by coords in the new wire + TopoDS_Vertex aVert = TopoDS::Vertex(aShape_i); + gp_Pnt aP = BRep_Tool::Pnt(aVert); + + bool isFound = false; + TopTools_MapOfShape mapShape; + TopExp_Explorer exp (theWire, TopAbs_VERTEX); + for (; exp.More() && !isFound; exp.Next()) { + if (mapShape.Add(exp.Current())) { + TopoDS_Vertex aVi = TopoDS::Vertex(exp.Current()); + gp_Pnt aPi = BRep_Tool::Pnt(aVi); + if (aPi.Distance(aP) < LinTol) { + aMapToRemove.Add(aVi); + isFound = true; + } + } + } + } + } + } + + /* + BRepLib::BuildCurves3d(theWire); + Handle(ShapeFix_Shape) Fixer = new ShapeFix_Shape(theWire); + Fixer->SetPrecision(LinTol); + Fixer->SetMaxTolerance(LinTol); + Fixer->Perform(); + theWire = TopoDS::Wire(Fixer->Shape()); + */ + + TopoDS_Edge prevEdge; + TopTools_ListOfShape finalList, currChain; + + BRepTools_WireExplorer wexp (theWire); + if (wexp.More()) { + prevEdge = wexp.Current(); + currChain.Append(prevEdge); + wexp.Next(); + } + else { + Standard_NullObject::Raise("Empty wire given"); + } + + for (; wexp.More(); wexp.Next()) { + TopoDS_Edge anEdge = wexp.Current(); + TopoDS_Vertex CurVertex = wexp.CurrentVertex(); + + bool continueChain = false; + if (aMapToRemove.Contains(CurVertex) || removeAll) { + // if C1 -> continue chain + if (AreEdgesC1(prevEdge, anEdge)) { + continueChain = true; + } + } + + if (!continueChain) { + if (currChain.Extent() == 1) { + // add one edge to the final list + finalList.Append(currChain.First()); + } + else { + // make wire from the list of edges + BRep_Builder B; + TopoDS_Wire aCurrWire; + B.MakeWire(aCurrWire); + TopTools_ListIteratorOfListOfShape itEdges (currChain); + for (; itEdges.More(); itEdges.Next()) { + TopoDS_Shape aValue = itEdges.Value(); + B.Add(aCurrWire, TopoDS::Edge(aValue)); + } + + // make edge from the wire + TopoDS_Edge anEdge = GEOMImpl_ShapeDriver::MakeEdgeFromWire(aCurrWire, LinTol, AngTol); + + // add this new edge to the final list + finalList.Append(anEdge); + } + currChain.Clear(); + } + + // add one edge to the chain + currChain.Append(anEdge); + prevEdge = anEdge; + } + + if (currChain.Extent() == 1) { + // add one edge to the final list + finalList.Append(currChain.First()); + } + else { + // make wire from the list of edges + BRep_Builder B; + TopoDS_Wire aCurrWire; + B.MakeWire(aCurrWire); + TopTools_ListIteratorOfListOfShape itEdges (currChain); + for (; itEdges.More(); itEdges.Next()) { + TopoDS_Shape aValue = itEdges.Value(); + B.Add(aCurrWire, TopoDS::Edge(aValue)); + } + + // make edge from the wire + TopoDS_Edge anEdge = GEOMImpl_ShapeDriver::MakeEdgeFromWire(aCurrWire, LinTol, AngTol); + + // add this new edge to the final list + finalList.Append(anEdge); + } + + BRep_Builder B; + TopoDS_Wire aFinalWire; + B.MakeWire(aFinalWire); + TopTools_ListIteratorOfListOfShape itEdges (finalList); + for (; itEdges.More(); itEdges.Next()) { + TopoDS_Shape aValue = itEdges.Value(); + B.Add(aFinalWire, TopoDS::Edge(aValue)); + } + theOutShape = aFinalWire; + + BRepCheck_Analyzer ana (theOutShape, Standard_True); + if (!ana.IsValid()) + StdFail_NotDone::Raise("Non valid shape result"); +} + +//======================================================================= +//function : AreEdgesC1 +//purpose : +//======================================================================= +Standard_Boolean GEOMImpl_HealingDriver::AreEdgesC1 (const TopoDS_Edge& E1, const TopoDS_Edge& E2) +{ + BRepAdaptor_Curve aCurve1 (E1); + BRepAdaptor_Curve aCurve2 (E2); + + if (aCurve1.Continuity() == GeomAbs_C0 || aCurve2.Continuity() == GeomAbs_C0) + return Standard_False; + + Standard_Real tol, tolMax = Precision::Confusion(); + for (TopExp_Explorer ExV1 (E1, TopAbs_VERTEX); ExV1.More(); ExV1.Next()) { + TopoDS_Vertex Vertex = TopoDS::Vertex(ExV1.Current()); + tol = BRep_Tool::Tolerance(Vertex); + if (tol > tolMax) + tolMax = tol; + } + for (TopExp_Explorer ExV2 (E2, TopAbs_VERTEX); ExV2.More(); ExV2.Next()) { + TopoDS_Vertex Vertex = TopoDS::Vertex(ExV2.Current()); + tol = BRep_Tool::Tolerance(Vertex); + if (tol > tolMax) + tolMax = tol; + } + + Standard_Real f1, l1, f2, l2; + f1 = aCurve1.FirstParameter(); + l1 = aCurve1.LastParameter(); + f2 = aCurve2.FirstParameter(); + l2 = aCurve2.LastParameter(); + + if (f1 > l1) { + Standard_Real tmp = f1; + f1 = l1; + l1 = tmp; + } + + if (f2 > l2) { + Standard_Real tmp = f2; + f2 = l2; + l2 = tmp; + } + + gp_Pnt pf1, pl1, pf2, pl2; + gp_Vec vf1, vl1, vf2, vl2; + aCurve1.D1(f1, pf1, vf1); + aCurve1.D1(l1, pl1, vl1); + aCurve2.D1(f2, pf2, vf2); + aCurve2.D1(l2, pl2, vl2); + + // pf1--->---pl1.pf2--->---pl2 + if (pl1.SquareDistance(pf2) < tolMax*tolMax) { + if (vl1.Angle(vf2) < Precision::Angular()) + return Standard_True; + } + // pl1---<---pf1.pf2--->---pl2 + else if (pf1.SquareDistance(pf2) < tolMax*tolMax) { + if (vf1.Angle(-vf2) < Precision::Angular()) + return Standard_True; + } + // pf1--->---pl1.pl2---<---pf2 + else if (pl1.SquareDistance(pl2) < tolMax*tolMax) { + if (vl1.Angle(-vl2) < Precision::Angular()) + return Standard_True; + } + // pl1---<---pf1.pl2---<---pf2 + else { + if (vf1.Angle(vl2) < Precision::Angular()) + return Standard_True; + } + + return Standard_False; +} + //======================================================================= //function : GEOMImpl_HealingDriver_Type_ //purpose : diff --git a/src/GEOMImpl/GEOMImpl_HealingDriver.hxx b/src/GEOMImpl/GEOMImpl_HealingDriver.hxx index 9493912d9..f1d740d6f 100644 --- a/src/GEOMImpl/GEOMImpl_HealingDriver.hxx +++ b/src/GEOMImpl/GEOMImpl_HealingDriver.hxx @@ -57,6 +57,8 @@ class GEOMImpl_HealingDriver; class GEOMImpl_IHealing; #include +#include +#include Standard_EXPORT Handle_Standard_Type& STANDARD_TYPE(GEOMImpl_HealingDriver); @@ -128,45 +130,51 @@ class GEOMImpl_HealingDriver : public TFunction_Driver { public: - inline void* operator new(size_t,void* anAddress) - { - return anAddress; - } - inline void* operator new(size_t size) - { - return Standard::Allocate(size); - } - inline void operator delete(void *anAddress) - { - if (anAddress) Standard::Free((Standard_Address&)anAddress); - } - - // Methods PUBLIC - // -Standard_EXPORT GEOMImpl_HealingDriver(); -Standard_EXPORT virtual Standard_Integer Execute(TFunction_Logbook& log) const; -Standard_EXPORT virtual void Validate(TFunction_Logbook&) const {} -Standard_EXPORT Standard_Boolean MustExecute(const TFunction_Logbook&) const { return Standard_True; } -Standard_EXPORT static const Standard_GUID& GetID(); -Standard_EXPORT ~GEOMImpl_HealingDriver() {}; - - - // Type management - // -Standard_EXPORT friend Handle_Standard_Type& GEOMImpl_HealingDriver_Type_(); -Standard_EXPORT const Handle(Standard_Type)& DynamicType() const { return STANDARD_TYPE(GEOMImpl_HealingDriver) ; } -Standard_EXPORT Standard_Boolean IsKind(const Handle(Standard_Type)& AType) const { return (STANDARD_TYPE(GEOMImpl_HealingDriver) == AType || TFunction_Driver::IsKind(AType)); } + inline void* operator new(size_t,void* anAddress) + { + return anAddress; + } + inline void* operator new(size_t size) + { + return Standard::Allocate(size); + } + inline void operator delete(void *anAddress) + { + if (anAddress) Standard::Free((Standard_Address&)anAddress); + } + + // Methods PUBLIC + // + Standard_EXPORT GEOMImpl_HealingDriver(); + Standard_EXPORT ~GEOMImpl_HealingDriver() {}; + + Standard_EXPORT static const Standard_GUID& GetID(); + + Standard_EXPORT virtual Standard_Integer Execute(TFunction_Logbook& log) const; + Standard_EXPORT virtual void Validate(TFunction_Logbook&) const {} + Standard_EXPORT Standard_Boolean MustExecute(const TFunction_Logbook&) const { return Standard_True; } + + Standard_EXPORT static Standard_Boolean AreEdgesC1 (const TopoDS_Edge& E1, const TopoDS_Edge& E2); + Standard_EXPORT static void FuseCollinearEdges (const TopoDS_Shape&, + const Handle(TColStd_HSequenceOfTransient)&, + TopoDS_Shape&); + + // Type management + // + Standard_EXPORT friend Handle_Standard_Type& GEOMImpl_HealingDriver_Type_(); + Standard_EXPORT const Handle(Standard_Type)& DynamicType() const { return STANDARD_TYPE(GEOMImpl_HealingDriver) ; } + Standard_EXPORT Standard_Boolean IsKind(const Handle(Standard_Type)& AType) const { return (STANDARD_TYPE(GEOMImpl_HealingDriver) == AType || TFunction_Driver::IsKind(AType)); } private: -Standard_Boolean ShapeProcess ( GEOMImpl_IHealing*, const TopoDS_Shape&, TopoDS_Shape& ) const; -Standard_Boolean SuppressFaces ( GEOMImpl_IHealing*, const TopoDS_Shape&, TopoDS_Shape& ) const; -Standard_Boolean CloseContour ( GEOMImpl_IHealing*, const TopoDS_Shape&, TopoDS_Shape& ) const; -Standard_Boolean RemoveIntWires( GEOMImpl_IHealing*, const TopoDS_Shape&, TopoDS_Shape& ) const; -Standard_Boolean RemoveHoles ( GEOMImpl_IHealing*, const TopoDS_Shape&, TopoDS_Shape& ) const; -Standard_Boolean Sew ( GEOMImpl_IHealing*, const TopoDS_Shape&, TopoDS_Shape& ) const; -Standard_Boolean AddPointOnEdge( GEOMImpl_IHealing*, const TopoDS_Shape&, TopoDS_Shape& ) const; -Standard_Boolean ChangeOrientation( GEOMImpl_IHealing*, const TopoDS_Shape&, TopoDS_Shape& ) const; -void LimitTolerance( GEOMImpl_IHealing*, const TopoDS_Shape&, TopoDS_Shape& ) const; + Standard_Boolean ShapeProcess ( GEOMImpl_IHealing*, const TopoDS_Shape&, TopoDS_Shape& ) const; + Standard_Boolean SuppressFaces ( GEOMImpl_IHealing*, const TopoDS_Shape&, TopoDS_Shape& ) const; + Standard_Boolean CloseContour ( GEOMImpl_IHealing*, const TopoDS_Shape&, TopoDS_Shape& ) const; + Standard_Boolean RemoveIntWires( GEOMImpl_IHealing*, const TopoDS_Shape&, TopoDS_Shape& ) const; + Standard_Boolean RemoveHoles ( GEOMImpl_IHealing*, const TopoDS_Shape&, TopoDS_Shape& ) const; + Standard_Boolean Sew ( GEOMImpl_IHealing*, const TopoDS_Shape&, TopoDS_Shape& ) const; + Standard_Boolean AddPointOnEdge( GEOMImpl_IHealing*, const TopoDS_Shape&, TopoDS_Shape& ) const; + Standard_Boolean ChangeOrientation( GEOMImpl_IHealing*, const TopoDS_Shape&, TopoDS_Shape& ) const; + void LimitTolerance( GEOMImpl_IHealing*, const TopoDS_Shape&, TopoDS_Shape& ) const; }; diff --git a/src/GEOMImpl/GEOMImpl_IFillet1d.hxx b/src/GEOMImpl/GEOMImpl_IFillet1d.hxx index 484d92b65..eb3ef0ee6 100644 --- a/src/GEOMImpl/GEOMImpl_IFillet1d.hxx +++ b/src/GEOMImpl/GEOMImpl_IFillet1d.hxx @@ -15,16 +15,16 @@ // 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 interface to a function for the Fillet1d and creation. -// + #include "GEOM_Function.hxx" #define FILLET1D_ARG_SH 1 #define FILLET1D_ARG_R 2 -#define FILLET1D_ARG_LENG 3 -#define FILLET1D_ARG_LAST 4 +#define FILLET1D_ARG_BOOL 3 +#define FILLET1D_ARG_LENG 4 +#define FILLET1D_ARG_LAST 5 class GEOMImpl_IFillet1d { @@ -36,11 +36,13 @@ class GEOMImpl_IFillet1d Handle(GEOM_Function) GetShape() { return _func->GetReference(FILLET1D_ARG_SH); } void SetR(double theR) { _func->SetReal(FILLET1D_ARG_R, theR); } + void SetFlag(const Standard_Boolean theFlag) { _func->SetInteger(FILLET1D_ARG_BOOL, theFlag ? 1 : 0); } void SetLength(int theLen) { _func->SetInteger(FILLET1D_ARG_LENG, theLen); } void SetVertex(int theInd, int theVertex) { _func->SetInteger(FILLET1D_ARG_LAST + theInd, theVertex); } double GetR() { return _func->GetReal(FILLET1D_ARG_R); } + Standard_Boolean GetFlag() { return (_func->GetInteger(FILLET1D_ARG_BOOL) == 1); } int GetLength() { return _func->GetInteger(FILLET1D_ARG_LENG); } int GetVertex(int theInd) { return _func->GetInteger(FILLET1D_ARG_LAST + theInd); } diff --git a/src/GEOMImpl/GEOMImpl_IHealing.hxx b/src/GEOMImpl/GEOMImpl_IHealing.hxx index 91f8c34f0..2ee387908 100755 --- a/src/GEOMImpl/GEOMImpl_IHealing.hxx +++ b/src/GEOMImpl/GEOMImpl_IHealing.hxx @@ -18,28 +18,31 @@ // 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 "GEOM_Function.hxx" #include #include - -#define ARG_SHAPE_PROCESS_OPERATORS 1 -#define ARG_SHAPE_PROCESS_PARAMS 2 -#define ARG_SHAPE_PROCESS_VALUES 3 -#define ARG_ORIGINAL 4 -#define ARG_LIST_ARGUMENTS 5 -#define ARG_IS_COMMON_VERTEX 6 -#define ARG_TOLERANCE 7 -#define ARG_DEV_EDGE_VALUE 8 -#define ARG_IS_BY_PARAMETER 9 -#define ARG_SUBSHAPE_INDEX 10 - +#include "TColStd_HSequenceOfTransient.hxx" class GEOMImpl_IHealing { public: + + enum { + ARG_SHAPE_PROCESS_OPERATORS = 1, + ARG_SHAPE_PROCESS_PARAMS = 2, + ARG_SHAPE_PROCESS_VALUES = 3, + ARG_ORIGINAL = 4, + ARG_LIST_ARGUMENTS = 5, + ARG_IS_COMMON_VERTEX = 6, + ARG_TOLERANCE = 7, + ARG_DEV_EDGE_VALUE = 8, + ARG_IS_BY_PARAMETER = 9, + ARG_SUBSHAPE_INDEX = 10, + ARG_LIST_SHAPES = 11 + }; + GEOMImpl_IHealing(Handle(GEOM_Function) theFunction): _func(theFunction) {} void SetOperators( const Handle(TColStd_HArray1OfExtendedString)& arr ) { if ( !arr.IsNull() ) _func->SetStringArray(ARG_SHAPE_PROCESS_OPERATORS, arr); } @@ -75,6 +78,11 @@ public: void SetIndex( Standard_Integer val ) { _func->SetInteger(ARG_SUBSHAPE_INDEX, val); } Standard_Integer GetIndex() { return _func->GetInteger(ARG_SUBSHAPE_INDEX); } + void SetShapes(const Handle(TColStd_HSequenceOfTransient)& theShapes) + { _func->SetReferenceList(ARG_LIST_SHAPES, theShapes); } + Handle(TColStd_HSequenceOfTransient) GetShapes() + { return _func->GetReferenceList(ARG_LIST_SHAPES); } + private: Handle(GEOM_Function) _func; }; diff --git a/src/GEOMImpl/GEOMImpl_IHealingOperations.cxx b/src/GEOMImpl/GEOMImpl_IHealingOperations.cxx index 9043f811a..3db0e6a93 100644 --- a/src/GEOMImpl/GEOMImpl_IHealingOperations.cxx +++ b/src/GEOMImpl/GEOMImpl_IHealingOperations.cxx @@ -691,15 +691,13 @@ Handle(GEOM_Object) GEOMImpl_IHealingOperations::DivideEdge (Handle(GEOM_Object) #if OCC_VERSION_LARGE > 0x06010000 OCC_CATCH_SIGNALS; #endif - if (!GetSolver()->ComputeFunction(aFunction)) - { + if (!GetSolver()->ComputeFunction(aFunction)) { SetErrorCode("Healing driver failed"); return NULL; } } - catch (Standard_Failure) - { - Handle(Standard_Failure) aFail = Standard_Failure::Caught(); + catch (Standard_Failure) { + Handle(Standard_Failure) aFail = Standard_Failure::Caught(); SetErrorCode(aFail->GetMessageString()); return NULL; } @@ -712,6 +710,81 @@ Handle(GEOM_Object) GEOMImpl_IHealingOperations::DivideEdge (Handle(GEOM_Object) return aNewObject; } +//============================================================================= +/*! + * FuseCollinearEdgesWithinWire + */ +//============================================================================= +Handle(GEOM_Object) GEOMImpl_IHealingOperations::FuseCollinearEdgesWithinWire + (Handle(GEOM_Object) theWire, + std::list theVertices) +{ + SetErrorCode(KO); + + if (theWire.IsNull()) return NULL; + + // Add a new object + Handle(GEOM_Object) aRes = GetEngine()->AddObject(GetDocID(), theWire->GetType()); + + // Add a new function + Handle(GEOM_Function) aFunction; + aFunction = aRes->AddFunction(GEOMImpl_HealingDriver::GetID(), FUSE_COLLINEAR_EDGES); + if (aFunction.IsNull()) return NULL; + + // Check if the function is set correctly + if (aFunction->GetDriverGUID() != GEOMImpl_HealingDriver::GetID()) return NULL; + + GEOMImpl_IHealing aCI (aFunction); + + Handle(GEOM_Function) aRefShape = theWire->GetLastFunction(); + if (aRefShape.IsNull()) return NULL; + aCI.SetOriginal(aRefShape); + + Handle(TColStd_HSequenceOfTransient) aVertices = new TColStd_HSequenceOfTransient; + std::list::iterator it = theVertices.begin(); + for (; it != theVertices.end(); it++) { + Handle(GEOM_Function) aRefSh = (*it)->GetLastFunction(); + if (aRefSh.IsNull()) { + SetErrorCode("NULL argument shape for the shape construction"); + return NULL; + } + aVertices->Append(aRefSh); + } + aCI.SetShapes(aVertices); + + // Compute the new wire + try { +#if OCC_VERSION_LARGE > 0x06010000 + OCC_CATCH_SIGNALS; +#endif + if (!GetSolver()->ComputeFunction(aFunction)) { + SetErrorCode("Healing driver failed"); + return NULL; + } + } + catch (Standard_Failure) { + Handle(Standard_Failure) aFail = Standard_Failure::Caught(); + SetErrorCode(aFail->GetMessageString()); + return NULL; + } + + // Make a Python command + GEOM::TPythonDump pd (aFunction); + pd << aRes << " = geompy.FuseCollinearEdgesWithinWire(" << theWire << ", ["; + // Vertices + it = theVertices.begin(); + if (it != theVertices.end()) { + pd << (*it++); + while (it != theVertices.end()) { + pd << ", " << (*it++); + } + } + pd << "])"; + + SetErrorCode(OK); + return aRes; +} + //============================================================================= /*! * GetFreeBoundary diff --git a/src/GEOMImpl/GEOMImpl_IHealingOperations.hxx b/src/GEOMImpl/GEOMImpl_IHealingOperations.hxx index e65976190..7434d937c 100644 --- a/src/GEOMImpl/GEOMImpl_IHealingOperations.hxx +++ b/src/GEOMImpl/GEOMImpl_IHealingOperations.hxx @@ -18,7 +18,6 @@ // 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 _GEOMImpl_IHealingOperations_HXX_ #define _GEOMImpl_IHealingOperations_HXX_ @@ -78,6 +77,10 @@ class GEOMImpl_IHealingOperations : public GEOM_IOperations { double theValue, bool isByParameter ); + Standard_EXPORT Handle(GEOM_Object) FuseCollinearEdgesWithinWire + (Handle(GEOM_Object) theWire, + std::list theVertices); + // this function does not use Function-Driver mechanism, it just computes the free // boundary edges and returns them in the sequence. It is called just for information reasons // and it's not intended for history/undo/redo/etc.. diff --git a/src/GEOMImpl/GEOMImpl_ILocalOperations.cxx b/src/GEOMImpl/GEOMImpl_ILocalOperations.cxx index 92b3c91a4..76b8f0563 100644 --- a/src/GEOMImpl/GEOMImpl_ILocalOperations.cxx +++ b/src/GEOMImpl/GEOMImpl_ILocalOperations.cxx @@ -18,7 +18,6 @@ // 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 @@ -492,7 +491,8 @@ Handle(GEOM_Object) GEOMImpl_ILocalOperations::MakeFillet2D */ //============================================================================= Handle(GEOM_Object) GEOMImpl_ILocalOperations::MakeFillet1D - (Handle(GEOM_Object) theShape, double theR, std::list theVertexes) + (Handle(GEOM_Object) theShape, double theR, + std::list theVertexes, bool doIgnoreSecantVertices) { SetErrorCode(KO); @@ -514,6 +514,7 @@ Handle(GEOM_Object) GEOMImpl_ILocalOperations::MakeFillet1D aCI.SetShape(aRefShape); aCI.SetR(theR); + aCI.SetFlag(doIgnoreSecantVertices); int aLen = theVertexes.size(); aCI.SetLength(aLen); diff --git a/src/GEOMImpl/GEOMImpl_ILocalOperations.hxx b/src/GEOMImpl/GEOMImpl_ILocalOperations.hxx index e1588da41..e3877b63b 100644 --- a/src/GEOMImpl/GEOMImpl_ILocalOperations.hxx +++ b/src/GEOMImpl/GEOMImpl_ILocalOperations.hxx @@ -18,7 +18,6 @@ // 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 _GEOMImpl_ILocalOperations_HXX_ #define _GEOMImpl_ILocalOperations_HXX_ @@ -50,7 +49,7 @@ class GEOMImpl_ILocalOperations : public GEOM_IOperations { Standard_EXPORT Handle(GEOM_Object) MakeFillet2D (Handle(GEOM_Object) theShape, double theR, std::list theVertexes); Standard_EXPORT Handle(GEOM_Object) MakeFillet1D (Handle(GEOM_Object) theShape, double theR, - std::list theVertexes); + std::list theVertexes, bool doIgnoreSecantVertices); Standard_EXPORT Handle(GEOM_Object) MakeChamferAll (Handle(GEOM_Object) theShape, double theD); Standard_EXPORT Handle(GEOM_Object) MakeChamferEdge (Handle(GEOM_Object) theShape, diff --git a/src/GEOMImpl/GEOMImpl_IShapesOperations.cxx b/src/GEOMImpl/GEOMImpl_IShapesOperations.cxx index b81b261ce..682311302 100644 --- a/src/GEOMImpl/GEOMImpl_IShapesOperations.cxx +++ b/src/GEOMImpl/GEOMImpl_IShapesOperations.cxx @@ -18,7 +18,6 @@ // 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 : GEOMImpl_IShapesOperations.cxx // Created : @@ -1010,10 +1009,10 @@ Handle(TColStd_HSequenceOfTransient) GEOMImpl_IShapesOperations::GetGlueShapes TopTools_DataMapIteratorOfDataMapOfShapeListOfShape aItDMSLS (aImages); for (int index = 1; aItDMSLS.More(); aItDMSLS.Next(), ++index) { // some key shape - //const TopoDS_Shape& aSkey = aItDMSLS.Key(); + //const TopoDS_Shape& aSkey = aItDMSLS.Key(); // list of shapes of the argument that can be glued - const TopTools_ListOfShape& aLSD = aItDMSLS.Value(); + const TopTools_ListOfShape& aLSD = aItDMSLS.Value(); //listShape.Append(aLSD.First()); TopoDS_Shape aValue = aLSD.First(); @@ -3920,8 +3919,8 @@ Handle(GEOM_Object) GEOMImpl_IShapesOperations::GetInPlace (Handle(GEOM_Object) return NULL; } - // the list of shapes aLSA contains the shapes - // of the Shape For Search that corresponds + // the list of shapes aLSA contains the shapes + // of the Shape For Search that corresponds // to the Argument aWhat const TopTools_ListOfShape& aLSA = aDMSLS.Find(aWhat); if (aLSA.Extent() == 0) { @@ -4920,7 +4919,8 @@ Handle(GEOM_Object) GEOMImpl_IShapesOperations::GetSame(const Handle(GEOM_Object //function : GetSameIDs //purpose : //======================================================================= -Handle(TColStd_HSequenceOfInteger) GEOMImpl_IShapesOperations::GetSameIDs(const Handle(GEOM_Object)& theShapeWhere, +Handle(TColStd_HSequenceOfInteger) GEOMImpl_IShapesOperations::GetSameIDs + (const Handle(GEOM_Object)& theShapeWhere, const Handle(GEOM_Object)& theShapeWhat) { SetErrorCode(KO); @@ -4931,8 +4931,6 @@ Handle(TColStd_HSequenceOfInteger) GEOMImpl_IShapesOperations::GetSameIDs(const if (aWhere.IsNull() || aWhat.IsNull()) return NULL; - int anIndex = -1; - bool isFound = false; TopTools_ListOfShape listShape; TopTools_MapOfShape aMap; @@ -5017,6 +5015,6 @@ Handle(TColStd_HSequenceOfInteger) GEOMImpl_IShapesOperations::GetSameIDs(const return aSeq; } else { SetErrorCode(NOT_FOUND_ANY); - return NULL; + return NULL; } } diff --git a/src/GEOMImpl/GEOMImpl_ShapeDriver.cxx b/src/GEOMImpl/GEOMImpl_ShapeDriver.cxx index c05db9fc1..e1ef28022 100644 --- a/src/GEOMImpl/GEOMImpl_ShapeDriver.cxx +++ b/src/GEOMImpl/GEOMImpl_ShapeDriver.cxx @@ -144,14 +144,14 @@ Standard_Integer GEOMImpl_ShapeDriver::Execute(TFunction_Logbook& log) const if (aShape_i.IsNull()) { Standard_NullObject::Raise("Shape for wire construction is null"); } - if (aShape_i.ShapeType() == TopAbs_EDGE || aShape_i.ShapeType() == TopAbs_WIRE) { - TopExp_Explorer exp (aShape_i, TopAbs_EDGE); - for (; exp.More(); exp.Next()) - B.Add(aWire, TopoDS::Edge(exp.Current())); - } else { - Standard_TypeMismatch::Raise - ("Shape for wire construction is neither an edge nor a wire"); - } + if (aShape_i.ShapeType() == TopAbs_EDGE || aShape_i.ShapeType() == TopAbs_WIRE) { + TopExp_Explorer exp (aShape_i, TopAbs_EDGE); + for (; exp.More(); exp.Next()) + B.Add(aWire, TopoDS::Edge(exp.Current())); + } else { + Standard_TypeMismatch::Raise + ("Shape for wire construction is neither an edge nor a wire"); + } } // fix edges order @@ -491,6 +491,108 @@ Standard_Integer GEOMImpl_ShapeDriver::Execute(TFunction_Logbook& log) const Standard_Real AngTol = aCI.GetAngularTolerance(); if (aWire.IsNull()) Standard_NullObject::Raise("Argument Wire is null"); + aShape = MakeEdgeFromWire(aWire, LinTol, AngTol); + } + else if (aType == EDGE_CURVE_LENGTH) { + GEOMImpl_IVector aVI (aFunction); + + // RefCurve + Handle(GEOM_Function) aRefCurve = aVI.GetPoint1(); + if (aRefCurve.IsNull()) Standard_NullObject::Raise("Argument Curve is null"); + TopoDS_Shape aRefShape1 = aRefCurve->GetValue(); + if (aRefShape1.ShapeType() != TopAbs_EDGE) { + Standard_TypeMismatch::Raise + ("Edge On Curve creation aborted : curve shape is not an edge"); + } + TopoDS_Edge aRefEdge = TopoDS::Edge(aRefShape1); + TopoDS_Vertex V1, V2; + TopExp::Vertices(aRefEdge, V1, V2, Standard_True); + + // RefPoint + TopoDS_Vertex aRefVertex; + Handle(GEOM_Function) aRefPoint = aVI.GetPoint2(); + if (aRefPoint.IsNull()) { + aRefVertex = V1; + } + else { + TopoDS_Shape aRefShape2 = aRefPoint->GetValue(); + if (aRefShape2.ShapeType() != TopAbs_VERTEX) { + Standard_TypeMismatch::Raise + ("Edge On Curve creation aborted : start point shape is not a vertex"); + } + aRefVertex = TopoDS::Vertex(aRefShape2); + } + gp_Pnt aRefPnt = BRep_Tool::Pnt(aRefVertex); + + // Length + Standard_Real aLength = aVI.GetParameter(); + //Standard_Real aCurveLength = IntTools::Length(aRefEdge); + //if (aLength > aCurveLength) { + // Standard_ConstructionError::Raise + // ("Edge On Curve creation aborted : given length is greater than edges length"); + //} + if (fabs(aLength) < Precision::Confusion()) { + Standard_ConstructionError::Raise + ("Edge On Curve creation aborted : given length is smaller than Precision::Confusion()"); + } + + // Check orientation + Standard_Real UFirst, ULast; + Handle(Geom_Curve) EdgeCurve = BRep_Tool::Curve(aRefEdge, UFirst, ULast); + Handle(Geom_Curve) ReOrientedCurve = EdgeCurve; + + Standard_Real dU = ULast - UFirst; + Standard_Real par1 = UFirst + 0.1 * dU; + Standard_Real par2 = ULast - 0.1 * dU; + + gp_Pnt P1 = EdgeCurve->Value(par1); + gp_Pnt P2 = EdgeCurve->Value(par2); + + if (aRefPnt.SquareDistance(P2) < aRefPnt.SquareDistance(P1)) { + ReOrientedCurve = EdgeCurve->Reversed(); + UFirst = EdgeCurve->ReversedParameter(ULast); + } + + // Get the point by length + GeomAdaptor_Curve AdapCurve = GeomAdaptor_Curve(ReOrientedCurve); + GCPnts_AbscissaPoint anAbsPnt (AdapCurve, aLength, UFirst); + Standard_Real aParam = anAbsPnt.Parameter(); + + if (AdapCurve.IsClosed() && aLength < 0.0) { + Standard_Real aTmp = aParam; + aParam = UFirst; + UFirst = aTmp; + } + + BRepBuilderAPI_MakeEdge aME (ReOrientedCurve, UFirst, aParam); + if (aME.IsDone()) + aShape = aME.Shape(); + } + else { + } + + if (aShape.IsNull()) return 0; + + // Check shape validity + BRepCheck_Analyzer ana (aShape, false); + if (!ana.IsValid()) { + //Standard_ConstructionError::Raise("Algorithm have produced an invalid shape result"); + } + + aFunction->SetValue(aShape); + + log.SetTouched(Label()); + + if (!aWarning.IsEmpty()) + Standard_Failure::Raise(aWarning.ToCString()); + + return 1; +} + +TopoDS_Edge GEOMImpl_ShapeDriver::MakeEdgeFromWire(const TopoDS_Shape& aWire, + const Standard_Real LinTol, + const Standard_Real AngTol) +{ TopoDS_Edge ResEdge; BRepLib::BuildCurves3d(aWire); @@ -834,105 +936,9 @@ Standard_Integer GEOMImpl_ShapeDriver::Execute(TFunction_Logbook& log) const if (FinalReverse) ResEdge.Reverse(); - aShape = ResEdge; - } - else if (aType == EDGE_CURVE_LENGTH) { - GEOMImpl_IVector aVI (aFunction); - - // RefCurve - Handle(GEOM_Function) aRefCurve = aVI.GetPoint1(); - if (aRefCurve.IsNull()) Standard_NullObject::Raise("Argument Curve is null"); - TopoDS_Shape aRefShape1 = aRefCurve->GetValue(); - if (aRefShape1.ShapeType() != TopAbs_EDGE) { - Standard_TypeMismatch::Raise - ("Edge On Curve creation aborted : curve shape is not an edge"); - } - TopoDS_Edge aRefEdge = TopoDS::Edge(aRefShape1); - TopoDS_Vertex V1, V2; - TopExp::Vertices(aRefEdge, V1, V2, Standard_True); - - // RefPoint - TopoDS_Vertex aRefVertex; - Handle(GEOM_Function) aRefPoint = aVI.GetPoint2(); - if (aRefPoint.IsNull()) { - aRefVertex = V1; - } - else { - TopoDS_Shape aRefShape2 = aRefPoint->GetValue(); - if (aRefShape2.ShapeType() != TopAbs_VERTEX) { - Standard_TypeMismatch::Raise - ("Edge On Curve creation aborted : start point shape is not a vertex"); - } - aRefVertex = TopoDS::Vertex(aRefShape2); - } - gp_Pnt aRefPnt = BRep_Tool::Pnt(aRefVertex); - - // Length - Standard_Real aLength = aVI.GetParameter(); - //Standard_Real aCurveLength = IntTools::Length(aRefEdge); - //if (aLength > aCurveLength) { - // Standard_ConstructionError::Raise - // ("Edge On Curve creation aborted : given length is greater than edges length"); - //} - if (fabs(aLength) < Precision::Confusion()) { - Standard_ConstructionError::Raise - ("Edge On Curve creation aborted : given length is smaller than Precision::Confusion()"); - } - - // Check orientation - Standard_Real UFirst, ULast; - Handle(Geom_Curve) EdgeCurve = BRep_Tool::Curve(aRefEdge, UFirst, ULast); - Handle(Geom_Curve) ReOrientedCurve = EdgeCurve; - - Standard_Real dU = ULast - UFirst; - Standard_Real par1 = UFirst + 0.1 * dU; - Standard_Real par2 = ULast - 0.1 * dU; - - gp_Pnt P1 = EdgeCurve->Value(par1); - gp_Pnt P2 = EdgeCurve->Value(par2); - - if (aRefPnt.SquareDistance(P2) < aRefPnt.SquareDistance(P1)) { - ReOrientedCurve = EdgeCurve->Reversed(); - UFirst = EdgeCurve->ReversedParameter(ULast); - } - - // Get the point by length - GeomAdaptor_Curve AdapCurve = GeomAdaptor_Curve(ReOrientedCurve); - GCPnts_AbscissaPoint anAbsPnt (AdapCurve, aLength, UFirst); - Standard_Real aParam = anAbsPnt.Parameter(); - - if (AdapCurve.IsClosed() && aLength < 0.0) { - Standard_Real aTmp = aParam; - aParam = UFirst; - UFirst = aTmp; - } - - BRepBuilderAPI_MakeEdge aME (ReOrientedCurve, UFirst, aParam); - if (aME.IsDone()) - aShape = aME.Shape(); - } - else { - } - - if (aShape.IsNull()) return 0; - - // Check shape validity - BRepCheck_Analyzer ana (aShape, false); - if (!ana.IsValid()) { - //Standard_ConstructionError::Raise("Algorithm have produced an invalid shape result"); - } - - aFunction->SetValue(aShape); - - log.SetTouched(Label()); - - if (!aWarning.IsEmpty()) - Standard_Failure::Raise(aWarning.ToCString()); - - return 1; + return ResEdge; } - //======================================================================= //function : GEOMImpl_ShapeDriver_Type_ //purpose : diff --git a/src/GEOMImpl/GEOMImpl_ShapeDriver.hxx b/src/GEOMImpl/GEOMImpl_ShapeDriver.hxx index 41c0fd6ae..653ad7e13 100644 --- a/src/GEOMImpl/GEOMImpl_ShapeDriver.hxx +++ b/src/GEOMImpl/GEOMImpl_ShapeDriver.hxx @@ -18,11 +18,10 @@ // 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 : GEOMImpl_ShapeDriver.ixx // Module : GEOMImpl -// + #ifndef _GEOMImpl_ShapeDriver_HeaderFile #define _GEOMImpl_ShapeDriver_HeaderFile @@ -119,6 +118,9 @@ class Handle(GEOMImpl_ShapeDriver) : public Handle(TFunction_Driver) { #include #endif +#include +#include + class TColStd_SequenceOfExtendedString; @@ -126,35 +128,38 @@ class GEOMImpl_ShapeDriver : public TFunction_Driver { public: - inline void* operator new(size_t,void* anAddress) - { - return anAddress; - } - inline void* operator new(size_t size) - { - return Standard::Allocate(size); - } - inline void operator delete(void *anAddress) - { - if (anAddress) Standard::Free((Standard_Address&)anAddress); - } - - // Methods PUBLIC - // -Standard_EXPORT GEOMImpl_ShapeDriver(); -Standard_EXPORT virtual Standard_Integer Execute(TFunction_Logbook& log) const; -Standard_EXPORT virtual void Validate(TFunction_Logbook&) const {} -Standard_EXPORT Standard_Boolean MustExecute(const TFunction_Logbook&) const { return Standard_True; } -Standard_EXPORT static const Standard_GUID& GetID(); -Standard_EXPORT ~GEOMImpl_ShapeDriver() {}; - - - // Type management - // -Standard_EXPORT friend Handle_Standard_Type& GEOMImpl_ShapeDriver_Type_(); -Standard_EXPORT const Handle(Standard_Type)& DynamicType() const { return STANDARD_TYPE(GEOMImpl_ShapeDriver) ; } -Standard_EXPORT Standard_Boolean IsKind(const Handle(Standard_Type)& AType) const { return (STANDARD_TYPE(GEOMImpl_ShapeDriver) == AType || TFunction_Driver::IsKind(AType)); } - + inline void* operator new(size_t,void* anAddress) + { + return anAddress; + } + inline void* operator new(size_t size) + { + return Standard::Allocate(size); + } + inline void operator delete(void *anAddress) + { + if (anAddress) Standard::Free((Standard_Address&)anAddress); + } + + // Methods PUBLIC + // + Standard_EXPORT GEOMImpl_ShapeDriver(); + Standard_EXPORT ~GEOMImpl_ShapeDriver() {}; + + Standard_EXPORT virtual Standard_Integer Execute(TFunction_Logbook& log) const; + Standard_EXPORT virtual void Validate(TFunction_Logbook&) const {} + Standard_EXPORT Standard_Boolean MustExecute(const TFunction_Logbook&) const { return Standard_True; } + Standard_EXPORT static const Standard_GUID& GetID(); + + Standard_EXPORT static TopoDS_Edge MakeEdgeFromWire(const TopoDS_Shape& aWire, + const Standard_Real LinTol, + const Standard_Real AngTol); + + // Type management + // + Standard_EXPORT friend Handle_Standard_Type& GEOMImpl_ShapeDriver_Type_(); + Standard_EXPORT const Handle(Standard_Type)& DynamicType() const { return STANDARD_TYPE(GEOMImpl_ShapeDriver) ; } + Standard_EXPORT Standard_Boolean IsKind(const Handle(Standard_Type)& AType) const { return (STANDARD_TYPE(GEOMImpl_ShapeDriver) == AType || TFunction_Driver::IsKind(AType)); } }; diff --git a/src/GEOMImpl/GEOMImpl_Types.hxx b/src/GEOMImpl/GEOMImpl_Types.hxx index b8a332987..2e5f871b8 100755 --- a/src/GEOMImpl/GEOMImpl_Types.hxx +++ b/src/GEOMImpl/GEOMImpl_Types.hxx @@ -285,6 +285,7 @@ #define DIVIDE_EDGE 7 #define CHANGE_ORIENTATION 8 #define LIMIT_TOLERANCE 9 +#define FUSE_COLLINEAR_EDGES 10 #define BASIC_FILLING 1 diff --git a/src/GEOM_I/GEOM_IHealingOperations_i.cc b/src/GEOM_I/GEOM_IHealingOperations_i.cc index 84f1e2324..9a064b3d0 100644 --- a/src/GEOM_I/GEOM_IHealingOperations_i.cc +++ b/src/GEOM_I/GEOM_IHealingOperations_i.cc @@ -18,7 +18,6 @@ // 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 @@ -396,6 +395,43 @@ GEOM::GEOM_Object_ptr GEOM_IHealingOperations_i::DivideEdge (GEOM::GEOM_Object_p return GetObject(aNewObject); } +//============================================================================= +/*! + * FuseCollinearEdgesWithinWire + */ +//============================================================================= +GEOM::GEOM_Object_ptr GEOM_IHealingOperations_i::FuseCollinearEdgesWithinWire + (GEOM::GEOM_Object_ptr theWire, + const GEOM::ListOfGO& theVertices) +{ + GEOM::GEOM_Object_var aGEOMObject; + + //Set a not done flag + GetOperations()->SetNotDone(); + + //Get the reference objects + Handle(GEOM_Object) aWire = GetObjectImpl(theWire); + if (aWire.IsNull()) return aGEOMObject._retn(); + + int ind, aLen; + std::list aVerts; + //Get the shapes + aLen = theVertices.length(); + for (ind = 0; ind < aLen; ind++) { + Handle(GEOM_Object) aSh = GetObjectImpl(theVertices[ind]); + if (aSh.IsNull()) return aGEOMObject._retn(); + aVerts.push_back(aSh); + } + + //Perform operation + Handle(GEOM_Object) anObject = + GetOperations()->FuseCollinearEdgesWithinWire(aWire, aVerts); + if (!GetOperations()->IsDone() || anObject.IsNull()) + return aGEOMObject._retn(); + + return GetObject(anObject); +} + //============================================================================= /*! * GetFreeBoundary diff --git a/src/GEOM_I/GEOM_IHealingOperations_i.hh b/src/GEOM_I/GEOM_IHealingOperations_i.hh index b7344e9cc..bdbaf8689 100644 --- a/src/GEOM_I/GEOM_IHealingOperations_i.hh +++ b/src/GEOM_I/GEOM_IHealingOperations_i.hh @@ -18,8 +18,6 @@ // 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 _GEOM_IHealingOperations_i_HeaderFile #define _GEOM_IHealingOperations_i_HeaderFile @@ -81,6 +79,9 @@ class GEOM_I_EXPORT GEOM_IHealingOperations_i : CORBA::Double theValue, CORBA::Boolean isByParameter); + GEOM::GEOM_Object_ptr FuseCollinearEdgesWithinWire (GEOM::GEOM_Object_ptr theWire, + const GEOM::ListOfGO& theVertices); + CORBA::Boolean GetFreeBoundary(GEOM::GEOM_Object_ptr theObject, GEOM::ListOfGO_out theClosedWires, GEOM::ListOfGO_out theOpenWires ); diff --git a/src/GEOM_I/GEOM_ILocalOperations_i.cc b/src/GEOM_I/GEOM_ILocalOperations_i.cc index ec764c2be..954778614 100644 --- a/src/GEOM_I/GEOM_ILocalOperations_i.cc +++ b/src/GEOM_I/GEOM_ILocalOperations_i.cc @@ -18,7 +18,6 @@ // 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 @@ -245,7 +244,8 @@ GEOM::GEOM_Object_ptr GEOM_ILocalOperations_i::MakeFillet2D //============================================================================= GEOM::GEOM_Object_ptr GEOM_ILocalOperations_i::MakeFillet1D (GEOM::GEOM_Object_ptr theShape, CORBA::Double theR, - const GEOM::ListOfLong& theVertexes) + const GEOM::ListOfLong& theVertexes, + CORBA::Boolean doIgnoreSecantVertices) { GEOM::GEOM_Object_var aGEOMObject; @@ -263,7 +263,7 @@ GEOM::GEOM_Object_ptr GEOM_ILocalOperations_i::MakeFillet1D //Create the Fillet Handle(GEOM_Object) anObject = - GetOperations()->MakeFillet1D(aShapeRef, theR, aVertexes); + GetOperations()->MakeFillet1D(aShapeRef, theR, aVertexes, doIgnoreSecantVertices); if (!GetOperations()->IsDone() || anObject.IsNull()) return aGEOMObject._retn(); diff --git a/src/GEOM_I/GEOM_ILocalOperations_i.hh b/src/GEOM_I/GEOM_ILocalOperations_i.hh index 432f56ffc..764f8e5cb 100644 --- a/src/GEOM_I/GEOM_ILocalOperations_i.hh +++ b/src/GEOM_I/GEOM_ILocalOperations_i.hh @@ -18,7 +18,6 @@ // 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 _GEOM_ILocalOperations_i_HeaderFile #define _GEOM_ILocalOperations_i_HeaderFile @@ -65,7 +64,8 @@ class GEOM_I_EXPORT GEOM_ILocalOperations_i : const GEOM::ListOfLong& theVertexes); GEOM::GEOM_Object_ptr MakeFillet1D (GEOM::GEOM_Object_ptr theShape, CORBA::Double theR, - const GEOM::ListOfLong& theVertexes); + const GEOM::ListOfLong& theVertexes, + CORBA::Boolean doIgnoreSecantVertices); GEOM::GEOM_Object_ptr MakeChamferAll (GEOM::GEOM_Object_ptr theShape, CORBA::Double theD); diff --git a/src/GEOM_I_Superv/GEOM_Superv_i.cc b/src/GEOM_I_Superv/GEOM_Superv_i.cc index 684558c07..b3c2cf038 100644 --- a/src/GEOM_I_Superv/GEOM_Superv_i.cc +++ b/src/GEOM_I_Superv/GEOM_Superv_i.cc @@ -18,7 +18,6 @@ // 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 "GEOM_Superv_i.hh" #include "SALOME_LifeCycleCORBA.hxx" @@ -3034,14 +3033,16 @@ GEOM::GEOM_Object_ptr GEOM_Superv_i::MakeFillet2D (GEOM::GEOM_Object_ptr theShap //============================================================================= GEOM::GEOM_Object_ptr GEOM_Superv_i::MakeFillet1D (GEOM::GEOM_Object_ptr theShape, CORBA::Double theR, - GEOM::GEOM_List_ptr theVertexes) + GEOM::GEOM_List_ptr theVertexes, + CORBA::Boolean doIgnoreSecantVertices) { beginService( " GEOM_Superv_i::MakeFillet1D" ); MESSAGE("GEOM_Superv_i::MakeFillet1D"); if (GEOM_List_i* aListImplV = dynamic_cast*>(GetServant(theVertexes, myPOA).in())) { getLocalOp(); - GEOM::GEOM_Object_ptr anObj = myLocalOp->MakeFillet1D(theShape, theR, aListImplV->GetList()); + GEOM::GEOM_Object_ptr anObj = myLocalOp->MakeFillet1D + (theShape, theR, aListImplV->GetList(), doIgnoreSecantVertices); endService( " GEOM_Superv_i::MakeFillet1D" ); return anObj; } diff --git a/src/GEOM_I_Superv/GEOM_Superv_i.hh b/src/GEOM_I_Superv/GEOM_Superv_i.hh index 3dd0192fe..7a9014af8 100644 --- a/src/GEOM_I_Superv/GEOM_Superv_i.hh +++ b/src/GEOM_I_Superv/GEOM_Superv_i.hh @@ -642,7 +642,7 @@ public: GEOM::GEOM_Object_ptr MakeFillet2D (GEOM::GEOM_Object_ptr theShape, CORBA::Double theR, GEOM::GEOM_List_ptr theVertexes); GEOM::GEOM_Object_ptr MakeFillet1D (GEOM::GEOM_Object_ptr theShape, CORBA::Double theR, - GEOM::GEOM_List_ptr theVertexes); + GEOM::GEOM_List_ptr theVertexes, CORBA::Boolean doIgnoreSecantVertices); GEOM::GEOM_Object_ptr MakeChamferAll (GEOM::GEOM_Object_ptr theShape, CORBA::Double theD); GEOM::GEOM_Object_ptr MakeChamferEdge (GEOM::GEOM_Object_ptr theShape, CORBA::Double theD1, CORBA::Double theD2, diff --git a/src/GEOM_SWIG/GEOM_TestHealing.py b/src/GEOM_SWIG/GEOM_TestHealing.py index 6fe6df6c0..abe4a9810 100644 --- a/src/GEOM_SWIG/GEOM_TestHealing.py +++ b/src/GEOM_SWIG/GEOM_TestHealing.py @@ -19,14 +19,12 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com -# -# GEOM GEOM_SWIG : binding of C++ implementaion with Python +# GEOM_SWIG : binding of C++ implementaion with Python # File : GEOM_TestHealing.py # Author : Julia DOROVSKIKH # Module : GEOM -# $Header$ -# + def TestProcessShape (geompy): ##Load shape from BREP file @@ -324,6 +322,51 @@ def TestDivideEdge (geompy): Id_Box = geompy.addToStudy(Box, "Box") Id_Divide = geompy.addToStudy(Divide, "Box with Divided Edge") +def TestFuseEdges (geompy): + + # create vertices + p1 = geompy.MakeVertex(0, 0, 0) + p2 = geompy.MakeVertex(70, 0, 0) + p3 = geompy.MakeVertex(70, 50, 0) + p4 = geompy.MakeVertex(70, 80, 0) + p5 = geompy.MakeVertex(50, 80, 0) + p6 = geompy.MakeVertex(20, 80, 0) + p7 = geompy.MakeVertex(0, 80, 0) + p8 = geompy.MakeVertex(0, 30, 0) + + points = [p1, p2, p3, p4, p5, p6, p7, p8] + + # make a wire + wire_1 = geompy.MakePolyline(points, True) + + # suppress some vertices in the wire + wire_2 = geompy.FuseCollinearEdgesWithinWire(wire_1, [p3]) + wire_3 = geompy.FuseCollinearEdgesWithinWire(wire_1, [p5, p6]) + + # suppress all suitable vertices in the wire + wire_4 = geompy.FuseCollinearEdgesWithinWire(wire_1, []) + + wires = [wire_1, wire_2, wire_3, wire_4] + + # add objects in the study + ii = 1 + for point in points: + geompy.addToStudy(point, "p%d"%ii) + ii = ii + 1 + pass + + ii = 1 + for wire in wires: + geompy.addToStudy(wire, "wire_%d"%ii) + wire_points = geompy.SubShapeAllSortedCentres(wire, geompy.ShapeType["VERTEX"]) + jj = 1 + for point in wire_points: + geompy.addToStudyInFather(wire, point, "point_%d"%jj) + jj = jj + 1 + pass + ii = ii + 1 + pass + def TestHealingOperations (geompy, math): TestMakeSewing(geompy, math) @@ -333,3 +376,4 @@ def TestHealingOperations (geompy, math): TestCloseContour(geompy) TestSuppressFaces(geompy) TestProcessShape(geompy) + TestFuseEdges(geompy) diff --git a/src/GEOM_SWIG/geompyDC.py b/src/GEOM_SWIG/geompyDC.py index f07490efb..46a7d224d 100644 --- a/src/GEOM_SWIG/geompyDC.py +++ b/src/GEOM_SWIG/geompyDC.py @@ -4642,6 +4642,29 @@ class geompyDC(GEOM._objref_GEOM_Gen): anObj.SetParameters(Parameters) return anObj + ## Suppress the vertices in the wire in case if adjacent edges are C1 continuous. + # @param theWire Wire to minimize the number of C1 continuous edges in. + # @param theVertices A list of vertices to suppress. If the list + # is empty, all vertices in a wire will be assumed. + # @return New GEOM.GEOM_Object with modified wire. + # + # @ref tui_fuse_collinear_edges "Example" + def FuseCollinearEdgesWithinWire(self, theWire, theVertices = []): + """ + Suppress the vertices in the wire in case if adjacent edges are C1 continuous. + + Parameters: + theWire Wire to minimize the number of C1 continuous edges in. + theVertices A list of vertices to suppress. If the list + is empty, all vertices in a wire will be assumed. + + Returns: + New GEOM.GEOM_Object with modified wire. + """ + anObj = self.HealOp.FuseCollinearEdgesWithinWire(theWire, theVertices) + RaiseIfFailed("FuseCollinearEdgesWithinWire", self.HealOp) + return anObj + ## Change orientation of the given object. Updates given shape. # @param theObject Shape to be processed. # @return Updated theObject @@ -6001,10 +6024,16 @@ class geompyDC(GEOM._objref_GEOM_Gen): # \note Global index of sub-shape can be obtained, using method GetSubShapeID() # \note The list of vertices could be empty, # in this case fillet will done done at all vertices in wire + # @param doIgnoreSecantVertices If FALSE, fillet radius is always limited + # by the length of the edges, nearest to the fillet vertex. + # But sometimes the next edge is C1 continuous with the one, nearest to + # the fillet point, and such two (or more) edges can be united to allow + # bigger radius. Set this flag to TRUE to allow collinear edges union, + # thus ignoring the secant vertex (vertices). # @return New GEOM.GEOM_Object, containing the result shape. # # @ref tui_fillet2d "Example" - def MakeFillet1D(self,theShape, theR, theListOfVertexes): + def MakeFillet1D(self,theShape, theR, theListOfVertexes, doIgnoreSecantVertices = True): """ Perform a fillet on the specified edges of the given shape @@ -6012,6 +6041,12 @@ class geompyDC(GEOM._objref_GEOM_Gen): theShape Wire Shape to perform fillet on. theR Fillet radius. theListOfVertexes Global indices of vertexes to perform fillet on. + doIgnoreSecantVertices If FALSE, fillet radius is always limited + by the length of the edges, nearest to the fillet vertex. + But sometimes the next edge is C1 continuous with the one, nearest to + the fillet point, and such two (or more) edges can be united to allow + bigger radius. Set this flag to TRUE to allow collinear edges union, + thus ignoring the secant vertex (vertices). Note: Global index of sub-shape can be obtained, using method geompy.GetSubShapeID @@ -6027,8 +6062,8 @@ class geompyDC(GEOM._objref_GEOM_Gen): Fillet_1D_1 = geompy.MakeFillet1D(Wire_1, 55, [3, 4, 6, 8, 10]) """ # Example: see GEOM_TestAll.py - theR,Parameters = ParseParameters(theR) - anObj = self.LocalOp.MakeFillet1D(theShape, theR, theListOfVertexes) + theR,doIgnoreSecantVertices,Parameters = ParseParameters(theR,doIgnoreSecantVertices) + anObj = self.LocalOp.MakeFillet1D(theShape, theR, theListOfVertexes, doIgnoreSecantVertices) RaiseIfFailed("MakeFillet1D", self.LocalOp) anObj.SetParameters(Parameters) return anObj diff --git a/src/OperationGUI/OperationGUI_Fillet1d2dDlg.cxx b/src/OperationGUI/OperationGUI_Fillet1d2dDlg.cxx index 16cc60845..ee5b86633 100644 --- a/src/OperationGUI/OperationGUI_Fillet1d2dDlg.cxx +++ b/src/OperationGUI/OperationGUI_Fillet1d2dDlg.cxx @@ -15,7 +15,6 @@ // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com -// // GEOM GEOMGUI : GUI for Geometry component // File : OperationGUI_Fillet1d2dDlg.cxx @@ -68,7 +67,7 @@ OperationGUI_Fillet1d2dDlg::OperationGUI_Fillet1d2dDlg (GeometryGUI* theGeometry mainFrame()->RadioButton2->close(); mainFrame()->RadioButton3->close(); - GroupVertexes = new DlgRef_2Sel1Spin(centralWidget()); + GroupVertexes = new DlgRef_2Sel1Spin2Check (centralWidget()); GroupVertexes->GroupBox1->setTitle(myIs1D ? tr("GEOM_FILLET_1D") : tr("GEOM_FILLET_2D")); GroupVertexes->TextLabel1->setText(myIs1D ? tr("GEOM_PLANAR_EDGE_WIRE") : tr("GEOM_PLANAR_FACE")); GroupVertexes->TextLabel2->setText(tr("GEOM_VERTEXES")); @@ -77,6 +76,13 @@ OperationGUI_Fillet1d2dDlg::OperationGUI_Fillet1d2dDlg (GeometryGUI* theGeometry GroupVertexes->PushButton2->setIcon(iconSelect); GroupVertexes->LineEdit1->setReadOnly(true); GroupVertexes->LineEdit2->setReadOnly(true); + if (myIs1D) { + GroupVertexes->CheckButton1->setText(tr("GEOM_FILLET_1D_IGNORE_SECANT")); + GroupVertexes->CheckButton1->setChecked(true); + } + else + GroupVertexes->CheckButton1->close(); + GroupVertexes->CheckButton2->close(); QVBoxLayout* layout = new QVBoxLayout(centralWidget()); layout->setMargin(0); layout->setSpacing(6); @@ -127,8 +133,6 @@ void OperationGUI_Fillet1d2dDlg::Init() connect(buttonOk(), SIGNAL(clicked()), this, SLOT(ClickOnOk() )); connect(buttonApply(), SIGNAL(clicked()), this, SLOT(ClickOnApply())); - // connect(this, SIGNAL(constructorsClicked(int)), this, SLOT(ConstructorsClicked(int))); - connect(GroupVertexes->PushButton1, SIGNAL(clicked()), this, SLOT(SetEditCurrentArgument())); connect(GroupVertexes->PushButton2, SIGNAL(clicked()), this, SLOT(SetEditCurrentArgument())); @@ -137,6 +141,8 @@ void OperationGUI_Fillet1d2dDlg::Init() connect(GroupVertexes->SpinBox_DX, SIGNAL(valueChanged(double)), this, SLOT(ValueChangedInSpinBox(double))); + connect(GroupVertexes->CheckButton1, SIGNAL(toggled(bool)), this, SLOT(processPreview())); + initName(myIs1D ? tr("GEOM_FILLET_1D") : tr("GEOM_FILLET_2D")); GroupVertexes->PushButton1->click(); @@ -401,7 +407,7 @@ bool OperationGUI_Fillet1d2dDlg::execute (ObjectList& objects) GEOM::GEOM_ILocalOperations_var anOper = GEOM::GEOM_ILocalOperations::_narrow(getOperation()); GEOM::GEOM_Object_var anObj = myIs1D ? - anOper->MakeFillet1D(myShape, getRadius(), aListOfIndexes) : + anOper->MakeFillet1D(myShape, getRadius(), aListOfIndexes, GroupVertexes->CheckButton1->isChecked()) : anOper->MakeFillet2D(myShape, getRadius(), aListOfIndexes); if (!anObj->_is_nil()) @@ -416,5 +422,5 @@ bool OperationGUI_Fillet1d2dDlg::execute (ObjectList& objects) //================================================================================= double OperationGUI_Fillet1d2dDlg::getRadius() const { - return GroupVertexes ->SpinBox_DX->value(); + return GroupVertexes->SpinBox_DX->value(); } diff --git a/src/OperationGUI/OperationGUI_Fillet1d2dDlg.h b/src/OperationGUI/OperationGUI_Fillet1d2dDlg.h index 85fdf27f7..04b4a7fce 100644 --- a/src/OperationGUI/OperationGUI_Fillet1d2dDlg.h +++ b/src/OperationGUI/OperationGUI_Fillet1d2dDlg.h @@ -15,12 +15,11 @@ // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com -// // GEOM GEOMGUI : GUI for Geometry component // File : OperationGUI_Fillet1d2dDlg.h // Author : DMV, OCN -// + #ifndef OPERATIONGUI_Fillet1d2dDLG_H #define OPERATIONGUI_Fillet1d2dDLG_H @@ -28,7 +27,7 @@ #include -class DlgRef_2Sel1Spin; +class DlgRef_2Sel1Spin2Check; //================================================================================= // class : OperationGUI_Fillet1d2dDlg @@ -68,7 +67,7 @@ private: GEOM::GEOM_Object_var myShape; TColStd_IndexedMapOfInteger myVertexes; - DlgRef_2Sel1Spin* GroupVertexes; + DlgRef_2Sel1Spin2Check* GroupVertexes; }; #endif // OPERATIONGUI_Fillet1d2dDLG_H diff --git a/src/RepairGUI/Makefile.am b/src/RepairGUI/Makefile.am index a4453ae5c..4c1bf3d8e 100644 --- a/src/RepairGUI/Makefile.am +++ b/src/RepairGUI/Makefile.am @@ -15,13 +15,11 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com -# -# GEOM REPAIRGUI : # File : Makefile.am # Author : Alexander BORODIN, Open CASCADE S.A.S. (alexander.borodin@opencascade.com) # Package : RepairGUI -# + include $(top_srcdir)/adm_local/unix/make_common_starter.am # header files @@ -39,7 +37,8 @@ salomeinclude_HEADERS = \ RepairGUI_ChangeOrientationDlg.h \ RepairGUI_GlueDlg.h \ RepairGUI_LimitToleranceDlg.h \ - RepairGUI_RemoveExtraEdgesDlg.h + RepairGUI_RemoveExtraEdgesDlg.h \ + RepairGUI_FuseEdgesDlg.h # Libraries targets lib_LTLIBRARIES = libRepairGUI.la @@ -59,6 +58,7 @@ dist_libRepairGUI_la_SOURCES = \ RepairGUI_GlueDlg.h \ RepairGUI_LimitToleranceDlg.h \ RepairGUI_RemoveExtraEdgesDlg.h \ + RepairGUI_FuseEdgesDlg.h \ \ RepairGUI.cxx \ RepairGUI_SewingDlg.cxx \ @@ -73,7 +73,8 @@ dist_libRepairGUI_la_SOURCES = \ RepairGUI_ChangeOrientationDlg.cxx \ RepairGUI_GlueDlg.cxx \ RepairGUI_LimitToleranceDlg.cxx \ - RepairGUI_RemoveExtraEdgesDlg.cxx + RepairGUI_RemoveExtraEdgesDlg.cxx \ + RepairGUI_FuseEdgesDlg.cxx MOC_FILES = \ RepairGUI_SewingDlg_moc.cxx \ @@ -88,7 +89,8 @@ MOC_FILES = \ RepairGUI_ChangeOrientationDlg_moc.cxx \ RepairGUI_GlueDlg_moc.cxx \ RepairGUI_LimitToleranceDlg_moc.cxx \ - RepairGUI_RemoveExtraEdgesDlg_moc.cxx + RepairGUI_RemoveExtraEdgesDlg_moc.cxx \ + RepairGUI_FuseEdgesDlg_moc.cxx nodist_libRepairGUI_la_SOURCES = \ $(MOC_FILES) diff --git a/src/RepairGUI/RepairGUI.cxx b/src/RepairGUI/RepairGUI.cxx index 3c72cbac3..5144986fa 100644 --- a/src/RepairGUI/RepairGUI.cxx +++ b/src/RepairGUI/RepairGUI.cxx @@ -44,6 +44,7 @@ #include "RepairGUI_LimitToleranceDlg.h" // Method LIMIT TOLERANCE #include "RepairGUI_ChangeOrientationDlg.h" // Method CHANGE ORIENTATION #include "RepairGUI_RemoveExtraEdgesDlg.h" // Method REMOVE EXTRA EDGES +#include "RepairGUI_FuseEdgesDlg.h" // Method FUSE COLLINEAR EDGES //======================================================================= // function : RepairGUI() @@ -90,9 +91,10 @@ bool RepairGUI::OnGUIEvent( int theCommandID, SUIT_Desktop* parent ) case GEOMOp::OpFreeFaces: aDlg = new RepairGUI_FreeFacesDlg (getGeometryGUI(), parent); break; case GEOMOp::OpOrientation: aDlg = new RepairGUI_ChangeOrientationDlg (getGeometryGUI(), parent); break; case GEOMOp::OpRemoveExtraEdges: aDlg = new RepairGUI_RemoveExtraEdgesDlg (getGeometryGUI(), parent); break; - default: - app->putInfo(tr("GEOM_PRP_COMMAND").arg(theCommandID)); - break; + case GEOMOp::OpFuseEdges: aDlg = new RepairGUI_FuseEdgesDlg (getGeometryGUI(), parent); break; + default: + app->putInfo(tr("GEOM_PRP_COMMAND").arg(theCommandID)); + break; } if (aDlg) diff --git a/src/RepairGUI/RepairGUI_FuseEdgesDlg.cxx b/src/RepairGUI/RepairGUI_FuseEdgesDlg.cxx new file mode 100644 index 000000000..4356b1896 --- /dev/null +++ b/src/RepairGUI/RepairGUI_FuseEdgesDlg.cxx @@ -0,0 +1,347 @@ +// Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// 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 "RepairGUI_FuseEdgesDlg.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +// OCCT Includes +#include +#include + +#include + +//================================================================================= +// class : RepairGUI_FuseEdgesDlg() +// purpose : Constructs a RepairGUI_FuseEdgesDlg which is a child of 'parent', with the +// name 'name' and widget flags set to 'f'. +// The dialog will by default be modeless, unless you set 'modal' to +// TRUE to construct a modal dialog. +//================================================================================= +RepairGUI_FuseEdgesDlg::RepairGUI_FuseEdgesDlg (GeometryGUI* theGeometryGUI, + QWidget* parent) + : GEOMBase_Skeleton(theGeometryGUI, parent, false) +{ + SUIT_ResourceMgr* aResMgr = myGeomGUI->getApp()->resourceMgr(); + QPixmap image0 (aResMgr->loadPixmap("GEOM", tr("ICON_DLG_FUSE_EDGES"))); + QPixmap iconSelect (aResMgr->loadPixmap("GEOM", tr("ICON_SELECT"))); + + setWindowTitle(tr("GEOM_FUSE_EDGES_TITLE")); + + mainFrame()->GroupConstructors->setTitle(tr("GEOM_FUSE_EDGES")); + mainFrame()->RadioButton1->setIcon(image0); + mainFrame()->RadioButton2->close(); + mainFrame()->RadioButton3->close(); + + GroupVertexes = new DlgRef_2Sel1Spin (centralWidget()); + GroupVertexes->GroupBox1->setTitle(tr("GEOM_FUSE_EDGES")); + GroupVertexes->TextLabel1->setText(tr("GEOM_WIRE")); + GroupVertexes->TextLabel2->setText(tr("GEOM_VERTEXES")); + GroupVertexes->PushButton1->setIcon(iconSelect); + GroupVertexes->PushButton2->setIcon(iconSelect); + GroupVertexes->LineEdit1->setReadOnly(true); + GroupVertexes->LineEdit2->setReadOnly(true); + + GroupVertexes->TextLabel3->setShown(false); + GroupVertexes->SpinBox_DX->setShown(false); + + QVBoxLayout* layout = new QVBoxLayout (centralWidget()); + layout->setMargin(0); + layout->setSpacing(6); + layout->addWidget(GroupVertexes); + + setHelpFileName("fuse_edges_operation_page.html"); + + // Initialisation + Init(); + resize(100,100); +} + +//================================================================================= +// function : ~RepairGUI_FuseEdgesDlg() +// purpose : Destroys the object and frees any allocated resources +//================================================================================= +RepairGUI_FuseEdgesDlg::~RepairGUI_FuseEdgesDlg() +{ +} + +//================================================================================= +// function : Init() +// purpose : +//================================================================================= +void RepairGUI_FuseEdgesDlg::Init() +{ + // Clear line edits + GroupVertexes->LineEdit1->setText(""); + GroupVertexes->LineEdit2->setText(""); + + myShape = GEOM::GEOM_Object::_nil(); + + myPoints.clear(); + + // signals and slots connections + connect(buttonOk(), SIGNAL(clicked()), this, SLOT(ClickOnOk())); + connect(buttonApply(), SIGNAL(clicked()), this, SLOT(ClickOnApply())); + + connect(GroupVertexes->PushButton1, SIGNAL(clicked()), this, SLOT(SetEditCurrentArgument())); + connect(GroupVertexes->PushButton2, SIGNAL(clicked()), this, SLOT(SetEditCurrentArgument())); + + connect(GroupVertexes->LineEdit1, SIGNAL(returnPressed()), this, SLOT(LineEditReturnPressed())); + connect(GroupVertexes->LineEdit2, SIGNAL(returnPressed()), this, SLOT(LineEditReturnPressed())); + + initName(tr("FUSE_EDGES_NEW_OBJ_NAME")); + GroupVertexes->PushButton1->click(); + + SelectionIntoArgument(); +} + +//================================================================================= +// function : ClickOnOk() +// purpose : +//================================================================================= +void RepairGUI_FuseEdgesDlg::ClickOnOk() +{ + setIsApplyAndClose(true); + if (ClickOnApply()) + ClickOnCancel(); +} + +//================================================================================= +// function : ClickOnApply() +// purpose : +//================================================================================= +bool RepairGUI_FuseEdgesDlg::ClickOnApply() +{ + if (!onAccept()) + return false; + + initName(); + // Reset dialog state + GroupVertexes->LineEdit1->setText(""); + GroupVertexes->LineEdit2->setText(""); + myShape = GEOM::GEOM_Object::_nil(); + myPoints.clear(); + GroupVertexes->PushButton1->click(); + + return true; +} + +//================================================================================= +// function : SelectionIntoArgument() +// purpose : Called when selection is changed or on dialog initialization or activation +//================================================================================= +void RepairGUI_FuseEdgesDlg::SelectionIntoArgument() +{ + myEditCurrentArgument->setText(""); + + LightApp_SelectionMgr* aSelMgr = myGeomGUI->getApp()->selectionMgr(); + SALOME_ListIO aSelList; + aSelMgr->selectedObjects(aSelList); + + // If selection of main object is activated + if (myEditCurrentArgument == GroupVertexes->LineEdit1) { + myShape = GEOM::GEOM_Object::_nil(); + if (aSelList.Extent() == 1) { + GEOM::GEOM_Object_var anObj = + GEOMBase::ConvertIOinGEOMObject(aSelList.First()); + + if (!anObj->_is_nil()) { + QString aName = GEOMBase::GetName(anObj); + TopoDS_Shape aShape; + if (GEOMBase::GetShape(anObj, aShape, TopAbs_SHAPE) && !aShape.IsNull()) { + TColStd_IndexedMapOfInteger aMap; + aSelMgr->GetIndexes(aSelList.First(), aMap); + if (aMap.Extent() == 1) { // Local Selection + int anIndex = aMap(1); + aName += QString(":wire_%1").arg(anIndex); + + //Find SubShape Object in Father + GEOM::GEOM_Object_var aFindedObject = GEOMBase_Helper::findObjectInFather(anObj, aName); + + if (aFindedObject->_is_nil()) { // Object not found in study + GEOM::GEOM_IShapesOperations_var aShapesOp = getGeomEngine()->GetIShapesOperations(getStudyId()); + anObj = aShapesOp->GetSubShape(anObj, anIndex); + } + else + anObj = aFindedObject; // get Object from study + } + else { // Global Selection + if (aShape.ShapeType() != TopAbs_WIRE) { + anObj = GEOM::GEOM_Object::_nil(); + aName = ""; + } + } + } + myShape = anObj; + myEditCurrentArgument->setText(aName); + } + } + + if (!myShape->_is_nil() && myPoints.isEmpty()) + GroupVertexes->PushButton2->click(); + } + else if (myEditCurrentArgument == GroupVertexes->LineEdit2) { + myPoints = getSelected(TopAbs_VERTEX, -1); + if (!myPoints.isEmpty()) + myEditCurrentArgument->setText(QString::number(myPoints.count()) + "_" + tr("GEOM_POINT") + tr("_S_")); + else + myEditCurrentArgument->setText(""); + } +} + +//================================================================================= +// function : SetEditCurrentArgument() +// purpose : +//================================================================================= +void RepairGUI_FuseEdgesDlg::SetEditCurrentArgument() +{ + QPushButton* send = (QPushButton*)sender(); + + if (send == GroupVertexes->PushButton1) { + myEditCurrentArgument = GroupVertexes->LineEdit1; + GroupVertexes->PushButton2->setDown(false); + GroupVertexes->LineEdit2->setEnabled(false); + } + else if (send == GroupVertexes->PushButton2) { + myEditCurrentArgument = GroupVertexes->LineEdit2; + GroupVertexes->PushButton1->setDown(false); + GroupVertexes->LineEdit1->setEnabled(false); + } + + // enable line edit + myEditCurrentArgument->setEnabled(true); + myEditCurrentArgument->setFocus(); + // after setFocus(), because it will be setDown(false) when loses focus + send->setDown(true); + + activateSelection(); +} + +//================================================================================= +// function : LineEditReturnPressed() +// purpose : +//================================================================================= +void RepairGUI_FuseEdgesDlg::LineEditReturnPressed() +{ + QLineEdit* send = (QLineEdit*)sender(); + + if (send == GroupVertexes->LineEdit1) + myEditCurrentArgument = GroupVertexes->LineEdit1; + else if (send == GroupVertexes->LineEdit2) + myEditCurrentArgument = GroupVertexes->LineEdit2; + else + return; + + GEOMBase_Skeleton::LineEditReturnPressed(); +} + +//================================================================================= +// function : ActivateThisDialog() +// purpose : +//================================================================================= +void RepairGUI_FuseEdgesDlg::ActivateThisDialog() +{ + GEOMBase_Skeleton::ActivateThisDialog(); +} + +//================================================================================= +// function : enterEvent() +// purpose : +//================================================================================= +void RepairGUI_FuseEdgesDlg::enterEvent (QEvent*) +{ + if (!mainFrame()->GroupConstructors->isEnabled()) + this->ActivateThisDialog(); +} + +//================================================================================= +// function : activateSelection +// purpose : Activate selection in accordance with myEditCurrentArgument +//================================================================================= +void RepairGUI_FuseEdgesDlg::activateSelection() +{ + disconnect(myGeomGUI->getApp()->selectionMgr(), 0, this, 0); + globalSelection(); + if (myEditCurrentArgument == GroupVertexes->LineEdit1) + globalSelection(GEOM_WIRE); + else if (!myShape->_is_nil() && myEditCurrentArgument == GroupVertexes->LineEdit2) + localSelection(myShape, TopAbs_VERTEX); + + connect(myGeomGUI->getApp()->selectionMgr(), SIGNAL(currentSelectionChanged()), + this, SLOT(SelectionIntoArgument())); +} + +//================================================================================= +// function : createOperation +// purpose : +//================================================================================= +GEOM::GEOM_IOperations_ptr RepairGUI_FuseEdgesDlg::createOperation() +{ + return getGeomEngine()->GetIHealingOperations(getStudyId()); +} + +//================================================================================= +// function : isValid() +// purpose : Verify validity of input data +//================================================================================= +bool RepairGUI_FuseEdgesDlg::isValid (QString& msg) +{ + return (!myShape->_is_nil()); +} + +//================================================================================= +// function : execute +// purpose : +//================================================================================= +bool RepairGUI_FuseEdgesDlg::execute (ObjectList& objects) +{ + GEOM::GEOM_IHealingOperations_var anOper = GEOM::GEOM_IHealingOperations::_narrow(getOperation()); + + GEOM::ListOfGO_var points = new GEOM::ListOfGO(); + points->length(myPoints.count()); + for (int i = 0; i < myPoints.count(); i++) + points[i] = myPoints[i].copy(); + + GEOM::GEOM_Object_var anObj = anOper->FuseCollinearEdgesWithinWire(myShape, points.in()); + + if (!anObj->_is_nil()) + objects.push_back(anObj._retn()); + + return true; +} + +//================================================================================= +// function : addSubshapeToStudy +// purpose : virtual method to add new SubObjects if local selection +//================================================================================= +void RepairGUI_FuseEdgesDlg::addSubshapesToStudy() +{ + for (int i = 0; i < myPoints.count(); i++) + GEOMBase::PublishSubObject(myPoints[i].get()); +} diff --git a/src/RepairGUI/RepairGUI_FuseEdgesDlg.h b/src/RepairGUI/RepairGUI_FuseEdgesDlg.h new file mode 100644 index 000000000..38d019aff --- /dev/null +++ b/src/RepairGUI/RepairGUI_FuseEdgesDlg.h @@ -0,0 +1,67 @@ +// Copyright (C) 2007-2012 CEA/DEN, EDF R&D, OPEN CASCADE +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// 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 REPAIRGUI_FuseEdgesDLG_H +#define REPAIRGUI_FuseEdgesDLG_H + +#include + +#include + +class DlgRef_2Sel1Spin; + +//================================================================================= +// class : RepairGUI_FuseEdgesDlg +// purpose : +//================================================================================= +class RepairGUI_FuseEdgesDlg : public GEOMBase_Skeleton +{ + Q_OBJECT + +public: + RepairGUI_FuseEdgesDlg (GeometryGUI*, QWidget*); + ~RepairGUI_FuseEdgesDlg(); + +protected: + // redefined from GEOMBase_Helper + virtual GEOM::GEOM_IOperations_ptr createOperation(); + virtual bool isValid (QString&); + virtual bool execute (ObjectList&); + virtual void addSubshapesToStudy(); + +private slots: + void ClickOnOk(); + bool ClickOnApply(); + void ActivateThisDialog(); + void LineEditReturnPressed(); + void SelectionIntoArgument(); + void SetEditCurrentArgument(); + +private: + void Init(); + void enterEvent (QEvent*); + void activateSelection(); + +private: + GEOM::GEOM_Object_var myShape; + QList myPoints; + + DlgRef_2Sel1Spin* GroupVertexes; +}; + +#endif // REPAIRGUI_FuseEdgesDLG_H -- 2.39.2