mirror of
https://git.dev.opencascade.org/repos/occt.git
synced 2025-08-14 13:30:48 +03:00
0028968: Incorrect offset for the faces with singularities
Simple offset algorithm (BRepOffset_MakeSimpleOffset) is improved to handle the case when bspline surface has imprecise singularity at one of sides (when side is degenerated but not exactly to one point). In such case, the algorithm tries to collapse all poles of singular side of the surface to the same point; this allows avoiding flapping of normal due to small fluctuations of surface. If face being offset contains degenerated edges, then check for singularity is done using position and tolerance of corresponding vertices. In addition, each side is checked with some user-defined tolerance (by default Precision::Confusion()); this helps to process cases when no edge is located at that side or if such edge is not encoded as degenerated. New parameter Tolerance is introduced for that in BRepOffset_MakeSimpleOffset class. Tests added: bugs modelg_7 bug28968 - on isolated faces as reported in the issue, mostly for visual check (absence of loops) offset simple F01-05 - on original shells, checking tolerances of resulting shell
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
|
||||
|
||||
#include <BRepOffset.hxx>
|
||||
#include <BRep_Tool.hxx>
|
||||
#include <Geom_BSplineSurface.hxx>
|
||||
#include <Geom_ConicalSurface.hxx>
|
||||
#include <Geom_CylindricalSurface.hxx>
|
||||
@@ -31,15 +32,24 @@
|
||||
#include <gp_Ax3.hxx>
|
||||
#include <gp_Dir.hxx>
|
||||
#include <gp_Vec.hxx>
|
||||
#include <NCollection_LocalArray.hxx>
|
||||
#include <Precision.hxx>
|
||||
#include <TopExp.hxx>
|
||||
#include <TopExp_Explorer.hxx>
|
||||
#include <TopoDS.hxx>
|
||||
#include <TopoDS_Edge.hxx>
|
||||
#include <TopoDS_Face.hxx>
|
||||
#include <TopoDS_ListOfShape.hxx>
|
||||
#include <TopoDS_Vertex.hxx>
|
||||
|
||||
//=======================================================================
|
||||
//function : Surface
|
||||
//purpose :
|
||||
//=======================================================================
|
||||
Handle(Geom_Surface) BRepOffset::Surface(const Handle(Geom_Surface)& Surface,
|
||||
const Standard_Real Offset,
|
||||
BRepOffset_Status& theStatus)
|
||||
const Standard_Real Offset,
|
||||
BRepOffset_Status& theStatus,
|
||||
Standard_Boolean allowC0)
|
||||
{
|
||||
Standard_Real Tol = Precision::Confusion();
|
||||
|
||||
@@ -152,17 +162,158 @@ Handle(Geom_Surface) BRepOffset::Surface(const Handle(Geom_Surface)& Surface,
|
||||
Handle(Geom_RectangularTrimmedSurface)::DownCast(Surface);
|
||||
Standard_Real U1,U2,V1,V2;
|
||||
S->Bounds(U1,U2,V1,V2);
|
||||
Handle(Geom_Surface) Off = BRepOffset::Surface (S->BasisSurface(), Offset, theStatus);
|
||||
Handle(Geom_Surface) Off = BRepOffset::Surface (S->BasisSurface(), Offset, theStatus, allowC0);
|
||||
Result = new Geom_RectangularTrimmedSurface (Off,U1,U2,V1,V2);
|
||||
}
|
||||
else if (TheType == STANDARD_TYPE(Geom_OffsetSurface)) {
|
||||
}
|
||||
|
||||
if ( Result.IsNull()) {
|
||||
Result = new Geom_OffsetSurface( Surface, Offset);
|
||||
Result = new Geom_OffsetSurface( Surface, Offset, allowC0);
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
//function : CollapseSingularities
|
||||
//purpose :
|
||||
//=======================================================================
|
||||
Handle(Geom_Surface) BRepOffset::CollapseSingularities (const Handle(Geom_Surface)& theSurface,
|
||||
const TopoDS_Face& theFace,
|
||||
Standard_Real thePrecision)
|
||||
{
|
||||
// check surface type to see if it can be processed
|
||||
Handle(Standard_Type) aType = theSurface->DynamicType();
|
||||
if (aType != STANDARD_TYPE(Geom_BSplineSurface))
|
||||
{
|
||||
// for the moment, only bspline surfaces are treated;
|
||||
// in the future, bezier surfaces and surfaces of revolution can be also handled
|
||||
return theSurface;
|
||||
}
|
||||
|
||||
// find singularities (vertices of degenerated edges)
|
||||
NCollection_List<gp_Pnt> aDegenPnt;
|
||||
NCollection_List<Standard_Real> aDegenTol;
|
||||
for (TopExp_Explorer anExp (theFace, TopAbs_EDGE); anExp.More(); anExp.Next())
|
||||
{
|
||||
TopoDS_Edge anEdge = TopoDS::Edge (anExp.Current());
|
||||
if (! BRep_Tool::Degenerated (anEdge))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
TopoDS_Vertex aV1, aV2;
|
||||
TopExp::Vertices (anEdge, aV1, aV2);
|
||||
if (! aV1.IsSame (aV2))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
aDegenPnt.Append (BRep_Tool::Pnt (aV1));
|
||||
aDegenTol.Append (BRep_Tool::Tolerance (aV1));
|
||||
}
|
||||
|
||||
// iterate by sides of the surface
|
||||
if (aType == STANDARD_TYPE(Geom_BSplineSurface))
|
||||
{
|
||||
Handle(Geom_BSplineSurface) aBSpline = Handle(Geom_BSplineSurface)::DownCast (theSurface);
|
||||
const TColgp_Array2OfPnt& aPoles = aBSpline->Poles();
|
||||
|
||||
Handle(Geom_BSplineSurface) aCopy;
|
||||
|
||||
// iterate by sides: {U=0; V=0; U=1; V=1}
|
||||
Standard_Integer RowStart[4] = {aPoles.LowerRow(), aPoles.LowerRow(), aPoles.UpperRow(), aPoles.LowerRow()};
|
||||
Standard_Integer ColStart[4] = {aPoles.LowerCol(), aPoles.LowerCol(), aPoles.LowerCol(), aPoles.UpperCol()};
|
||||
Standard_Integer RowStep[4] = {0, 1, 0, 1};
|
||||
Standard_Integer ColStep[4] = {1, 0, 1, 0};
|
||||
Standard_Integer NbSteps[4] = {aPoles.RowLength(), aPoles.ColLength(), aPoles.RowLength(), aPoles.ColLength()};
|
||||
for (Standard_Integer iSide = 0; iSide < 4; iSide++)
|
||||
{
|
||||
// compute center of gravity of side poles
|
||||
gp_XYZ aSum;
|
||||
for (int iPole = 0; iPole < NbSteps[iSide]; iPole++)
|
||||
{
|
||||
aSum += aPoles (RowStart[iSide] + iPole * RowStep[iSide], ColStart[iSide] + iPole * ColStep[iSide]).XYZ();
|
||||
}
|
||||
gp_Pnt aCenter (aSum / NbSteps[iSide]);
|
||||
|
||||
// determine if all poles of the side fit into:
|
||||
Standard_Boolean isCollapsed = Standard_True; // aCenter precisely (with gp::Resolution())
|
||||
Standard_Boolean isSingular = Standard_True; // aCenter with thePrecision
|
||||
NCollection_LocalArray<Standard_Boolean,4> isDegenerated (aDegenPnt.Extent()); // degenerated vertex
|
||||
for (size_t iDegen = 0; iDegen < isDegenerated.Size(); ++iDegen) isDegenerated[iDegen] = Standard_True;
|
||||
for (int iPole = 0; iPole < NbSteps[iSide]; iPole++)
|
||||
{
|
||||
const gp_Pnt& aPole = aPoles (RowStart[iSide] + iPole * RowStep[iSide], ColStart[iSide] + iPole * ColStep[iSide]);
|
||||
|
||||
// distance from CG
|
||||
Standard_Real aDistCG = aCenter.Distance (aPole);
|
||||
if (aDistCG > gp::Resolution())
|
||||
isCollapsed = Standard_False;
|
||||
if (aDistCG > thePrecision)
|
||||
isSingular = Standard_False;
|
||||
|
||||
// distances from degenerated points
|
||||
NCollection_List<gp_Pnt>::Iterator aDegPntIt (aDegenPnt);
|
||||
NCollection_List<Standard_Real>::Iterator aDegTolIt(aDegenTol);
|
||||
for (size_t iDegen = 0; iDegen < isDegenerated.Size(); aDegPntIt.Next(), aDegTolIt.Next(), ++iDegen)
|
||||
{
|
||||
if (isDegenerated[iDegen] && aDegPntIt.Value().Distance (aPole) >= aDegTolIt.Value())
|
||||
{
|
||||
isDegenerated[iDegen] = Standard_False;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isCollapsed)
|
||||
{
|
||||
continue; // already Ok, nothing to be done
|
||||
}
|
||||
|
||||
// decide to collapse the side: either if it is singular with thePrecision,
|
||||
// or if it fits into one (and only one) degenerated point
|
||||
if (! isSingular)
|
||||
{
|
||||
Standard_Integer aNbFit = 0;
|
||||
NCollection_List<gp_Pnt>::Iterator aDegPntIt (aDegenPnt);
|
||||
NCollection_List<Standard_Real>::Iterator aDegTolIt(aDegenTol);
|
||||
for (size_t iDegen = 0; iDegen < isDegenerated.Size(); ++iDegen)
|
||||
{
|
||||
if (isDegenerated[iDegen])
|
||||
{
|
||||
// remove degenerated point as soon as it fits at least one side, to prevent total collapse
|
||||
aDegenPnt.Remove (aDegPntIt);
|
||||
aDegenTol.Remove (aDegTolIt);
|
||||
aNbFit++;
|
||||
}
|
||||
else
|
||||
{
|
||||
aDegPntIt.Next();
|
||||
aDegTolIt.Next();
|
||||
}
|
||||
}
|
||||
|
||||
// if side fits more than one degenerated vertex, do not collapse it
|
||||
// to be on the safe side
|
||||
isSingular = (aNbFit == 1);
|
||||
}
|
||||
|
||||
// do collapse
|
||||
if (isSingular)
|
||||
{
|
||||
if (aCopy.IsNull())
|
||||
{
|
||||
aCopy = Handle(Geom_BSplineSurface)::DownCast (theSurface->Copy());
|
||||
}
|
||||
for (int iPole = 0; iPole < NbSteps[iSide]; iPole++)
|
||||
{
|
||||
aCopy->SetPole (RowStart[iSide] + iPole * RowStep[iSide], ColStart[iSide] + iPole * ColStep[iSide], aCenter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! aCopy.IsNull())
|
||||
return aCopy;
|
||||
}
|
||||
|
||||
return theSurface;
|
||||
}
|
||||
|
@@ -21,27 +21,17 @@
|
||||
#include <Standard_DefineAlloc.hxx>
|
||||
#include <Standard_Handle.hxx>
|
||||
|
||||
#include <Standard_Real.hxx>
|
||||
#include <BRepOffset_Status.hxx>
|
||||
|
||||
class Geom_Surface;
|
||||
class BRepOffset_MakeOffset;
|
||||
class BRepOffset_Inter3d;
|
||||
class BRepOffset_Inter2d;
|
||||
class BRepOffset_Offset;
|
||||
class BRepOffset_Analyse;
|
||||
class BRepOffset_MakeLoops;
|
||||
class BRepOffset_Tool;
|
||||
class BRepOffset_Interval;
|
||||
|
||||
class TopoDS_Face;
|
||||
|
||||
//! Auxiliary tools for offset algorithms
|
||||
|
||||
class BRepOffset
|
||||
{
|
||||
public:
|
||||
|
||||
DEFINE_STANDARD_ALLOC
|
||||
|
||||
|
||||
//! returns the Offset surface computed from the
|
||||
//! surface <Surface> at an OffsetDistance <Offset>.
|
||||
//!
|
||||
@@ -50,37 +40,29 @@ public:
|
||||
//!
|
||||
//! If no particular case is detected, the returned
|
||||
//! surface will have the Type Geom_OffsetSurface.
|
||||
Standard_EXPORT static Handle(Geom_Surface) Surface (const Handle(Geom_Surface)& Surface, const Standard_Real Offset, BRepOffset_Status& theStatus);
|
||||
//! Parameter allowC0 is then passed as last argument to
|
||||
//! constructor of Geom_OffsetSurface.
|
||||
Standard_EXPORT static Handle(Geom_Surface) Surface (const Handle(Geom_Surface)& Surface,
|
||||
const Standard_Real Offset,
|
||||
BRepOffset_Status& theStatus,
|
||||
Standard_Boolean allowC0 = Standard_False);
|
||||
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
|
||||
|
||||
|
||||
friend class BRepOffset_MakeOffset;
|
||||
friend class BRepOffset_Inter3d;
|
||||
friend class BRepOffset_Inter2d;
|
||||
friend class BRepOffset_Offset;
|
||||
friend class BRepOffset_Analyse;
|
||||
friend class BRepOffset_MakeLoops;
|
||||
friend class BRepOffset_Tool;
|
||||
friend class BRepOffset_Interval;
|
||||
//! Preprocess surface to be offset (bspline, bezier, or revolution based on
|
||||
//! bspline or bezier curve), by collapsing each singular side to single point.
|
||||
//!
|
||||
//! This is to avoid possible flipping of normal at the singularity
|
||||
//! of the surface due to non-zero distance between the poles that
|
||||
//! logically should be in one point (singularity).
|
||||
//!
|
||||
//! The (parametric) side of the surface is considered to be singularity if face
|
||||
//! has degenerated edge whose vertex encompasses (by its tolerance) all points on that side,
|
||||
//! or if all poles defining that side fit into sphere with radius thePrecision.
|
||||
//!
|
||||
//! Returns either original surface or its modified copy (if some poles have been moved).
|
||||
Standard_EXPORT static Handle(Geom_Surface) CollapseSingularities (const Handle(Geom_Surface)& theSurface,
|
||||
const TopoDS_Face& theFace,
|
||||
Standard_Real thePrecision);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // _BRepOffset_HeaderFile
|
||||
|
@@ -50,7 +50,9 @@
|
||||
//purpose : Constructor
|
||||
//=============================================================================
|
||||
BRepOffset_MakeSimpleOffset::BRepOffset_MakeSimpleOffset()
|
||||
: myIsBuildSolid(Standard_False),
|
||||
: myOffsetValue(0.),
|
||||
myTolerance(Precision::Confusion()),
|
||||
myIsBuildSolid(Standard_False),
|
||||
myMaxAngle(0.0),
|
||||
myError(BRepOffsetSimple_OK),
|
||||
myIsDone(Standard_False)
|
||||
@@ -66,6 +68,7 @@ BRepOffset_MakeSimpleOffset::BRepOffset_MakeSimpleOffset(const TopoDS_Shape& the
|
||||
const Standard_Real theOffsetValue)
|
||||
: myInputShape(theInputShape),
|
||||
myOffsetValue(theOffsetValue),
|
||||
myTolerance(Precision::Confusion()),
|
||||
myIsBuildSolid(Standard_False),
|
||||
myMaxAngle(0.0),
|
||||
myError(BRepOffsetSimple_OK),
|
||||
@@ -177,7 +180,7 @@ void BRepOffset_MakeSimpleOffset::Perform()
|
||||
ComputeMaxAngle();
|
||||
|
||||
myBuilder.Init(myInputShape);
|
||||
Handle(BRepOffset_SimpleOffset) aMapper = new BRepOffset_SimpleOffset(myInputShape, myOffsetValue);
|
||||
Handle(BRepOffset_SimpleOffset) aMapper = new BRepOffset_SimpleOffset(myInputShape, myOffsetValue, myTolerance);
|
||||
myBuilder.Perform(aMapper);
|
||||
|
||||
if (!myBuilder.IsDone())
|
||||
|
@@ -95,6 +95,12 @@ public:
|
||||
//! Sets offset value.
|
||||
void SetOffsetValue(const Standard_Real theOffsetValue) { myOffsetValue = theOffsetValue; }
|
||||
|
||||
//! Gets tolerance (used for handling singularities).
|
||||
Standard_Real GetTolerance() const { return myTolerance; }
|
||||
|
||||
//! Sets tolerance (used for handling singularities).
|
||||
void SetTolerance (const Standard_Real theValue) { myTolerance = theValue; }
|
||||
|
||||
//! Gets done state.
|
||||
Standard_Boolean IsDone() const { return myIsDone; }
|
||||
|
||||
@@ -134,6 +140,9 @@ private:
|
||||
//! Offset value.
|
||||
Standard_Real myOffsetValue;
|
||||
|
||||
//! Tolerance (for singularities)
|
||||
Standard_Real myTolerance;
|
||||
|
||||
//! Solid building flag. True means solid construction.
|
||||
Standard_Boolean myIsBuildSolid;
|
||||
|
||||
|
@@ -21,6 +21,7 @@
|
||||
#include <BRepBuilderAPI_MakeEdge.hxx>
|
||||
#include <BRepLib.hxx>
|
||||
#include <BRep_Tool.hxx>
|
||||
#include <BRepOffset.hxx>
|
||||
#include <Geom_OffsetSurface.hxx>
|
||||
#include <GeomAdaptor_Curve.hxx>
|
||||
#include <Geom2dAdaptor_HCurve.hxx>
|
||||
@@ -42,8 +43,10 @@ static const Standard_Integer NCONTROL=22;
|
||||
//purpose : Constructor
|
||||
//=============================================================================
|
||||
BRepOffset_SimpleOffset::BRepOffset_SimpleOffset(const TopoDS_Shape& theInputShape,
|
||||
const Standard_Real theOffsetValue)
|
||||
: myOffsetValue(theOffsetValue)
|
||||
const Standard_Real theOffsetValue,
|
||||
const Standard_Real theTolerance)
|
||||
: myOffsetValue(theOffsetValue),
|
||||
myTolerance(theTolerance)
|
||||
{
|
||||
FillOffsetData(theInputShape);
|
||||
}
|
||||
@@ -223,6 +226,7 @@ void BRepOffset_SimpleOffset::FillFaceData(const TopoDS_Face& theFace)
|
||||
// Any existing transformation is applied to the surface.
|
||||
// New face will have null transformation.
|
||||
Handle(Geom_Surface) aS = BRep_Tool::Surface(theFace);
|
||||
aS = BRepOffset::CollapseSingularities (aS, theFace, myTolerance);
|
||||
|
||||
// Take into account face orientation.
|
||||
Standard_Real aMult = 1.0;
|
||||
|
@@ -46,8 +46,12 @@ public:
|
||||
DEFINE_STANDARD_RTTI_INLINE(BRepOffset_SimpleOffset, BRepTools_Modification)
|
||||
|
||||
//! Constructor.
|
||||
//! @param theInputShape shape to be offset
|
||||
//! @param theOffsetValue offset distance (signed)
|
||||
//! @param theTolerance tolerance for handling singular points
|
||||
Standard_EXPORT BRepOffset_SimpleOffset(const TopoDS_Shape& theInputShape,
|
||||
const Standard_Real theOffsetValue);
|
||||
const Standard_Real theOffsetValue,
|
||||
const Standard_Real theTolerance);
|
||||
|
||||
//! Returns Standard_True if the face <F> has been
|
||||
//! modified. In this case, <S> is the new geometric
|
||||
@@ -178,6 +182,9 @@ private:
|
||||
|
||||
//! Offset value.
|
||||
Standard_Real myOffsetValue;
|
||||
|
||||
//! Tolerance.
|
||||
Standard_Real myTolerance;
|
||||
};
|
||||
|
||||
#endif // _BRepOffset_SimpleOffset_HeaderFile
|
||||
|
Reference in New Issue
Block a user