diff --git a/src/ShapeFix/ShapeFix_Face.cdl b/src/ShapeFix/ShapeFix_Face.cdl index a24de09a3c..c6151bea83 100755 --- a/src/ShapeFix/ShapeFix_Face.cdl +++ b/src/ShapeFix/ShapeFix_Face.cdl @@ -1,9 +1,7 @@ --- File: ShapeFix_Face.cdl --- Created: Wed Jun 3 12:33:25 1998 --- Author: data exchange team --- ----Copyright: Matra Datavision 1998 - +---File: ShapeFix_Face.cdl +---Created: Wed Jun 3 12:33:25 1998 +---Author: data exchange team +---Copyright: Open CASCADE SAS 2011 class Face from ShapeFix inherits Root from ShapeFix @@ -37,7 +35,7 @@ is ---Purpose: Creates a tool and loads a face ClearModes (me: mutable) is virtual; - ---Purpose: Sets all modes to default + ---Purpose: Sets all modes to default Init (me: mutable; face: Face from TopoDS); ---Purpose: Loads a whole face already created, with its wires, sense and @@ -54,147 +52,154 @@ is -- By default it will be FORWARD, or REVERSED if is False SetMsgRegistrator (me: mutable; msgreg: BasicMsgRegistrator from ShapeExtend) is redefined; - ---Purpose: Sets message registrator + ---Purpose: Sets message registrator SetPrecision (me: mutable; preci: Real) is redefined; - ---Purpose: Sets basic precision value (also to FixWireTool) + ---Purpose: Sets basic precision value (also to FixWireTool) SetMinTolerance (me: mutable; mintol: Real) is redefined; - ---Purpose: Sets minimal allowed tolerance (also to FixWireTool) + ---Purpose: Sets minimal allowed tolerance (also to FixWireTool) SetMaxTolerance (me: mutable; maxtol: Real) is redefined; - ---Purpose: Sets maximal allowed tolerance (also to FixWireTool) + ---Purpose: Sets maximal allowed tolerance (also to FixWireTool) FixWireMode (me: mutable) returns Integer; - ---C++: return & - ---C++: inline + ---C++: return & + ---C++: inline ---Purpose: Returns (modifiable) the mode for applying fixes of -- ShapeFix_Wire, by default True. FixOrientationMode (me: mutable) returns Integer; - ---C++: return & - ---C++: inline + ---C++: return & + ---C++: inline ---Purpose: Returns (modifiable) the fix orientation mode, by default -- True. If True, wires oriented to border limited square. FixAddNaturalBoundMode (me: mutable) returns Integer; - ---C++: return & - ---C++: inline + ---C++: return & + ---C++: inline ---Purpose: Returns (modifiable) the add natural bound mode. - -- If true, natural boundary is added on faces that miss them. + -- If true, natural boundary is added on faces that miss them. -- Default is False for faces with single wire (they are -- handled by FixOrientation in that case) and True for others. FixMissingSeamMode (me: mutable) returns Integer; - ---C++: return & - ---C++: inline + ---C++: return & + ---C++: inline ---Purpose: Returns (modifiable) the fix missing seam mode, by default -- True. If True, tries to insert seam is missed. FixSmallAreaWireMode (me: mutable) returns Integer; - ---C++: return & - ---C++: inline + ---C++: return & + ---C++: inline ---Purpose: Returns (modifiable) the fix small area wire mode, by default -- False. If True, drops small wires. FixIntersectingWiresMode (me: mutable) returns Integer; - ---C++: return & - ---C++: inline + ---C++: return & + ---C++: inline ---Purpose: Returns (modifiable) the fix intersecting wires mode -- by default True. FixLoopWiresMode (me: mutable) returns Integer; - ---C++: return & - ---C++: inline + ---C++: return & + ---C++: inline ---Purpose: Returns (modifiable) the fix loop wires mode -- by default True. FixSplitFaceMode (me: mutable) returns Integer; - ---C++: return & - ---C++: inline + ---C++: return & + ---C++: inline ---Purpose: Returns (modifiable) the fix split face mode -- by default True. AutoCorrectPrecisionMode (me: mutable) returns Integer; - ---C++: return & - ---C++: inline + ---C++: return & + ---C++: inline ---Purpose: Returns (modifiable) the auto-correct precision mode -- by default False. + + FixPeriodicDegeneratedMode (me: mutable) returns Integer; + ---C++: return & + ---C++: inline + ---Purpose: Returns (modifiable) the activation flag for periodic + -- degenerated fix. False by default. Face (me) returns Face from TopoDS; - ---C++: inline + ---C++: inline ---Purpose: Returns a face which corresponds to the current state -- Warning: The finally produced face may be another one ... but with the -- same support Result (me) returns Shape from TopoDS; - ---C++: inline - ---Purpose: Returns resulting shape (Face or Shell if splitted) - -- To be used instead of Face() if FixMissingSeam involved + ---C++: inline + ---Purpose: Returns resulting shape (Face or Shell if splitted) + -- To be used instead of Face() if FixMissingSeam involved Add (me: mutable; wire: Wire from TopoDS); ---Purpose: Add a wire to current face using BRep_Builder. - -- Wire is added without taking into account orientation of face - -- (as if face were FORWARD). + -- Wire is added without taking into account orientation of face + -- (as if face were FORWARD). ---Warning: Method is retained for compatibility with previous versions. Perform (me: mutable) returns Boolean; ---Purpose: Performs all the fixes, depending on modes - -- Function Status returns the status of last call to Perform() - -- ShapeExtend_OK : face was OK, nothing done - -- ShapeExtend_DONE1: some wires are fixed - -- ShapeExtend_DONE2: orientation of wires fixed - -- ShapeExtend_DONE3: missing seam added - -- ShapeExtend_DONE4: small area wire removed - -- ShapeExtend_DONE5: natural bounds added - -- ShapeExtend_FAIL1: some fails during fixing wires - -- ShapeExtend_FAIL2: cannot fix orientation of wires - -- ShapeExtend_FAIL3: cannot add missing seam - -- ShapeExtend_FAIL4: cannot remove small area wire + -- Function Status returns the status of last call to Perform() + -- ShapeExtend_OK : face was OK, nothing done + -- ShapeExtend_DONE1: some wires are fixed + -- ShapeExtend_DONE2: orientation of wires fixed + -- ShapeExtend_DONE3: missing seam added + -- ShapeExtend_DONE4: small area wire removed + -- ShapeExtend_DONE5: natural bounds added + -- ShapeExtend_FAIL1: some fails during fixing wires + -- ShapeExtend_FAIL2: cannot fix orientation of wires + -- ShapeExtend_FAIL3: cannot add missing seam + -- ShapeExtend_FAIL4: cannot remove small area wire FixOrientation (me: mutable) returns Boolean; ---Purpose: Fixes orientation of wires on the face - -- It tries to make all wires lie outside all others (according - -- to orientation) by reversing orientation of some of them. - -- If face lying on sphere or torus has single wire and + -- It tries to make all wires lie outside all others (according + -- to orientation) by reversing orientation of some of them. + -- If face lying on sphere or torus has single wire and -- AddNaturalBoundMode is True, that wire is not reversed in -- any case (supposing that natural bound will be added). - -- Returns True if wires were reversed + -- Returns True if wires were reversed FixOrientation (me: mutable; MapWires : in out DataMapOfShapeListOfShape from TopTools) returns Boolean; ---Purpose: Fixes orientation of wires on the face - -- It tries to make all wires lie outside all others (according - -- to orientation) by reversing orientation of some of them. - -- If face lying on sphere or torus has single wire and + -- It tries to make all wires lie outside all others (according + -- to orientation) by reversing orientation of some of them. + -- If face lying on sphere or torus has single wire and -- AddNaturalBoundMode is True, that wire is not reversed in -- any case (supposing that natural bound will be added). - -- Returns True if wires were reversed - -- OutWires return information about out wires + list of - -- internal wires for each (for performing split face). + -- Returns True if wires were reversed + -- OutWires return information about out wires + list of + -- internal wires for each (for performing split face). FixAddNaturalBound(me: mutable) returns Boolean; ---Purpose: Adds natural boundary on face if it is missing. - -- Two cases are supported: - -- - face has no wires - -- - face lies on geometrically double-closed surface + -- Two cases are supported: + -- - face has no wires + -- - face lies on geometrically double-closed surface -- (sphere or torus) and none of wires is left-oriented - -- Returns True if natural boundary was added + -- Returns True if natural boundary was added FixMissingSeam (me: mutable) returns Boolean; ---Purpose: Detects and fixes the special case when face on a closed -- surface is given by two wires closed in 3d but with gap in 2d. -- In that case it creates a new wire from the two, and adds a -- missing seam edge - -- Returns True if missing seam was added + -- Returns True if missing seam was added FixSmallAreaWire (me: mutable) returns Boolean; - ---Purpose: Detects wires with small area (that is less than - -- 100*Precision::PConfusion(). Removes these wires if they are internal. - -- Returns : True if at least one small wire removed, - -- False if does nothing. + ---Purpose: Detects wires with small area (that is less than + -- 100*Precision::PConfusion(). Removes these wires if they are internal. + -- Returns : True if at least one small wire removed, + -- False if does nothing. + FixLoopWire(me: mutable; aResWires : out SequenceOfShape from TopTools) returns Boolean; - ---Purpose: Detects if wire has a loop and fixes this situation by splitting on the few parts. - -- if wire has a loops and it was splitted Status was set to value ShapeExtend_DONE6. + ---Purpose: Detects if wire has a loop and fixes this situation by splitting on the few parts. + -- if wire has a loops and it was splitted Status was set to value ShapeExtend_DONE6. SplitEdge (me: mutable; sewd: WireData from ShapeExtend; num: Integer from Standard; param: Real from Standard; vert: Vertex from TopoDS; @@ -209,35 +214,43 @@ is FixIntersectingWires (me: mutable) returns Boolean; ---Purpose: Detects and fixes the special case when face has more than one wire - -- and this wires have intersection point + -- and this wires have intersection point FixWiresTwoCoincEdges (me: mutable) returns Boolean; ---Purpose: If wire contains two coincidence edges it must be removed - -- Queries on status after Perform() + -- Queries on status after Perform() FixSplitFace (me: mutable; MapWires : DataMapOfShapeListOfShape from TopTools) returns Boolean; ---Purpose: Split face if there are more than one out wire - -- using inrormation after FixOrientation() + -- using inrormation after FixOrientation() + + FixPeriodicDegenerated (me: mutable) + returns Boolean; + ---Purpose: Fixes topology for a specific case when face is composed + -- by a single wire belting a periodic surface. In that case + -- a degenerated edge is reconstructed in the degenerated pole + -- of the surface. Initial wire gets consistent orientation. + -- Must be used in couple and before FixMissingSeam routine Status (me; status: Status from ShapeExtend) returns Boolean; - ---Purpose: Returns the status of last call to Perform() - -- ShapeExtend_OK : face was OK, nothing done - -- ShapeExtend_DONE1: some wires are fixed - -- ShapeExtend_DONE2: orientation of wires fixed - -- ShapeExtend_DONE3: missing seam added - -- ShapeExtend_DONE4: small area wire removed - -- ShapeExtend_DONE5: natural bounds added - -- ShapeExtend_DONE8: face may be splited - -- ShapeExtend_FAIL1: some fails during fixing wires - -- ShapeExtend_FAIL2: cannot fix orientation of wires - -- ShapeExtend_FAIL3: cannot add missing seam - -- ShapeExtend_FAIL4: cannot remove small area wire - ---C++: inline + ---Purpose: Returns the status of last call to Perform() + -- ShapeExtend_OK : face was OK, nothing done + -- ShapeExtend_DONE1: some wires are fixed + -- ShapeExtend_DONE2: orientation of wires fixed + -- ShapeExtend_DONE3: missing seam added + -- ShapeExtend_DONE4: small area wire removed + -- ShapeExtend_DONE5: natural bounds added + -- ShapeExtend_DONE8: face may be splited + -- ShapeExtend_FAIL1: some fails during fixing wires + -- ShapeExtend_FAIL2: cannot fix orientation of wires + -- ShapeExtend_FAIL3: cannot add missing seam + -- ShapeExtend_FAIL4: cannot remove small area wire + ---C++: inline FixWireTool (me: mutable) returns Wire from ShapeFix; - ---Purpose: Returns tool for fixing wires. - ---C++: inline + ---Purpose: Returns tool for fixing wires. + ---C++: inline fields @@ -247,15 +260,17 @@ fields myFixWire: Wire from ShapeFix is protected; myFwd : Boolean is protected; - myFixWireMode : Integer; - myFixOrientationMode : Integer; - myFixAddNaturalBoundMode: Integer; - myFixMissingSeamMode : Integer; - myFixSmallAreaWireMode : Integer; - myFixLoopWiresMode : Integer; --gka 08.01.2004 - myFixIntersectingWiresMode : Integer; -- skl 23.12.2003 - myFixSplitFaceMode : Integer; -- skl 03.02.2004 - myAutoCorrectPrecisionMode: Integer; --skl 26.03.2010 + myFixWireMode : Integer; + myFixOrientationMode : Integer; + myFixAddNaturalBoundMode : Integer; + myFixMissingSeamMode : Integer; + myFixSmallAreaWireMode : Integer; + myFixLoopWiresMode : Integer; -- gka 08.01.2004 + myFixIntersectingWiresMode : Integer; -- skl 23.12.2003 + myFixSplitFaceMode : Integer; -- skl 03.02.2004 + myAutoCorrectPrecisionMode : Integer; -- skl 26.03.2010 + myFixPeriodicDegenerated : Integer; -- ssv 29.09.2011 + myStatus : Integer is protected; end Face; diff --git a/src/ShapeFix/ShapeFix_Face.cxx b/src/ShapeFix/ShapeFix_Face.cxx index 2c6255bbc8..51eccf3b30 100755 --- a/src/ShapeFix/ShapeFix_Face.cxx +++ b/src/ShapeFix/ShapeFix_Face.cxx @@ -41,7 +41,10 @@ #include #include #include +#include #include +#include +#include #include #include @@ -51,8 +54,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -124,15 +129,16 @@ ShapeFix_Face::ShapeFix_Face(const TopoDS_Face &face) void ShapeFix_Face::ClearModes() { - myFixWireMode = -1; - myFixOrientationMode = -1; - myFixAddNaturalBoundMode = -1; - myFixMissingSeamMode = -1; - myFixSmallAreaWireMode = -1; + myFixWireMode = -1; + myFixOrientationMode = -1; + myFixAddNaturalBoundMode = -1; + myFixMissingSeamMode = -1; + myFixSmallAreaWireMode = -1; myFixIntersectingWiresMode = -1; - myFixLoopWiresMode =-1; - myFixSplitFaceMode =-1; - myAutoCorrectPrecisionMode = 1; + myFixLoopWiresMode = -1; + myFixSplitFaceMode = -1; + myAutoCorrectPrecisionMode = 1; + myFixPeriodicDegenerated = -1; } //======================================================================= @@ -445,6 +451,11 @@ Standard_Boolean ShapeFix_Face::Perform() myResult = myFace; TopoDS_Shape savShape = myFace; //gka BUG 6555 + + // Specific case for conic surfaces + if ( NeedFix(myFixPeriodicDegenerated) ) + this->FixPeriodicDegenerated(); + // fix missing seam if ( NeedFix ( myFixMissingSeamMode ) ) { if ( FixMissingSeam() ) { @@ -2241,3 +2252,229 @@ Standard_Boolean ShapeFix_Face::FixSplitFace(const TopTools_DataMapOfShapeListOf return Standard_False; } + +//======================================================================= +//function : IsPeriodicConicalLoop +//purpose : Checks whether the passed wire makes up a periodic loop on +// passed conical surface +//======================================================================= + +static Standard_Boolean IsPeriodicConicalLoop(const Handle(Geom_ConicalSurface)& theSurf, + const TopoDS_Wire& theWire, + const Standard_Real theTolerance, + Standard_Real& theMinU, + Standard_Real& theMaxU, + Standard_Real& theMinV, + Standard_Real& theMaxV, + Standard_Boolean& isUDecrease) +{ + if ( theSurf.IsNull() ) + Standard_False; + + ShapeAnalysis_Edge aSAE; + TopLoc_Location aLoc; + + Standard_Real aCumulDeltaU = 0.0, aCumulDeltaUAbs = 0.0; + Standard_Real aMinU = RealLast(); + Standard_Real aMinV = aMinU; + Standard_Real aMaxU = -aMinU; + Standard_Real aMaxV = aMaxU; + + // Iterate over the edges to check whether the wire is periodic on conical surface + BRepTools_WireExplorer aWireExp(theWire); + for ( ; aWireExp.More(); aWireExp.Next() ) + { + const TopoDS_Edge& aCurrentEdge = aWireExp.Current(); + Handle(Geom2d_Curve) aC2d; + Standard_Real aPFirst, aPLast; + + aSAE.PCurve(aCurrentEdge, theSurf, aLoc, aC2d, aPFirst, aPLast, Standard_True); + + if ( aC2d.IsNull() ) + return Standard_False; + + gp_Pnt2d aUVFirst = aC2d->Value(aPFirst), + aUVLast = aC2d->Value(aPLast); + + Standard_Real aUFirst = aUVFirst.X(), aULast = aUVLast.X(); + Standard_Real aVFirst = aUVFirst.Y(), aVLast = aUVLast.Y(); + + Standard_Real aCurMaxU = Max(aUFirst, aULast), + aCurMinU = Min(aUFirst, aULast); + Standard_Real aCurMaxV = Max(aVFirst, aVLast), + aCurMinV = Min(aVFirst, aVLast); + + if ( aCurMinU < aMinU ) + aMinU = aCurMinU; + if ( aCurMaxU > aMaxU ) + aMaxU = aCurMaxU; + if ( aCurMinV < aMinV ) + aMinV = aCurMinV; + if ( aCurMaxV > aMaxV ) + aMaxV = aCurMaxV; + + Standard_Real aDeltaU = aULast - aUFirst; + + aCumulDeltaU += aDeltaU; + aCumulDeltaUAbs += Abs(aDeltaU); + } + + theMinU = aMinU; + theMaxU = aMaxU; + theMinV = aMinV; + theMaxV = aMaxV; + isUDecrease = (aCumulDeltaU < 0 ? Standard_True : Standard_False); + + Standard_Boolean is2PIDelta = Abs(aCumulDeltaUAbs - 2*M_PI) <= theTolerance; + Standard_Boolean isAroundApex = Abs(theMaxU - theMinU) > 2*M_PI - theTolerance; + + return is2PIDelta && isAroundApex; +} + +//======================================================================= +//function : FixPeriodicDegenerated +//purpose : +//======================================================================= + +Standard_Boolean ShapeFix_Face::FixPeriodicDegenerated() +{ + /* ===================== + * Prepare fix routine + * ===================== */ + + if ( !Context().IsNull() ) + { + TopoDS_Shape aSh = Context()->Apply(myFace); + myFace = TopoDS::Face(aSh); + } + + /* ================================================ + * Check if fix can be applied on the passed face + * ================================================ */ + + // Collect all wires owned by the face + TopTools_SequenceOfShape aWireSeq; + for ( TopoDS_Iterator aWireIt(myFace, Standard_False); aWireIt.More(); aWireIt.Next() ) + { + const TopoDS_Shape& aSubSh = aWireIt.Value(); + if ( aSubSh.ShapeType() != TopAbs_WIRE || ( aSubSh.Orientation() != TopAbs_FORWARD && + aSubSh.Orientation() != TopAbs_REVERSED ) ) + continue; + + aWireSeq.Append( aWireIt.Value() ); + } + + // Get number of wires and surface + Standard_Integer aNbWires = aWireSeq.Length(); + Handle(Geom_Surface) aSurface = BRep_Tool::Surface(myFace); + + // Only single wires on conical surfaces are checked + if ( aNbWires != 1 || aSurface.IsNull() || + aSurface->DynamicType() != STANDARD_TYPE(Geom_ConicalSurface) ) + return Standard_False; + + // Get the single wire + TopoDS_Wire aSoleWire = TopoDS::Wire( aWireSeq.Value(1) ); + + // Check whether this wire is belting the conical surface by period + Handle(Geom_ConicalSurface) aConeSurf = Handle(Geom_ConicalSurface)::DownCast(aSurface); + Standard_Real aMinLoopU = 0.0, aMaxLoopU = 0.0, aMinLoopV = 0.0, aMaxLoopV = 0.0; + Standard_Boolean isUDecrease = Standard_False; + + Standard_Boolean isConicLoop = IsPeriodicConicalLoop(aConeSurf, aSoleWire, Precision(), + aMinLoopU, aMaxLoopU, + aMinLoopV, aMaxLoopV, + isUDecrease); + + if ( !isConicLoop ) + return Standard_False; + + /* =============== + * Retrieve apex + * =============== */ + + // Get base circle of the conical surface (the circle it was built from) + Handle(Geom_Curve) aConeBaseCrv = aConeSurf->VIso(0.0); + Handle(Geom_Circle) aConeBaseCirc = Handle(Geom_Circle)::DownCast(aConeBaseCrv); + + // Retrieve conical props + Standard_Real aConeBaseR = aConeBaseCirc->Radius(); + Standard_Real aSemiAngle = aConeSurf->SemiAngle(); + + if ( fabs(aSemiAngle) <= Precision::Confusion() ) + return Standard_False; // Bad surface + + // Find the V parameter of the apex + Standard_Real aConeBaseH = aConeBaseR / Sin(aSemiAngle); + Standard_Real anApexV = -aConeBaseH; + + // Get apex vertex + TopoDS_Vertex anApex = BRepBuilderAPI_MakeVertex( aConeSurf->Apex() ); + + // ==================================== + // Build degenerated edge in the apex + // ==================================== + + TopoDS_Edge anApexEdge; + BRep_Builder aBuilder; + aBuilder.MakeEdge(anApexEdge); + + // Check if positional relationship between the initial wire and apex + // line in 2D is going to be consistent + if ( fabs(anApexV - aMinLoopV) <= Precision() || + fabs(anApexV - aMaxLoopV) <= Precision() || + ( anApexV < aMaxLoopV && anApexV > aMinLoopV ) ) + return Standard_False; + + Handle(Geom2d_Line) anApexCurve2d; + + // Apex curve below the wire + if ( anApexV < aMinLoopV ) + { + anApexCurve2d = new Geom2d_Line( gp_Pnt2d(aMinLoopU, anApexV), gp_Dir2d(1, 0) ); + if ( !isUDecrease ) + aSoleWire.Reverse(); + } + + // Apex curve above the wire + if ( anApexV > aMaxLoopV ) + { + anApexCurve2d = new Geom2d_Line( gp_Pnt2d(aMaxLoopU, anApexV), gp_Dir2d(-1, 0) ); + if ( isUDecrease ) + aSoleWire.Reverse(); + } + + // Create degenerated edge & wire for apex + aBuilder.UpdateEdge( anApexEdge, anApexCurve2d, myFace, Precision() ); + aBuilder.Add( anApexEdge, anApex ); + aBuilder.Add( anApexEdge, anApex.Reversed() ); + aBuilder.Degenerated(anApexEdge, Standard_True); + aBuilder.Range( anApexEdge, 0, fabs(aMaxLoopU - aMinLoopU) ); + TopoDS_Wire anApexWire = BRepBuilderAPI_MakeWire(anApexEdge); + + // =============================================================== + // Finalize the fix building new face and setting up the results + // =============================================================== + + // Collect the resulting set of wires + TopTools_SequenceOfShape aNewWireSeq; + aNewWireSeq.Append(aSoleWire); + aNewWireSeq.Append(anApexWire); + + // Assemble new face + TopoDS_Face aNewFace = TopoDS::Face( myFace.EmptyCopied() ); + aNewFace.Orientation(TopAbs_FORWARD); + BRep_Builder aFaceBuilder; + for ( Standard_Integer i = 1; i <= aNewWireSeq.Length(); i++ ) + { + TopoDS_Wire aNewWire = TopoDS::Wire( aNewWireSeq.Value(i) ); + aFaceBuilder.Add(aNewFace, aNewWire); + } + aNewFace.Orientation( myFace.Orientation() ); + + // Adjust the resulting state of the healing tool + myResult = aNewFace; + Context()->Replace(myFace, myResult); + + return Standard_True; +}