Loading files
// Copyright (c) 1999-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
// pdn 10.12.98: tr9_r0501-ug
// pdn 28.12.98: PRO10366 shifting pcurve between two singularities
//: k7 abv 5.01.99: USA60022.igs ent 243: FixMissingSeam() improved
//: l2 abv 10.01.99: USA60022 7289: corrections for reversed face
// gka 11.01.99 file PRO7755.stp #2018: work-around error in BRepLib_MakeFace
//: p4 abv, pdn 23.02.99: PRO9234 #15720: call BRepTools::Update() for faces
// rln 03.03.99 S4135: transmission of parameter precision to SA_Surface::NbSingularities
//: q5 abv 19.03.99 code improvement
//%14 pdn 15.03.99 adding function for fixing null area wires
//%15 pdn 20.03.99 code improvement
// abv 09.04.99 S4136: improve tolerance management; remove unused flag Closed
// #4 szv S4163: optimization
// smh 31.01.01 BUC60810 : Case of small wire on face in solid
// sln 25.09.2001 checking order of 3d and 2d representation curves
// abv 19.10.2001 FixAddNaturalBound improved and extracted as separate fix
// skl,pdn 14.05.2002 OCC55 (correction precision for small faces)
#include <Bnd_Box.hxx>
#include <BndLib_Add2dCurve.hxx>
#include <BRep_Builder.hxx>
#include <BRep_Tool.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepBuilderAPI_MakeVertex.hxx>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepTools.hxx>
#include <BRepTopAdaptor_FClass2d.hxx>
#include <Geom2d_BSplineCurve.hxx>
#include <Geom2d_Curve.hxx>
#include <Geom2d_Line.hxx>
#include <Geom2dAdaptor_Curve.hxx>
#include <Geom2dInt_GInter.hxx>
#include <Geom_BSplineSurface.hxx>
#include <Geom_Circle.hxx>
#include <Geom_ConicalSurface.hxx>
#include <Geom_Curve.hxx>
#include <Geom_RectangularTrimmedSurface.hxx>
#include <Geom_SphericalSurface.hxx>
#include <Geom_ToroidalSurface.hxx>
#include <Geom_Surface.hxx>
#include <GProp_GProps.hxx>
#include <Message_Msg.hxx>
#include <NCollection_Array1.hxx>
#include <Precision.hxx>
#include <ShapeAnalysis.hxx>
#include <ShapeAnalysis_Edge.hxx>
#include <ShapeAnalysis_Surface.hxx>
#include <ShapeBuild_Edge.hxx>
#include <ShapeBuild_ReShape.hxx>
#include <ShapeExtend_CompositeSurface.hxx>
#include <ShapeFix.hxx>
#include <ShapeFix_ComposeShell.hxx>
#include <ShapeFix_Edge.hxx>
#include <ShapeFix_Face.hxx>
#include <ShapeFix_IntersectionTool.hxx>
#include <ShapeFix_SplitTool.hxx>
#include <ShapeFix_Wire.hxx>
#include <Standard_Type.hxx>
#include <NCollection_Array2.hxx>
#include <NCollection_HArray2.hxx>
#include <gp_Pnt2d.hxx>
#include <NCollection_Sequence.hxx>
#include <Standard_Integer.hxx>
#include <NCollection_Map.hxx>
#include <TopExp.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Compound.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Iterator.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopoDS_Wire.hxx>
#include <TopoDS_Shape.hxx>
#include <TopTools_ShapeMapHasher.hxx>
#include <NCollection_DataMap.hxx>
#include <NCollection_List.hxx>
#include <NCollection_IndexedMap.hxx>
IMPLEMENT_STANDARD_RTTIEXT(ShapeFix_Face, ShapeFix_Root)
static bool IsSurfaceUVInfinite(const occ::handle<Geom_Surface>& theSurf)
{
double UMin, UMax, VMin, VMax;
theSurf->Bounds(UMin, UMax, VMin, VMax);
return (Precision::IsInfinite(UMin) || Precision::IsInfinite(UMax) || Precision::IsInfinite(VMin)
|| Precision::IsInfinite(VMax));
}
static bool IsSurfaceUVPeriodic(const occ::handle<GeomAdaptor_Surface>& theSurf)
{
return ((theSurf->IsUPeriodic() && theSurf->IsVPeriodic())
|| theSurf->GetType() == GeomAbs_Sphere);
}
//=================================================================================================
ShapeFix_Face::ShapeFix_Face()
{
myFwd = true;
myStatus = 0;
myFixWire = new ShapeFix_Wire;
ClearModes();
}
//=================================================================================================
ShapeFix_Face::ShapeFix_Face(const TopoDS_Face& face)
{
myFwd = true;
myStatus = 0;
myFixWire = new ShapeFix_Wire;
ClearModes();
Init(face);
}
//=================================================================================================
void ShapeFix_Face::ClearModes()
{
myFixWireMode = -1;
myFixOrientationMode = -1;
myFixAddNaturalBoundMode = -1;
myFixMissingSeamMode = -1;
myFixSmallAreaWireMode = -1;
myRemoveSmallAreaFaceMode = -1;
myFixIntersectingWiresMode = -1;
myFixLoopWiresMode = -1;
myFixSplitFaceMode = -1;
myAutoCorrectPrecisionMode = 1;
myFixPeriodicDegenerated = -1;
}
//=================================================================================================
void ShapeFix_Face::SetMsgRegistrator(const occ::handle<ShapeExtend_BasicMsgRegistrator>& msgreg)
{
ShapeFix_Root::SetMsgRegistrator(msgreg);
myFixWire->SetMsgRegistrator(msgreg);
}
//=================================================================================================
void ShapeFix_Face::SetPrecision(const double preci)
{
ShapeFix_Root::SetPrecision(preci);
myFixWire->SetPrecision(preci);
}
//=================================================================================================
void ShapeFix_Face::SetMinTolerance(const double mintol)
{
ShapeFix_Root::SetMinTolerance(mintol);
myFixWire->SetMinTolerance(mintol);
}
//=================================================================================================
void ShapeFix_Face::SetMaxTolerance(const double maxtol)
{
ShapeFix_Root::SetMaxTolerance(maxtol);
myFixWire->SetMaxTolerance(maxtol);
}
//=================================================================================================
void ShapeFix_Face::Init(const occ::handle<Geom_Surface>& surf, const double preci, const bool fwd)
{
myStatus = 0;
occ::handle<ShapeAnalysis_Surface> sas = new ShapeAnalysis_Surface(surf);
Init(sas, preci, fwd);
}
//=================================================================================================
void ShapeFix_Face::Init(const occ::handle<ShapeAnalysis_Surface>& surf,
const double preci,
const bool fwd)
{
myStatus = 0;
mySurf = surf;
SetPrecision(preci);
BRep_Builder B;
B.MakeFace(myFace, mySurf->Surface(), ::Precision::Confusion());
myShape = myFace;
myFwd = fwd;
if (!fwd)
{
myFace.Orientation(TopAbs_REVERSED);
}
}
//=================================================================================================
void ShapeFix_Face::Init(const TopoDS_Face& theFace)
{
myStatus = 0;
// Check if surface is null. It doesn't make sense to create ShapeAnalysis_Surface in that case.
const occ::handle<Geom_Surface> aSurface = BRep_Tool::Surface(theFace);
if (!aSurface.IsNull())
{
mySurf = new ShapeAnalysis_Surface(aSurface);
}
myFwd = (theFace.Orientation() != TopAbs_REVERSED);
myFace = theFace;
myShape = myFace;
}
//=================================================================================================
void ShapeFix_Face::Add(const TopoDS_Wire& wire)
{
if (wire.IsNull())
{
return;
}
BRep_Builder B;
// szv#4:S4163:12Mar99 SGI warns
TopoDS_Shape fc = myFace.Oriented(TopAbs_FORWARD); //: l2 abv 10 Jan 99: Oriented()
B.Add(fc, wire);
}
//=======================================================================
// function : SplitWire
// purpose : auxiliary - try to split wire (it is needed if some segments
// were removed in ShapeFix_Wire::FixSelfIntersection() )
//=======================================================================
static bool SplitWire(const TopoDS_Face& face,
const TopoDS_Wire& wire,
NCollection_Sequence<TopoDS_Shape>& aResWires)
{
NCollection_Map<int> UsedEdges;
occ::handle<ShapeExtend_WireData> sewd = new ShapeExtend_WireData(wire);
int i, j, k;
ShapeAnalysis_Edge sae;
for (i = 1; i <= sewd->NbEdges(); i++)
{
if (UsedEdges.Contains(i))
{
continue;
}
TopoDS_Edge E1 = sewd->Edge(i);
UsedEdges.Add(i);
TopoDS_Vertex V0, V1, V2;
V0 = sae.FirstVertex(E1);
V1 = sae.LastVertex(E1);
occ::handle<ShapeExtend_WireData> sewd1 = new ShapeExtend_WireData;
sewd1->Add(E1);
bool IsConnectedEdge = true;
// Set to true when null pcurves prevent the 2D closure verification:
// the partial wire collected so far is discarded for this start edge.
bool aIsAbandonedSplit = false;
for (j = 2; j <= sewd->NbEdges() && IsConnectedEdge; j++)
{
TopoDS_Edge E2;
for (k = 2; k <= sewd->NbEdges(); k++)
{
if (UsedEdges.Contains(k))
{
continue;
}
E2 = sewd->Edge(k);
TopoDS_Vertex V21 = sae.FirstVertex(E2);
TopoDS_Vertex V22 = sae.LastVertex(E2);
if (sae.FirstVertex(E2).IsSame(V1))
{
sewd1->Add(E2);
UsedEdges.Add(k);
V1 = sae.LastVertex(E2);
break;
}
}
if (k > sewd->NbEdges())
{
IsConnectedEdge = false;
break;
}
if (V1.IsSame(V0))
{
// check that V0 and V1 are same in 2d too
double a1, b1, a2, b2;
occ::handle<Geom2d_Curve> curve1 = BRep_Tool::CurveOnSurface(E1, face, a1, b1);
occ::handle<Geom2d_Curve> curve2 = BRep_Tool::CurveOnSurface(E2, face, a2, b2);
if (curve1.IsNull() || curve2.IsNull())
{
aIsAbandonedSplit = true;
break;
}
gp_Pnt2d v0, v1;
if (E1.Orientation() == TopAbs_REVERSED)
{
a1 = b1;
}
if (E2.Orientation() == TopAbs_REVERSED)
{
b2 = a2;
}
curve1->D0(a1, v0);
curve2->D0(b2, v1);
GeomAdaptor_Surface anAdaptor(BRep_Tool::Surface(face));
double tol = std::max(BRep_Tool::Tolerance(V0), BRep_Tool::Tolerance(V1));
double maxResolution = 2 * std::max(anAdaptor.UResolution(tol), anAdaptor.VResolution(tol));
if (v0.SquareDistance(v1) < maxResolution)
{
// new wire is closed, put it into sequence
aResWires.Append(sewd1->Wire());
break;
}
}
}
if (aIsAbandonedSplit)
{
continue;
}
if (!IsConnectedEdge)
{
// create new notclosed wire
aResWires.Append(sewd1->Wire());
}
if (UsedEdges.Extent() == sewd->NbEdges())
{
break;
}
}
return true;
}
//=================================================================================================
bool ShapeFix_Face::Perform(const Message_ProgressRange& theProgress)
{
myStatus = ShapeExtend::EncodeStatus(ShapeExtend_OK);
myFixWire->SetContext(Context());
occ::handle<ShapeFix_Wire> theAdvFixWire = myFixWire;
if (theAdvFixWire.IsNull())
{
return false;
}
// Skip if an earlier fix already removed this face or replaced it with a non-face.
if (!Context().IsNull())
{
const TopoDS_Shape anApplied = Context()->Apply(myFace);
if (anApplied.IsNull() || anApplied.ShapeType() != TopAbs_FACE)
{
myResult = anApplied;
return false;
}
}
BRep_Builder B;
TopoDS_Shape aInitFace = myFace;
// perform first part of fixes on wires
bool isfixReorder = false;
bool isReplaced = false;
// gka fix in order to avoid lost messages (following OCC21771)
NCollection_DataMap<TopoDS_Shape, TopoDS_Shape, TopTools_ShapeMapHasher> aMapReorderedWires;
double aSavPreci = Precision();
if (NeedFix(myFixWireMode))
{
theAdvFixWire->SetFace(myFace, mySurf);
int usFixLackingMode = theAdvFixWire->FixLackingMode();
// int usFixNotchedEdgesMode = theAdvFixWire->FixNotchedEdgesMode(); // CR0024983
int usFixSelfIntersectionMode = theAdvFixWire->FixSelfIntersectionMode();
theAdvFixWire->FixLackingMode() = false;
// theAdvFixWire->FixNotchedEdgesMode() = false; // CR0024983
theAdvFixWire->FixSelfIntersectionMode() = false;
bool fixed = false;
TopoDS_Shape S = myFace;
if (!Context().IsNull())
{
S = Context()->Apply(myFace);
}
TopoDS_Shape emptyCopied = S.EmptyCopied();
TopoDS_Face tmpFace = TopoDS::Face(emptyCopied);
tmpFace.Orientation(TopAbs_FORWARD);
// skl 29.03.2010 (OCC21623)
if (myAutoCorrectPrecisionMode)
{
double size = ShapeFix::LeastEdgeSize(S);
double newpreci = std::min(aSavPreci, size / 2.);
newpreci = newpreci * 1.00001;
if (aSavPreci > newpreci && newpreci > Precision::Confusion())
{
SetPrecision(newpreci);
theAdvFixWire->SetPrecision(newpreci);
}
}
isfixReorder = false;
for (TopoDS_Iterator iter(S, false); iter.More(); iter.Next())
{
if (iter.Value().ShapeType() != TopAbs_WIRE)
{
B.Add(tmpFace, iter.Value());
continue;
}
TopoDS_Wire wire = TopoDS::Wire(iter.Value());
theAdvFixWire->Load(wire);
if (theAdvFixWire->NbEdges() == 0)
{
if (theAdvFixWire->WireData()->NbNonManifoldEdges())
{
B.Add(tmpFace, wire);
}
else
{
fixed = true;
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE5);
}
continue;
}
if (theAdvFixWire->Perform(theProgress))
{
// fixed = true;
isfixReorder = (theAdvFixWire->StatusReorder(ShapeExtend_DONE) || isfixReorder);
fixed = (theAdvFixWire->StatusSmall(ShapeExtend_DONE)
|| theAdvFixWire->StatusConnected(ShapeExtend_DONE)
|| theAdvFixWire->StatusEdgeCurves(ShapeExtend_DONE)
|| theAdvFixWire->StatusNotches(ShapeExtend_DONE) || // CR0024983
theAdvFixWire->StatusFixTails(ShapeExtend_DONE)
|| theAdvFixWire->StatusDegenerated(ShapeExtend_DONE)
|| theAdvFixWire->StatusClosed(ShapeExtend_DONE));
TopoDS_Wire w = theAdvFixWire->Wire();
if (fixed)
{
if (!Context().IsNull())
{
Context()->Replace(wire, w);
}
if (theAdvFixWire->NbEdges() == 0)
{
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE5);
continue;
}
}
else if (!wire.IsSame(w))
{
aMapReorderedWires.Bind(wire, w);
}
wire = w;
}
B.Add(tmpFace, wire);
}
theAdvFixWire->FixLackingMode() = usFixLackingMode;
theAdvFixWire->FixSelfIntersectionMode() = usFixSelfIntersectionMode;
if (!myFwd)
{
tmpFace.Orientation(TopAbs_REVERSED);
}
if (fixed)
{
if (!Context().IsNull())
{
Context()->Replace(S, tmpFace);
}
isReplaced = true;
}
if (fixed || isfixReorder)
{
myFace = tmpFace;
if (!theAdvFixWire->StatusReorder(ShapeExtend_DONE5))
{
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE1);
}
}
}
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())
{
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE3);
}
}
// cycle by all possible faces coming from FixMissingSeam
// each face is processed as if it was single
TopExp_Explorer exp(myResult, TopAbs_FACE);
for (; exp.More(); exp.Next())
{
myFace = TopoDS::Face(exp.Current());
bool NeedCheckSplitWire = false;
// perform second part of fixes on wires
if (NeedFix(myFixWireMode))
{
theAdvFixWire->SetFace(myFace, mySurf);
int usFixSmallMode = theAdvFixWire->FixSmallMode();
int usFixConnectedMode = theAdvFixWire->FixConnectedMode();
int usFixEdgeCurvesMode = theAdvFixWire->FixEdgeCurvesMode();
int usFixDegeneratedMode = theAdvFixWire->FixDegeneratedMode();
theAdvFixWire->FixSmallMode() = false;
theAdvFixWire->FixConnectedMode() = false;
theAdvFixWire->FixEdgeCurvesMode() = false;
theAdvFixWire->FixDegeneratedMode() = false;
bool fixed = false;
TopoDS_Shape S = myFace;
if (!Context().IsNull())
{
S = Context()->Apply(myFace);
}
TopoDS_Shape emptyCopied = S.EmptyCopied();
TopoDS_Face tmpFace = TopoDS::Face(emptyCopied);
tmpFace.Orientation(TopAbs_FORWARD);
for (TopoDS_Iterator iter(S, false); iter.More(); iter.Next())
{
if (iter.Value().ShapeType() != TopAbs_WIRE)
{
B.Add(tmpFace, iter.Value());
continue;
}
TopoDS_Wire wire = TopoDS::Wire(iter.Value());
theAdvFixWire->Load(wire);
if (theAdvFixWire->NbEdges() == 0)
{
if (theAdvFixWire->WireData()->NbNonManifoldEdges())
{
B.Add(tmpFace, wire);
}
else
{
fixed = true;
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE5);
}
continue;
}
if (theAdvFixWire->Perform())
{
isfixReorder = theAdvFixWire->StatusReorder(ShapeExtend_DONE);
fixed = (theAdvFixWire->StatusLacking(ShapeExtend_DONE)
|| theAdvFixWire->StatusSelfIntersection(ShapeExtend_DONE)
|| theAdvFixWire->StatusNotches(ShapeExtend_DONE)
|| theAdvFixWire->StatusFixTails(ShapeExtend_DONE));
TopoDS_Wire w = theAdvFixWire->Wire();
if (fixed)
{
if (!Context().IsNull())
{
Context()->Replace(wire, w);
}
}
else if (!wire.IsSame(w))
{
aMapReorderedWires.Bind(wire, w);
}
wire = w;
}
if (theAdvFixWire->StatusRemovedSegment())
{
NeedCheckSplitWire = true;
}
// fix for loop of wire
NCollection_Sequence<TopoDS_Shape> aLoopWires;
if (NeedFix(myFixLoopWiresMode) && FixLoopWire(aLoopWires))
{
if (aLoopWires.Length() > 1)
{
// clang-format off
SendWarning ( wire, Message_Msg ( "FixAdvFace.FixLoopWire.MSG0" ) );// Wire was split on several wires
}
// clang-format on
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE7);
fixed = true;
int k = 1;
for (; k <= aLoopWires.Length(); k++)
{
B.Add(tmpFace, aLoopWires.Value(k));
}
}
else
{
B.Add(tmpFace, wire);
}
}
theAdvFixWire->FixSmallMode() = usFixSmallMode;
theAdvFixWire->FixConnectedMode() = usFixConnectedMode;
theAdvFixWire->FixEdgeCurvesMode() = usFixEdgeCurvesMode;
theAdvFixWire->FixDegeneratedMode() = usFixDegeneratedMode;
if (fixed)
{
if (!myFwd)
{
tmpFace.Orientation(TopAbs_REVERSED);
}
if (!isReplaced && !aInitFace.IsSame(myResult) && !Context().IsNull())
{ // gka 06.09.04 BUG 6555
Context()->Replace(aInitFace, savShape);
}
if (!Context().IsNull())
{
Context()->Replace(S, tmpFace);
}
myFace = tmpFace;
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE1);
}
}
if (NeedCheckSplitWire)
{
// try to split wire - it is needed if some segments were removed
// in ShapeFix_Wire::FixSelfIntersection()
TopoDS_Shape S = myFace;
if (!Context().IsNull())
{
S = Context()->Apply(myFace);
}
TopoDS_Shape emptyCopied = S.EmptyCopied();
TopoDS_Face tmpFace = TopoDS::Face(emptyCopied);
tmpFace.Orientation(TopAbs_FORWARD);
NCollection_Sequence<TopoDS_Shape> aWires;
int nbw = 0;
for (TopoDS_Iterator iter(S, false); iter.More(); iter.Next())
{
if (iter.Value().ShapeType() != TopAbs_WIRE)
{
B.Add(tmpFace, iter.Value());
continue;
}
if (iter.Value().Orientation() != TopAbs_FORWARD
&& iter.Value().Orientation() != TopAbs_REVERSED)
{
B.Add(tmpFace, TopoDS::Wire(iter.Value()));
continue;
}
nbw++;
TopoDS_Wire wire = TopoDS::Wire(iter.Value());
SplitWire(tmpFace, wire, aWires);
}
if (nbw < aWires.Length())
{
for (int iw = 1; iw <= aWires.Length(); iw++)
{
B.Add(tmpFace, aWires.Value(iw));
}
if (!Context().IsNull())
{
Context()->Replace(S, tmpFace);
}
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE8);
myFace = tmpFace;
}
}
// fix intersecting wires
if (FixWiresTwoCoincEdges())
{
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE7);
}
if (NeedFix(myFixIntersectingWiresMode))
{
if (FixIntersectingWires())
{
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE6);
}
}
// fix orientation
NCollection_DataMap<TopoDS_Shape, NCollection_List<TopoDS_Shape>, TopTools_ShapeMapHasher>
MapWires;
MapWires.Clear();
if (NeedFix(myFixOrientationMode))
{
if (FixOrientation(MapWires))
{
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE2);
}
}
BRepTools::Update(myFace);
// fix natural bounds
bool NeedSplit = true;
if (FixAddNaturalBound())
{
NeedSplit = false;
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE5);
}
// split face
if (NeedFix(myFixSplitFaceMode) && NeedSplit && MapWires.Extent() > 1)
{
if (FixSplitFace(MapWires))
{
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE8);
}
}
}
// return the original preci
SetPrecision(aSavPreci);
theAdvFixWire->SetPrecision(aSavPreci);
// cycle by all possible faces coming from FixAddNaturalBound
// each face is processed as if it was single
for (exp.Init(myResult, TopAbs_FACE); exp.More(); exp.Next())
{
myFace = TopoDS::Face(exp.Current());
// fix small-area wires
if (NeedFix(myFixSmallAreaWireMode, false))
{
const bool isRemoveFace = NeedFix(myRemoveSmallAreaFaceMode, false);
if (FixSmallAreaWire(isRemoveFace))
{
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE4);
}
}
}
if (!Context().IsNull())
{
if (Status(ShapeExtend_DONE) && !isReplaced && !aInitFace.IsSame(savShape))
{
// gka fix in order to avoid lost messages (following OCC21771)
if (aMapReorderedWires.Extent())
{
TopoDS_Iterator aItW(aInitFace, false);
for (; aItW.More(); aItW.Next())
{
TopoDS_Shape aCurW = aItW.Value();
while (aMapReorderedWires.IsBound(aCurW))
{
const TopoDS_Shape& aFixW = aMapReorderedWires.Find(aCurW);
Context()->Replace(aCurW, aFixW);
aCurW = aFixW;
}
}
}
Context()->Replace(aInitFace, savShape);
}
myResult = Context()->Apply(aInitFace); // gka 06.09.04
}
else if (!Status(ShapeExtend_DONE))
{
myResult = aInitFace;
}
return Status(ShapeExtend_DONE);
}
//=================================================================================================
// Shift all pcurves of edges in the given wire on the given face
// to vector <vec>
static void Shift2dWire(const TopoDS_Wire& w,
const TopoDS_Face& f,
const gp_Vec2d vec,
const occ::handle<ShapeAnalysis_Surface>& theSurface,
bool recompute3d = false)
{
gp_Trsf2d tr2d;
tr2d.SetTranslation(vec.XY());
ShapeAnalysis_Edge sae;
ShapeBuild_Edge sbe;
BRep_Builder B;
for (TopoDS_Iterator ei(w, false); ei.More(); ei.Next())
{
TopoDS_Edge edge = TopoDS::Edge(ei.Value());
occ::handle<Geom2d_Curve> C2d;
double cf, cl;
if (!sae.PCurve(edge, f, C2d, cf, cl, true))
{
continue;
}
C2d->Transform(tr2d);
if (recompute3d)
{
// recompute 3d curve and vertex
sbe.RemoveCurve3d(edge);
sbe.BuildCurve3d(edge);
B.UpdateVertex(sae.FirstVertex(edge), theSurface->Value(C2d->Value(cf)), 0.);
}
}
}
// Cut interval from the sequence of intervals
static bool CutInterval(NCollection_Sequence<gp_Pnt2d>& intervals,
const gp_Pnt2d& toAddI,
const double period)
{
if (intervals.Length() <= 0)
{
return false;
}
for (int j = 0; j < 2; j++)
{ // try twice, align to bottom and to top
for (int i = 1; i <= intervals.Length(); i++)
{
gp_Pnt2d interval = intervals(i);
// ACIS907, OCC921 a054a.sat (face 124)
double shift = ShapeAnalysis::AdjustByPeriod((j ? toAddI.X() : toAddI.Y()),
0.5 * (interval.X() + interval.Y()),
period);
gp_Pnt2d toAdd(toAddI.X() + shift, toAddI.Y() + shift);
if (toAdd.Y() <= interval.X() || toAdd.X() >= interval.Y())
{
continue;
}
if (toAdd.X() > interval.X())
{
if (toAdd.Y() < interval.Y())
{
intervals.InsertBefore(i, interval);
intervals.ChangeValue(i + 1).SetX(toAdd.Y()); // i++...
}
intervals.ChangeValue(i).SetY(toAdd.X());
}
else if (toAdd.Y() < interval.Y())
{
intervals.ChangeValue(i).SetX(toAdd.Y());
}
else
{
intervals.Remove(i--);
}
}
}
return true;
}
// Find middle of the biggest interval
static double FindBestInterval(NCollection_Sequence<gp_Pnt2d>& intervals)
{
double shift = 0., max = -1.;
for (int i = 1; i <= intervals.Length(); i++)
{
gp_Pnt2d interval = intervals(i);
if (interval.Y() - interval.X() <= max)
{
continue;
}
max = interval.Y() - interval.X();
shift = interval.X() + 0.5 * max;
}
return shift;
}
//=================================================================================================
// Detect missing natural boundary on spherical surfaces and add it if
// necessary
// pdn 981202: add natural bounds if missing (on sphere only)
//: abv 28.08.01: rewritten and extended for toruses
bool ShapeFix_Face::FixAddNaturalBound()
{
if (mySurf.IsNull())
{
return false;
}
if (!Context().IsNull())
{
TopoDS_Shape S = Context()->Apply(myFace);
myFace = TopoDS::Face(S);
}
// collect wires in sequence
NCollection_Sequence<TopoDS_Shape> ws;
NCollection_Sequence<TopoDS_Shape> vs;
TopoDS_Iterator wi(myFace, false);
for (; wi.More(); wi.Next())
{
if (wi.Value().ShapeType() == TopAbs_WIRE
&& (wi.Value().Orientation() == TopAbs_FORWARD
|| wi.Value().Orientation() == TopAbs_REVERSED))
{
ws.Append(wi.Value());
}
else
{
vs.Append(wi.Value());
}
}
// deal with the case of an empty face: just create a new face by a standard tool
if (ws.IsEmpty() && !IsSurfaceUVInfinite(mySurf->Surface()))
{
BRepBuilderAPI_MakeFace aFaceBuilder(mySurf->Surface(), Precision::Confusion());
TopoDS_Face aNewFace = aFaceBuilder.Face();
aNewFace.Orientation(myFace.Orientation());
if (!Context().IsNull())
{
Context()->Replace(myFace, aNewFace);
}
// taking into account orientation
myFace = aNewFace;
// gka 11.01.99 file PRO7755.stp entity #2018 surface #1895: error BRepLib_MakeFace func
// IsDegenerated
occ::handle<ShapeFix_Edge> sfe = myFixWire->FixEdgeTool();
for (TopExp_Explorer Eed(myFace, TopAbs_EDGE); Eed.More(); Eed.Next())
{
TopoDS_Edge edg = TopoDS::Edge(Eed.Current());
sfe->FixVertexTolerance(edg, myFace);
}
// B.UpdateFace (myFace,myPrecision);
// clang-format off
SendWarning ( myFace, Message_Msg ( "FixAdvFace.FixOrientation.MSG0" ) );// Face created with natural bounds
// clang-format on
BRepTools::Update(myFace);
myResult = myFace;
return true;
}
// check if surface doesn't need natural bounds
if (!isNeedAddNaturalBound(ws))
{
return false;
}
// Collect information on free intervals in U and V
NCollection_Sequence<gp_Pnt2d> intU, intV, centers;
double SUF, SUL, SVF, SVL;
mySurf->Bounds(SUF, SUL, SVF, SVL);
intU.Append(gp_Pnt2d(SUF, SUL));
intV.Append(gp_Pnt2d(SVF, SVL));
int nb = ws.Length();
int i;
for (i = 1; i <= nb; i++)
{
double Umin, Vmin, Umax, Vmax;
// Bnd_Box2d B;
TopoDS_Wire aw = TopoDS::Wire(ws.Value(i));
TopoDS_Face aWireFace = TopoDS::Face(myFace.EmptyCopied());
BRep_Builder aB;
aB.Add(aWireFace, aw);
ShapeAnalysis::GetFaceUVBounds(aWireFace, Umin, Umax, Vmin, Vmax);
// PTV 01.11.2002 ACIS907, OCC921 end
if (mySurf->IsUClosed())
{
CutInterval(intU, gp_Pnt2d(Umin, Umax), SUL - SUF);
}
if (mySurf->IsVClosed())
{
CutInterval(intV, gp_Pnt2d(Vmin, Vmax), SVL - SVF);
}
centers.Append(gp_Pnt2d(0.5 * (Umin + Umax), 0.5 * (Vmin + Vmax)));
}
// find best interval and thus compute shift
gp_Pnt2d shift(0., 0.);
if (mySurf->IsUClosed())
{
shift.SetX(FindBestInterval(intU));
}
if (mySurf->IsVClosed())
{
shift.SetY(FindBestInterval(intV));
}
// Adjust all other wires to be inside outer one
gp_Pnt2d center(shift.X() + 0.5 * (SUL - SUF), shift.Y() + 0.5 * (SVL - SVF));
for (i = 1; i <= nb; i++)
{
TopoDS_Wire wire = TopoDS::Wire(ws.Value(i));
gp_Pnt2d sh(0., 0.);
if (mySurf->IsUClosed())
{
sh.SetX(ShapeAnalysis::AdjustByPeriod(centers(i).X(), center.X(), SUL - SUF));
}
if (mySurf->IsVClosed())
{
sh.SetY(ShapeAnalysis::AdjustByPeriod(centers(i).Y(), center.Y(), SVL - SVF));
}
Shift2dWire(wire, myFace, sh.XY(), mySurf);
}
// Create naturally bounded surface and add that wire to sequence
TopLoc_Location L;
occ::handle<Geom_Surface> surf = BRep_Tool::Surface(myFace, L);
BRepBuilderAPI_MakeFace mf(surf, Precision::Confusion());
TopoDS_Face ftmp = mf.Face();
ftmp.Location(L);
for (wi.Initialize(ftmp, false); wi.More(); wi.Next())
{
if (wi.Value().ShapeType() != TopAbs_WIRE)
{
continue;
}
TopoDS_Wire wire = TopoDS::Wire(wi.Value());
ws.Append(wire);
if (shift.XY().Modulus() < ::Precision::PConfusion())
{
continue;
}
Shift2dWire(wire, myFace, shift.XY(), mySurf, true);
}
// Fix possible case on sphere when gap contains degenerated edge
// and thus has a common part with natural boundary
// Such hole should be merged with boundary
if (mySurf->Adaptor3d()->GetType() == GeomAbs_Sphere && ws.Length() == nb + 1)
{
occ::handle<ShapeExtend_WireData> bnd = new ShapeExtend_WireData(TopoDS::Wire(ws.Last()));
// code to become separate method FixTouchingWires()
for (i = 1; i <= nb; i++)
{
occ::handle<ShapeExtend_WireData> sbwd = new ShapeExtend_WireData(TopoDS::Wire(ws.Value(i)));
for (int j = 1; j <= sbwd->NbEdges(); j++)
{
if (!BRep_Tool::Degenerated(sbwd->Edge(j)))
{
continue;
}
// find corresponding place in boundary
ShapeAnalysis_Edge sae;
TopoDS_Vertex V = sae.FirstVertex(sbwd->Edge(j));
int k;
for (k = 1; k <= bnd->NbEdges(); k++)
{
if (!BRep_Tool::Degenerated(bnd->Edge(k)))
{
continue;
}
if (BRepTools::Compare(V, sae.FirstVertex(bnd->Edge(k))))
{
break;
}
}
if (k > bnd->NbEdges())
{
continue;
}
// and insert hole to that place
BRep_Builder B;
B.Degenerated(sbwd->Edge(j), false);
B.Degenerated(bnd->Edge(k), false);
sbwd->SetLast(j);
bnd->Add(sbwd, k + 1);
ws.Remove(i--);
nb--;
myFixWire->SetFace(myFace, mySurf);
myFixWire->Load(bnd);
myFixWire->FixConnected();
myFixWire->FixDegenerated();
ws.SetValue(ws.Length(), bnd->Wire());
break;
}
}
}
// Create resulting face
BRep_Builder B;
TopoDS_Shape S = myFace.EmptyCopied();
S.Orientation(TopAbs_FORWARD);
for (i = 1; i <= ws.Length(); i++)
{
B.Add(S, ws.Value(i));
}
for (i = 1; i <= vs.Length(); i++)
{
B.Add(S, vs.Value(i));
}
if (!myFwd)
{
S.Orientation(TopAbs_REVERSED);
}
if (!Context().IsNull())
{
Context()->Replace(myFace, S);
}
myFace = TopoDS::Face(S);
BRepTools::Update(myFace);
// clang-format off
SendWarning ( myFace, Message_Msg ( "FixAdvFace.FixOrientation.MSG0" ) );// Face created with natural bounds
// clang-format on
return true;
}
//=================================================================================================
bool ShapeFix_Face::FixOrientation()
{
NCollection_DataMap<TopoDS_Shape, NCollection_List<TopoDS_Shape>, TopTools_ShapeMapHasher>
MapWires;
MapWires.Clear();
return FixOrientation(MapWires);
}
//=================================================================================================
bool ShapeFix_Face::isNeedAddNaturalBound(
const NCollection_Sequence<TopoDS_Shape>& theOrientedWires)
{
// if fix is not needed
if (!NeedFix(myFixAddNaturalBoundMode))
{
return false;
}
// if surface is not double-closed
if (mySurf && !IsSurfaceUVPeriodic(mySurf->Adaptor3d()))
{
return false;
}
// if face has an OUTER bound
if (ShapeAnalysis::IsOuterBound(myFace))
{
return false;
}
// check that not any wire has a seam edge and not any edge is degenerated.
// because the presence of a seam or degenerated edge indicates that this wire should be an
// external one, and in case of its incorrect orientation, this will be corrected.
int aNbOriented = theOrientedWires.Length();
for (int i = 1; i <= aNbOriented; i++)
{
TopoDS_Wire aWire = TopoDS::Wire(theOrientedWires.Value(i));
for (TopoDS_Iterator anEdgeIt(aWire); anEdgeIt.More(); anEdgeIt.Next())
{
TopoDS_Edge anEdge = TopoDS::Edge(anEdgeIt.Value());
if (BRep_Tool::Degenerated(anEdge))
{
return false;
}
if (BRep_Tool::IsClosed(anEdge, myFace))
{
return false;
}
}
}
return true;
}
//=================================================================================================
bool ShapeFix_Face::FixOrientation(
NCollection_DataMap<TopoDS_Shape, NCollection_List<TopoDS_Shape>, TopTools_ShapeMapHasher>&
MapWires)
{
bool done = false;
if (!Context().IsNull())
{
TopoDS_Shape S = Context()->Apply(myFace);
myFace = TopoDS::Face(S);
}
NCollection_Sequence<TopoDS_Shape> ws;
NCollection_Sequence<TopoDS_Shape> allSubShapes;
// smh: BUC60810 : protection against very small wires (one-edge, null-length)
NCollection_Sequence<TopoDS_Shape> VerySmallWires;
for (TopoDS_Iterator wi(myFace, false); wi.More(); wi.Next())
{
if (wi.Value().ShapeType() == TopAbs_VERTEX
|| (wi.Value().Orientation() != TopAbs_FORWARD
&& wi.Value().Orientation() != TopAbs_REVERSED))
{
allSubShapes.Append(wi.Value());
// ws.Append (wi.Value());
continue;
}
TopoDS_Iterator ei(wi.Value(), false);
TopoDS_Edge anEdge;
double length = RealLast();
if (ei.More())
{
anEdge = TopoDS::Edge(ei.Value());
ei.Next();
if (!ei.More())
{
length = 0;
double First, Last;
occ::handle<Geom_Curve> c3d;
ShapeAnalysis_Edge sae;
if (sae.Curve3d(anEdge, c3d, First, Last))
{
gp_Pnt pntIni = c3d->Value(First);
gp_XYZ prev;
prev = pntIni.XYZ();
int NbControl = 10;
for (int j = 1; j < NbControl; j++)
{
double prm = ((NbControl - 1 - j) * First + j * Last) / (NbControl - 1);
gp_Pnt pntCurr = c3d->Value(prm);
gp_XYZ curr = pntCurr.XYZ();
gp_XYZ delta = curr - prev;
length += delta.Modulus();
prev = curr;
}
}
}
}
else
{
length = 0;
}
if (length > ::Precision::Confusion())
{
ws.Append(wi.Value());
allSubShapes.Append(wi.Value());
}
else
{
VerySmallWires.Append(wi.Value());
}
}
if (VerySmallWires.Length() > 0)
{
done = true;
}
int nb = ws.Length();
int nbAll = allSubShapes.Length();
BRep_Builder B;
// if no wires, just do nothing
if (nb <= 0)
{
return false;
}
bool isAddNaturalBounds = isNeedAddNaturalBound(ws);
NCollection_Sequence<int> aSeqReversed;
// if wire is only one, check its orientation
if (nb == 1)
{
// skl 12.04.2002 for cases with nbwires>1 (VerySmallWires>1)
// make face with only one wire (ws.Value(1))
TopoDS_Shape dummy = myFace.EmptyCopied();
TopoDS_Face af = TopoDS::Face(dummy);
af.Orientation(TopAbs_FORWARD);
B.Add(af, ws.Value(1));
if (!isAddNaturalBounds && !ShapeAnalysis::IsOuterBound(af))
{
occ::handle<ShapeExtend_WireData> sbdw = new ShapeExtend_WireData(TopoDS::Wire(ws.Value(1)));
sbdw->Reverse(myFace);
ws.SetValue(1, sbdw->Wire());
// clang-format off
SendWarning(sbdw->Wire(), Message_Msg("FixAdvFace.FixOrientation.MSG5"));// Wire on face was reversed
// clang-format on
done = true;
}
}
// in case of several wires, perform complex analysis
// TEST CAUTION
// Multiple wires: relative orientations
// Each wire must "contain" all the others
// Obviously, in the case of leopard skin, there may be a problem
else if (!mySurf.IsNull())
{
// Take each wire (NB: curves present!)
// In principle, we should reject unclosed wires (see missing seam?)
// We classify it relative to the others, which must all be either IN or
// OUT. Otherwise, there is nesting -> SDB. If IN, OK, if OUT, we reverse
// (NB: not myClos here, so no stitching problem)
// If there is at least one inversion, the face must be redone (see myRebil)
bool uclosed = mySurf->IsUClosed();
bool vclosed = mySurf->IsVClosed();
double SUF, SUL, SVF, SVL;
mySurf->Bounds(SUF, SUL, SVF, SVL);
double uRange = SUL - SUF;
double vRange = SVL - SVF;
NCollection_DataMap<TopoDS_Shape, NCollection_List<TopoDS_Shape>, TopTools_ShapeMapHasher> MW;
NCollection_DataMap<TopoDS_Shape, int, TopTools_ShapeMapHasher> SI;
NCollection_Map<TopoDS_Shape, TopTools_ShapeMapHasher> MapIntWires;
MW.Clear();
SI.Clear();
MapIntWires.Clear();
int i;
NCollection_Array1<Bnd_Box2d> aWireBoxes(1, nb);
double uMiddle = 0, vMiddle = 0;
bool isFirst = true;
// create Bounding boxes for each wire
for (i = 1; i <= nb; i++)
{
TopoDS_Shape aShape = ws.Value(i);
TopoDS_Wire aWire = TopoDS::Wire(aShape);
Bnd_Box2d aBox;
double cf, cl;
TopoDS_Iterator ew(aWire);
for (; ew.More(); ew.Next())
{
TopoDS_Edge ed = TopoDS::Edge(ew.Value());
occ::handle<Geom2d_Curve> cw = BRep_Tool::CurveOnSurface(ed, myFace, cf, cl);
if (cw.IsNull())
{
continue;
}
Geom2dAdaptor_Curve gac;
double aFirst = cw->FirstParameter();
double aLast = cw->LastParameter();
if (cw->IsKind(STANDARD_TYPE(Geom2d_BSplineCurve)) && (cf < aFirst || cl > aLast))
{
// avoiding problems with segment in Bnd_Box
gac.Load(cw);
}
else
{
gac.Load(cw, cf, cl);
}
BndLib_Add2dCurve::Add(gac, ::Precision::Confusion(), aBox);
}
double aXMin, aXMax, aYMin, aYMax;
aBox.Get(aXMin, aYMin, aXMax, aYMax);
if (isFirst)
{
isFirst = false;
uMiddle = (aXMin + aXMax) * 0.5;
vMiddle = (aYMin + aYMax) * 0.5;
}
else
{
double xShift = 0, yShift = 0;
if (mySurf->IsUClosed())
{
xShift = ShapeAnalysis::AdjustByPeriod(0.5 * (aXMin + aXMax), uMiddle, uRange);
}
if (mySurf->IsVClosed())
{
yShift = ShapeAnalysis::AdjustByPeriod(0.5 * (aYMin + aYMax), vMiddle, vRange);
}
aBox.Update(aXMin + xShift, aYMin + yShift, aXMax + xShift, aYMax + yShift);
}
aWireBoxes.ChangeValue(i) = aBox;
}
for (i = 1; i <= nb; i++)
{
TopoDS_Shape asw = ws.Value(i);
TopoDS_Wire aw = TopoDS::Wire(asw);
Bnd_Box2d aBox1 = aWireBoxes.Value(i);
TopoDS_Shape dummy = myFace.EmptyCopied();
TopoDS_Face af = TopoDS::Face(dummy);
af.Orientation(TopAbs_FORWARD);
B.Add(af, aw);
// PTV OCC945 06.11.2002 files ie_exhaust-A.stp (entities 3782, 3787)
// tolerance is too big. It is seems that to identify placement of 2d point
// it is enough Precision::PConfusion(), cause wea re know that 2d point in TopAbs_ON
// BRepTopAdaptor_FClass2d clas (af,toluv);
bool CheckShift = true;
BRepTopAdaptor_FClass2d clas(af, ::Precision::PConfusion());
TopAbs_State sta = TopAbs_OUT;
TopAbs_State staout = clas.PerformInfinitePoint();
NCollection_List<TopoDS_Shape> IntWires;
int aWireIt = 0;
for (int j = 1; j <= nbAll; j++)
{
aWireIt++;
// if(i==j) continue;
TopoDS_Shape aSh2 = allSubShapes.Value(j);
if (aw == aSh2)
{
continue;
}
TopAbs_State stb = TopAbs_UNKNOWN;
if (aSh2.ShapeType() == TopAbs_VERTEX)
{
aWireIt--;
gp_Pnt aP = BRep_Tool::Pnt(TopoDS::Vertex(aSh2));
gp_Pnt2d p2d = mySurf->ValueOfUV(aP, Precision::Confusion());
stb = clas.Perform(p2d, false);
if (stb == staout && (uclosed || vclosed))
{
gp_Pnt2d p2d1;
if (uclosed)
{
p2d1.SetCoord(p2d.X() + uRange, p2d.Y());
stb = clas.Perform(p2d1, false);
}
if (stb == staout && vclosed)
{
p2d1.SetCoord(p2d.X(), p2d.Y() + vRange);
stb = clas.Perform(p2d1, false);
}
}
}
else if (aSh2.ShapeType() == TopAbs_WIRE)
{
CheckShift = true;
TopoDS_Wire bw = TopoDS::Wire(aSh2);
// int numin =0;
Bnd_Box2d aBox2 = aWireBoxes.Value(aWireIt);
if (aBox2.IsOut(aBox1))
{
continue;
}
TopoDS_Iterator ew(bw);
for (; ew.More(); ew.Next())
{
TopoDS_Edge ed = TopoDS::Edge(ew.Value());
double cf, cl;
occ::handle<Geom2d_Curve> cw = BRep_Tool::CurveOnSurface(ed, myFace, cf, cl);
if (cw.IsNull())
{
continue;
}
gp_Pnt2d unp = cw->Value((cf + cl) / 2.);
TopAbs_State ste = clas.Perform(unp, false);
if (ste == TopAbs_OUT || ste == TopAbs_IN)
{
if (stb == TopAbs_UNKNOWN)
{
stb = ste;
}
else
{
if (!(stb == ste))
{
sta = TopAbs_UNKNOWN;
SI.Bind(aw, 0);
j = nbAll;
break;
}
}
}
bool found = false;
gp_Pnt2d unp1;
if (stb == staout && CheckShift)
{
CheckShift = false;
if (uclosed)
{
unp1.SetCoord(unp.X() + uRange, unp.Y());
found = (staout != clas.Perform(unp1, false));
if (!found)
{
unp1.SetX(unp.X() - uRange);
found = (staout != clas.Perform(unp1, false));
}
}
if (vclosed && !found)
{
unp1.SetCoord(unp.X(), unp.Y() + vRange);
found = (staout != clas.Perform(unp1, false));
if (!found)
{
unp1.SetY(unp.Y() - vRange);
found = (staout != clas.Perform(unp1, false));
}
}
// Additional check of diagonal steps for toroidal surfaces
if (!found && uclosed && vclosed)
{
for (double dX = -1.0; dX <= 1.0 && !found; dX += 2.0)
{
for (double dY = -1.0; dY <= 1.0 && !found; dY += 2.0)
{
unp1.SetCoord(unp.X() + uRange * dX, unp.Y() + vRange * dY);
found = (staout != clas.Perform(unp1, false));
}
}
}
}
if (found)
{
if (stb == TopAbs_IN)
{
stb = TopAbs_OUT;
}
else
{
stb = TopAbs_IN;
}
Shift2dWire(bw, myFace, unp1.XY() - unp.XY(), mySurf);
}
}
}
if (stb == staout)
{
sta = TopAbs_IN;
}
else
{
IntWires.Append(aSh2);
MapIntWires.Add(aSh2);
}
}
if (sta == TopAbs_UNKNOWN)
{ // ERREUR
// clang-format off
SendWarning ( aw, Message_Msg ( "FixAdvFace.FixOrientation.MSG11" ) );// Cannot orient wire
// clang-format on
}
else
{
MW.Bind(aw, IntWires);
if (sta == TopAbs_OUT)
{
if (staout == TopAbs_IN)
{
// wire is OUT but InfinitePoint is IN => need to reverse
ShapeExtend_WireData sewd(aw);
sewd.Reverse(myFace);
ws.SetValue(i, sewd.Wire());
// clang-format off
SendWarning ( sewd.Wire(), Message_Msg ( "FixAdvFace.FixOrientation.MSG5" ) );// Wire on face was reversed
// clang-format on
aSeqReversed.Append(i);
done = true;
SI.Bind(ws.Value(i), 1);
MapWires.Bind(ws.Value(i), IntWires);
}
else
{
SI.Bind(aw, 1);
MapWires.Bind(aw, IntWires);
}
}
else
{
if (staout == TopAbs_OUT)
{
SI.Bind(aw, 2);
}
else
{
SI.Bind(aw, 3);
}
}
}
}
for (i = 1; i <= nb; i++)
{
TopoDS_Wire aw = TopoDS::Wire(ws.Value(i));
int tmpi = SI.Find(aw);
if (tmpi > 1)
{
if (!MapIntWires.Contains(aw))
{
const NCollection_List<TopoDS_Shape>& IW = MW.Find(aw);
if (tmpi == 3)
{
// wire is OUT but InfinitePoint is IN => need to reverse
ShapeExtend_WireData sewd(aw);
sewd.Reverse(myFace);
ws.SetValue(i, sewd.Wire());
// clang-format off
SendWarning ( sewd.Wire(), Message_Msg ( "FixAdvFace.FixOrientation.MSG5" ) );// Wire on face was reversed
// clang-format on
aSeqReversed.Append(i);
done = true;
MapWires.Bind(ws.Value(i), IW);
}
else
{
MapWires.Bind(aw, IW);
}
}
else
{
if (tmpi == 2)
{
// wire is IN but InfinitePoint is OUT => need to reverse
ShapeExtend_WireData sewd(aw);
sewd.Reverse(myFace);
ws.SetValue(i, sewd.Wire());
// clang-format off
SendWarning ( sewd.Wire(), Message_Msg ( "FixAdvFace.FixOrientation.MSG5" ) );// Wire on face was reversed
// clang-format on
aSeqReversed.Append(i);
done = true;
}
}
}
}
}
if (isAddNaturalBounds && nb == aSeqReversed.Length())
{
done = false;
}
// Should I rebuild? if myRebil is set
if (done)
{
TopoDS_Shape S = myFace.EmptyCopied();
S.Orientation(TopAbs_FORWARD);
int i = 1;
for (; i <= nb; i++)
{
B.Add(S, ws.Value(i));
}
if (nb < nbAll)
{
for (i = 1; i <= nbAll; i++)
{
const TopoDS_Shape& aS2 = allSubShapes.Value(i);
if (aS2.ShapeType() != TopAbs_WIRE
|| (aS2.Orientation() != TopAbs_FORWARD && aS2.Orientation() != TopAbs_REVERSED))
{
B.Add(S, aS2);
}
}
}
if (!myFwd)
{
S.Orientation(TopAbs_REVERSED);
}
if (!Context().IsNull())
{
Context()->Replace(myFace, S);
}
myFace = TopoDS::Face(S);
BRepTools::Update(myFace);
}
return done;
}
//=================================================================================================
//: i7 abv 18 Sep 98: ProSTEP TR9 r0501-ug.stp: algorithm of fixing missing seam changed
// test whether the wire is opened on period of periodical surface
static bool CheckWire(const TopoDS_Wire& wire,
const TopoDS_Face& face,
const double dU,
const double dV,
int& isuopen,
int& isvopen,
bool& isDeg)
{
gp_XY vec;
vec.SetX(0);
vec.SetY(0);
ShapeAnalysis_Edge sae;
isuopen = isvopen = 0;
isDeg = true;
for (TopoDS_Iterator ed(wire); ed.More(); ed.Next())
{
TopoDS_Edge edge = TopoDS::Edge(ed.Value());
if (!BRep_Tool::Degenerated(edge))
{
isDeg = false;
}
occ::handle<Geom2d_Curve> c2d;
double f, l;
if (!sae.PCurve(edge, face, c2d, f, l, true))
{
return false;
}
vec += c2d->Value(l).XY() - c2d->Value(f).XY();
}
double aDelta = std::abs(vec.X()) - dU;
if (std::abs(aDelta) < 0.1 * dU)
{
if (vec.X() > 0.0)
{
isuopen = 1;
}
else
{
isuopen = -1;
}
}
else
{
isuopen = 0;
}
aDelta = std::abs(vec.Y()) - dV;
if (std::abs(aDelta) < 0.1 * dV)
{
if (vec.Y() > 0.0)
{
isvopen = 1;
}
else
{
isvopen = -1;
}
}
else
{
isvopen = 0;
}
return isuopen || isvopen;
}
//=================================================================================================
bool ShapeFix_Face::FixMissingSeam()
{
if (mySurf.IsNull())
{
return false;
}
bool uclosed = mySurf->IsUClosed();
bool vclosed = mySurf->IsVClosed();
if (!uclosed && !vclosed)
{
return false;
}
if (!Context().IsNull())
{
TopoDS_Shape S = Context()->Apply(myFace);
myFace = TopoDS::Face(S);
}
//%pdn: surface should be made periodic before (see ShapeCustom_Surface)!
if (mySurf->Surface()->IsKind(STANDARD_TYPE(Geom_BSplineSurface)))
{
occ::handle<Geom_BSplineSurface> BSpl = occ::down_cast<Geom_BSplineSurface>(mySurf->Surface());
if (!BSpl->IsUPeriodic() && !BSpl->IsVPeriodic())
{
return false;
}
}
double URange, VRange, SUF, SUL, SVF, SVL;
mySurf->Bounds(SUF, SUL, SVF, SVL);
double fU1, fU2, fV1, fV2;
BRepTools::UVBounds(myFace, fU1, fU2, fV1, fV2);
// pdn OCC55 fix to faces without the wires to avoid identical first and last parameters
if (::Precision::IsInfinite(SUF) || ::Precision::IsInfinite(SUL))
{
if (::Precision::IsInfinite(SUF))
{
SUF = fU1;
}
if (::Precision::IsInfinite(SUL))
{
SUL = fU2;
}
if (std::abs(SUL - SUF) < ::Precision::PConfusion())
{
if (::Precision::IsInfinite(SUF))
{
SUF -= 1000.;
}
else
{
SUL += 1000.;
}
}
}
if (::Precision::IsInfinite(SVF) || ::Precision::IsInfinite(SVL))
{
if (::Precision::IsInfinite(SVF))
{
SVF = fV1;
}
if (::Precision::IsInfinite(SVL))
{
SVL = fV2;
}
if (std::abs(SVL - SVF) < ::Precision::PConfusion())
{
if (::Precision::IsInfinite(SVF))
{
SVF -= 1000.;
}
else
{
SVL += 1000.;
}
}
}
URange = std::min(std::abs(SUL - SUF), Precision::Infinite());
VRange = std::min(std::abs(SVL - SVF), Precision::Infinite());
// double UTol = 0.2 * URange, VTol = 0.2 * VRange;
int ismodeu = 0, ismodev = 0; // szv#4:S4163:12Mar99 was Boolean
int isdeg1 = 0, isdeg2 = 0;
NCollection_Sequence<TopoDS_Shape> ws;
NCollection_Sequence<TopoDS_Shape> aSeqNonManif;
for (TopoDS_Iterator wi(myFace, false); wi.More(); wi.Next())
{
if (wi.Value().ShapeType() != TopAbs_WIRE
|| (wi.Value().Orientation() != TopAbs_FORWARD
&& wi.Value().Orientation() != TopAbs_REVERSED))
{
aSeqNonManif.Append(wi.Value());
continue;
}
ws.Append(wi.Value());
}
TopoDS_Wire w1, w2;
int i;
for (i = 1; i <= ws.Length(); i++)
{
TopoDS_Wire wire = TopoDS::Wire(ws.Value(i));
int isuopen, isvopen;
bool isdeg;
if (!CheckWire(wire, myFace, URange, VRange, isuopen, isvopen, isdeg))
{
continue;
}
if (w1.IsNull())
{
w1 = wire;
ismodeu = isuopen;
ismodev = isvopen;
isdeg1 = isdeg ? i : 0;
}
else if (w2.IsNull())
{
if (ismodeu == -isuopen && ismodev == -isvopen)
{
w2 = wire;
isdeg2 = isdeg ? i : 0;
}
else if (ismodeu == isuopen && ismodev == isvopen)
{
w2 = wire;
isdeg2 = isdeg;
//: abv 29.08.01: If wires are contraversal, reverse one of them
// If first one is single degenerated edge, reverse it; else second
if (isdeg1)
{
w1.Reverse();
ismodeu = -ismodeu;
ismodev = -ismodev;
}
else
{
w2.Reverse();
}
}
}
// else return false; // abort
else
{
//: abv 30.08.09: if more than one open wires and more than two of them are
// completely degenerated, remove any of them
if (isdeg || isdeg1 || isdeg2)
{
ws.Remove(isdeg ? i : isdeg2 ? isdeg2 : isdeg1);
w1.Nullify();
w2.Nullify();
i = 0;
continue;
}
}
}
BRep_Builder B;
occ::handle<Geom_ToroidalSurface> aTorSurf =
occ::down_cast<Geom_ToroidalSurface>(mySurf->Surface());
bool anIsDegeneratedTor =
(aTorSurf.IsNull() ? false : aTorSurf->MajorRadius() < aTorSurf->MinorRadius());
// if the second wire is not null, we don't need mark the torus as degenerated
// and should process it as a regular one.
if (anIsDegeneratedTor && !w2.IsNull())
{
anIsDegeneratedTor = false;
}
if (w1.IsNull())
{
return false;
}
else if (w2.IsNull())
{
// For spheres and BSpline cone-like surfaces(bug 24055):
// If only one of wires limiting face on surface is open in 2d,
// this may means that degenerated edge should be added, and
// then usual procedure applied
gp_Pnt2d p;
gp_Dir2d d;
double aRange;
if (ismodeu && anIsDegeneratedTor)
{
double aRa = aTorSurf->MajorRadius();
double aRi = aTorSurf->MinorRadius();
double aPhi = std::acos(-aRa / aRi);
p.SetCoord(0.0, (ismodeu > 0 ? M_PI + aPhi : aPhi));
double aXCoord = -ismodeu;
d.SetCoord(aXCoord, 0.);
aRange = 2. * M_PI;
}
else if (ismodeu && mySurf->Surface()->IsKind(STANDARD_TYPE(Geom_SphericalSurface)))
{
p.SetCoord((ismodeu < 0 ? 0. : 2. * M_PI), ismodeu * 0.5 * M_PI);
double aXCoord = -ismodeu;
d.SetCoord(aXCoord, 0.);
aRange = 2. * M_PI;
}
else if (ismodev && mySurf->Surface()->IsKind(STANDARD_TYPE(Geom_BSplineSurface)))
{
double uCoord;
if (mySurf->Value(SUF, SVF).Distance(mySurf->Value(SUF, (SVF + SVL) / 2))
< ::Precision::Confusion())
{
uCoord = SUF;
}
else if (mySurf->Value(SUL, SVF).Distance(mySurf->Value(SUL, (SVF + SVL) / 2))
< ::Precision::Confusion())
{
uCoord = SUL;
}
else
{
return false;
}
p.SetCoord(uCoord, (ismodev < 0 ? 0. : VRange));
d.SetCoord(0., -ismodev);
aRange = VRange;
}
else if (ismodeu && mySurf->Surface()->IsKind(STANDARD_TYPE(Geom_BSplineSurface)))
{
double vCoord;
if (mySurf->Value(SUF, SVF).Distance(mySurf->Value((SUF + SUL) / 2, SVF))
< ::Precision::Confusion())
{
vCoord = SVF;
}
else if (mySurf->Value(SUL, SVL).Distance(mySurf->Value((SUF + SUL) / 2, SVL))
< ::Precision::Confusion())
{
vCoord = SVL;
}
else
{
return false;
}
p.SetCoord((ismodeu < 0 ? 0. : URange), vCoord);
double aXCoord = -ismodeu;
d.SetCoord(aXCoord, 0.);
aRange = URange;
}
else
{
return false;
}
occ::handle<Geom2d_Line> line = new Geom2d_Line(p, d);
TopoDS_Edge edge;
B.MakeEdge(edge);
B.Degenerated(edge, true);
B.UpdateEdge(edge, line, myFace, ::Precision::Confusion());
B.Range(edge, myFace, 0., aRange);
TopoDS_Vertex V;
B.MakeVertex(V, mySurf->Value(p.X(), p.Y()), ::Precision::Confusion());
V.Orientation(TopAbs_FORWARD);
B.Add(edge, V);
V.Orientation(TopAbs_REVERSED);
B.Add(edge, V);
B.MakeWire(w2);
B.Add(w2, edge);
ws.Append(w2);
}
// Check consistency of orientations of the two wires that need to be connected by a seam
double uf = SUF, vf = SVF;
int coord = (ismodeu ? 1 : 0);
int isneg = (ismodeu ? ismodeu : -ismodev);
double period = (ismodeu ? URange : VRange);
TopoDS_Shape S;
double m1[2][2], m2[2][2];
S = myFace.EmptyCopied();
B.Add(S, w1);
ShapeAnalysis::GetFaceUVBounds(TopoDS::Face(S), m1[0][0], m1[0][1], m1[1][0], m1[1][1]);
S = myFace.EmptyCopied();
B.Add(S, w2);
ShapeAnalysis::GetFaceUVBounds(TopoDS::Face(S), m2[0][0], m2[0][1], m2[1][0], m2[1][1]);
// For the case when surface is closed only in one direction it is necessary to check
// validity of orientation of the open wires in parametric space.
// In case of U closed surface wire with minimal V coordinate should be directed in positive
// direction by U In case of V closed surface wire with minimal U coordinate should be directed in
// negative direction by V
if (!vclosed || !uclosed || anIsDegeneratedTor)
{
double deltaOther = 0.5 * (m2[coord][0] + m2[coord][1]) - 0.5 * (m1[coord][0] + m1[coord][1]);
if (deltaOther * isneg < 0)
{
w1.Reverse();
w2.Reverse();
}
}
// sort original wires
occ::handle<ShapeFix_Wire> sfw = new ShapeFix_Wire;
sfw->SetFace(myFace, mySurf);
sfw->SetPrecision(Precision());
occ::handle<ShapeExtend_WireData> wd1 = new ShapeExtend_WireData(w1);
occ::handle<ShapeExtend_WireData> wd2 = new ShapeExtend_WireData(w2);
sfw->Load(wd1);
sfw->FixReorder();
sfw->Load(wd2);
sfw->FixReorder();
TopoDS_Wire w11 = wd1->Wire();
TopoDS_Wire w21 = wd2->Wire();
//: abv 29.08.01: reconstruct face taking into account reversing
TopoDS_Shape dummy = myFace.EmptyCopied();
TopoDS_Face tmpF = TopoDS::Face(dummy);
tmpF.Orientation(TopAbs_FORWARD);
for (i = 1; i <= ws.Length(); i++)
{
TopoDS_Wire wire = TopoDS::Wire(ws.Value(i));
if (wire.IsSame(w1))
{
wire = w11;
}
else if (wire.IsSame(w2))
{
wire = w21;
}
else
{
// other wires (not boundary) are considered as holes; make sure to have them oriented
// accordingly
TopoDS_Shape curface = tmpF.EmptyCopied();
B.Add(curface, wire);
curface.Orientation(myFace.Orientation());
if (ShapeAnalysis::IsOuterBound(TopoDS::Face(curface)))
{
wire.Reverse();
}
}
B.Add(tmpF, wire);
}
tmpF.Orientation(myFace.Orientation());
// A special kind of FixShifted is necessary for torus-like
// surfaces to adjust wires by period ALONG the missing SEAM direction
// tr9_r0501-ug.stp #187640
if (uclosed && vclosed && !anIsDegeneratedTor)
{
double shiftw2 = ShapeAnalysis::AdjustByPeriod(
0.5 * (m2[coord][0] + m2[coord][1]),
0.5 * (m1[coord][0] + m1[coord][1] + isneg * (period + ::Precision::PConfusion())),
period);
m1[coord][0] = std::min(m1[coord][0], m2[coord][0] + shiftw2);
m1[coord][1] = std::max(m1[coord][1], m2[coord][1] + shiftw2);
for (TopoDS_Iterator it(tmpF, false); it.More(); it.Next())
{
if (it.Value().ShapeType() != TopAbs_WIRE)
{
continue;
}
TopoDS_Wire w = TopoDS::Wire(it.Value());
if (w == w11)
{
continue;
}
double shift;
if (w == w21)
{
shift = shiftw2;
}
else
{
S = tmpF.EmptyCopied();
B.Add(S, w);
ShapeAnalysis::GetFaceUVBounds(TopoDS::Face(S), m2[0][0], m2[0][1], m2[1][0], m2[1][1]);
shift = ShapeAnalysis::AdjustByPeriod(0.5 * (m2[coord][0] + m2[coord][1]),
0.5 * (m1[coord][0] + m1[coord][1]),
period);
}
if (shift != 0.)
{
gp_Vec2d V(0., 0.);
V.SetCoord(coord + 1, shift);
ShapeAnalysis_Edge sae;
for (TopoDS_Iterator iw(w); iw.More(); iw.Next())
{
TopoDS_Edge E = TopoDS::Edge(iw.Value());
occ::handle<Geom2d_Curve> C;
double a, b;
if (!sae.PCurve(E, tmpF, C, a, b))
{
continue;
}
C->Translate(V);
}
}
}
// abv 05 Feb 02: OCC34
// by the way, select proper split place by V to avoid extra intersections
if (m1[coord][1] - m1[coord][0] <= period)
{
double other = 0.5 * (m1[coord][0] + m1[coord][1] - period);
if (ismodeu)
{
vf = other;
}
else
{
uf = other;
}
}
}
// find the best place by u and v to insert a seam
// (so as to minimize splitting edges as possible)
ShapeAnalysis_Edge sae;
int foundU = 0, foundV = 0;
int nb1 = wd1->NbEdges();
int nb2 = wd2->NbEdges();
for (int i1 = 1; i1 <= nb1 + nb2; i1++)
{
TopoDS_Edge edge1 = (i1 <= nb1 ? wd1->Edge(i1) : wd2->Edge(i1 - nb1));
occ::handle<Geom2d_Curve> c2d;
double f, l;
if (!sae.PCurve(edge1, tmpF, c2d, f, l, true))
{
return false;
}
gp_Pnt2d pos1 = c2d->Value(l).XY();
// the best place is end of edge which is nearest to 0
bool skipU = !uclosed;
if (uclosed && ismodeu)
{
pos1.SetX(pos1.X() + ShapeAnalysis::AdjustByPeriod(pos1.X(), SUF, URange));
if (foundU == 2 && std::abs(pos1.X()) > std::abs(uf))
{
skipU = true;
}
else if (!foundU || (foundU == 1 && std::abs(pos1.X()) < std::abs(uf)))
{
foundU = 1;
uf = pos1.X();
}
}
bool skipV = !vclosed;
if (vclosed && !ismodeu)
{
pos1.SetY(pos1.Y() + ShapeAnalysis::AdjustByPeriod(pos1.Y(), SVF, VRange));
if (foundV == 2 && std::abs(pos1.Y()) > std::abs(vf))
{
skipV = true;
}
else if (!foundV || (foundV == 1 && std::abs(pos1.Y()) < std::abs(vf)))
{
foundV = 1;
vf = pos1.Y();
}
}
if (skipU && skipV)
{
if (i1 <= nb1)
{
continue;
}
else
{
break;
}
}
// or yet better - if it is end of some edges on both wires
for (int i2 = 1; i1 <= nb1 && i2 <= nb2; i2++)
{
TopoDS_Edge edge2 = wd2->Edge(i2);
if (!sae.PCurve(edge2, tmpF, c2d, f, l, true))
{
return false;
}
gp_Pnt2d pos2 = c2d->Value(f).XY();
if (uclosed && ismodeu)
{
pos2.SetX(pos2.X() + ShapeAnalysis::AdjustByPeriod(pos2.X(), pos1.X(), URange));
if (std::abs(pos2.X() - pos1.X()) < ::Precision::PConfusion()
&& (foundU != 2 || std::abs(pos1.X()) < std::abs(uf)))
{
foundU = 2;
uf = pos1.X();
}
}
if (vclosed && !ismodeu)
{
pos2.SetY(pos2.Y() + ShapeAnalysis::AdjustByPeriod(pos2.Y(), pos1.Y(), VRange));
if (std::abs(pos2.Y() - pos1.Y()) < ::Precision::PConfusion()
&& (foundV != 2 || std::abs(pos1.Y()) < std::abs(vf)))
{
foundV = 2;
vf = pos1.Y();
}
}
}
}
// pdn fixing RTS on offsets
if (uf < SUF || uf > SUL)
{
uf += ShapeAnalysis::AdjustToPeriod(uf, SUF, SUF + URange);
}
if (vf < SVF || vf > SVL)
{
vf += ShapeAnalysis::AdjustToPeriod(vf, SVF, SVF + VRange);
}
// Create fictive grid and call ComposeShell to insert a seam
occ::handle<Geom_RectangularTrimmedSurface> RTS =
new Geom_RectangularTrimmedSurface(mySurf->Surface(), uf, uf + URange, vf, vf + VRange);
occ::handle<NCollection_HArray2<occ::handle<Geom_Surface>>> grid =
new NCollection_HArray2<occ::handle<Geom_Surface>>(1, 1, 1, 1);
grid->SetValue(1, 1, RTS); // mySurf->Surface() );
occ::handle<ShapeExtend_CompositeSurface> G = new ShapeExtend_CompositeSurface(grid);
TopLoc_Location L;
// addition non-manifold topology
int j = 1;
for (; j <= aSeqNonManif.Length(); j++)
{
B.Add(tmpF, aSeqNonManif.Value(j));
}
ShapeFix_ComposeShell CompShell;
CompShell.Init(G, L, tmpF, ::Precision::Confusion()); // myPrecision
if (Context().IsNull())
{
SetContext(new ShapeBuild_ReShape);
}
CompShell.ClosedMode() = true;
CompShell.SetContext(Context());
CompShell.SetMaxTolerance(MaxTolerance());
CompShell.Perform();
// abv 03.07.00: CAX-IF TRJ4: trj4_k1_goe-tu-214.stp: #785: reset mySurf
mySurf = new ShapeAnalysis_Surface(RTS);
myResult = CompShell.Result();
Context()->Replace(myFace, myResult);
// Remove small wires and / or faces that can be generated by ComposeShell
// (see tests bugs step bug30052_4, de step_3 E6)
int nbFaces = 0;
TopExp_Explorer expF(myResult, TopAbs_FACE);
for (; expF.More(); expF.Next())
{
TopoDS_Face aFace = TopoDS::Face(expF.Value());
TopExp_Explorer aExpW(aFace, TopAbs_WIRE);
int nbWires = 0;
for (; aExpW.More(); aExpW.Next())
{
ShapeFix_Wire aSfw(TopoDS::Wire(aExpW.Value()), aFace, Precision());
aSfw.SetContext(Context());
if (aSfw.NbEdges())
{
aSfw.FixSmall(true, Precision());
}
if (!aSfw.NbEdges())
{
Context()->Remove(aExpW.Value());
continue;
}
nbWires++;
}
if (!nbWires)
{
Context()->Remove(aFace);
continue;
}
nbFaces++;
}
myResult = Context()->Apply(myResult);
for (TopExp_Explorer exp(myResult, TopAbs_FACE); exp.More(); exp.Next())
{
myFace = TopoDS::Face(Context()->Apply(exp.Current()));
if (myFace.IsNull())
{
continue;
}
if (nbFaces > 1)
{
FixSmallAreaWire(true);
TopoDS_Shape aShape = Context()->Apply(myFace);
if (aShape.IsNull())
{
continue;
}
myFace = TopoDS::Face(aShape);
}
BRepTools::Update(myFace); //: p4
}
myResult = Context()->Apply(myResult);
SendWarning(Message_Msg("FixAdvFace.FixMissingSeam.MSG0")); // Missing seam-edge added
return true;
}
//=================================================================================================
//%14 pdn 24.02.99 PRO10109, USA60293 fix wire on face with small area.
bool ShapeFix_Face::FixSmallAreaWire(const bool theIsRemoveSmallFace)
{
if (!Context().IsNull())
{
TopoDS_Shape aShape = Context()->Apply(myFace);
myFace = TopoDS::Face(aShape);
}
BRep_Builder aBuilder;
int nbRemoved = 0, nbWires = 0;
TopoDS_Shape anEmptyCopy = myFace.EmptyCopied();
TopoDS_Face aFace = TopoDS::Face(anEmptyCopy);
aFace.Orientation(TopAbs_FORWARD);
const double aTolerance3d = ShapeFix_Root::Precision();
for (TopoDS_Iterator aWIt(myFace, false); aWIt.More(); aWIt.Next())
{
const TopoDS_Shape& aShape = aWIt.Value();
if (aShape.ShapeType() != TopAbs_WIRE && aShape.Orientation() != TopAbs_FORWARD
&& aShape.Orientation() != TopAbs_REVERSED)
{
continue;
}
const TopoDS_Wire& aWire = TopoDS::Wire(aShape);
occ::handle<ShapeAnalysis_Wire> anAnalyzer =
new ShapeAnalysis_Wire(aWire, myFace, aTolerance3d);
if (anAnalyzer->CheckSmallArea(aWire))
{
// Null area wire detected, wire skipped
SendWarning(aWire, Message_Msg("FixAdvFace.FixSmallAreaWire.MSG0"));
++nbRemoved;
}
else
{
aBuilder.Add(aFace, aWire);
++nbWires;
}
}
if (nbRemoved <= 0)
{
return false;
}
if (nbWires <= 0)
{
if (theIsRemoveSmallFace && !Context().IsNull())
{
Context()->Remove(myFace);
}
return false;
}
aFace.Orientation(myFace.Orientation());
if (!Context().IsNull())
{
Context()->Replace(myFace, aFace);
}
myFace = aFace;
return true;
}
//=================================================================================================
static void FindNext(
const TopoDS_Shape& aVert,
const TopoDS_Shape& ainitEdge,
NCollection_IndexedMap<TopoDS_Shape, TopTools_ShapeMapHasher>& aMapVertices,
NCollection_DataMap<TopoDS_Shape, NCollection_List<TopoDS_Shape>, TopTools_ShapeMapHasher>&
aMapVertexEdges,
const NCollection_Map<TopoDS_Shape, TopTools_ShapeMapHasher>& aMapSmallEdges,
const NCollection_Map<TopoDS_Shape, TopTools_ShapeMapHasher>& aMapSeemEdges,
NCollection_Map<TopoDS_Shape, TopTools_ShapeMapHasher>& aMapEdges,
occ::handle<ShapeExtend_WireData>& aWireData)
{
TopoDS_Iterator aItV(ainitEdge);
TopoDS_Shape anextVert = aVert;
bool isFind = false;
for (; aItV.More() && !isFind; aItV.Next())
{
if (!aItV.Value().IsSame(aVert))
{
isFind = true;
anextVert = aItV.Value();
}
}
if (!isFind && !aMapSmallEdges.Contains(ainitEdge))
{
return;
}
if (isFind && aMapVertices.Contains(anextVert))
{
return;
}
const NCollection_List<TopoDS_Shape>& aledges = aMapVertexEdges.Find(anextVert);
NCollection_List<TopoDS_Shape>::Iterator liter(aledges);
isFind = false;
TopoDS_Shape anextEdge;
for (; liter.More() && !isFind; liter.Next())
{
if (!aMapEdges.Contains(liter.Value()) && !liter.Value().IsSame(ainitEdge))
{
anextEdge = liter.Value();
aWireData->Add(anextEdge);
if (aMapSeemEdges.Contains(anextEdge))
{
aWireData->Add(anextEdge.Reversed());
}
isFind = true;
aMapEdges.Add(anextEdge);
FindNext(anextVert,
anextEdge,
aMapVertices,
aMapVertexEdges,
aMapSmallEdges,
aMapSeemEdges,
aMapEdges,
aWireData);
}
}
}
static bool isClosed2D(const TopoDS_Face& aFace, const TopoDS_Wire& aWire)
{
bool isClosed = true;
occ::handle<ShapeAnalysis_Wire> asaw =
new ShapeAnalysis_Wire(aWire, aFace, Precision::Confusion());
for (int i = 1; i <= asaw->NbEdges() && isClosed; i++)
{
TopoDS_Edge edge1 = asaw->WireData()->Edge(i);
// checking that wire is closed in 2D space with tolerance of vertex.
ShapeAnalysis_Edge sae;
TopoDS_Vertex v1 = sae.FirstVertex(edge1);
asaw->SetPrecision(BRep_Tool::Tolerance(v1));
asaw->CheckGap2d(i);
isClosed = (asaw->LastCheckStatus(ShapeExtend_OK));
}
return isClosed;
}
//=================================================================================================
bool ShapeFix_Face::FixLoopWire(NCollection_Sequence<TopoDS_Shape>& aResWires)
{
NCollection_IndexedMap<TopoDS_Shape, TopTools_ShapeMapHasher> aMapVertices;
NCollection_DataMap<TopoDS_Shape, NCollection_List<TopoDS_Shape>, TopTools_ShapeMapHasher>
aMapVertexEdges;
NCollection_Map<TopoDS_Shape, TopTools_ShapeMapHasher> aMapSmallEdges;
NCollection_Map<TopoDS_Shape, TopTools_ShapeMapHasher> aMapSeemEdges;
if (!FixWireTool()->Analyzer()->CheckLoop(aMapVertices,
aMapVertexEdges,
aMapSmallEdges,
aMapSeemEdges))
{
return false;
}
NCollection_Map<TopoDS_Shape, TopTools_ShapeMapHasher> aMapEdges;
NCollection_Sequence<TopoDS_Shape> aSeqWires;
// collecting wires from common vertex belonging more than 2 edges
int i = 1;
for (; i <= aMapVertices.Extent(); i++)
{
TopoDS_Shape aVert = aMapVertices.FindKey(i);
const NCollection_List<TopoDS_Shape>& aledges = aMapVertexEdges.Find(aVert);
NCollection_List<TopoDS_Shape>::Iterator liter(aledges);
for (; liter.More(); liter.Next())
{
TopoDS_Edge Edge = TopoDS::Edge(liter.Value());
if (aMapEdges.Contains(Edge))
{
continue;
}
occ::handle<ShapeExtend_WireData> aWireData = new ShapeExtend_WireData;
aWireData->Add(Edge);
if (aMapSeemEdges.Contains(Edge))
{
aWireData->Add(Edge.Reversed());
}
aMapEdges.Add(Edge);
FindNext(aVert,
Edge,
aMapVertices,
aMapVertexEdges,
aMapSmallEdges,
aMapSeemEdges,
aMapEdges,
aWireData);
if (aWireData->NbEdges() == 1 && aMapSmallEdges.Contains(aWireData->Edge(1)))
{
continue;
}
TopoDS_Vertex aV1, aV2;
TopoDS_Wire aWire = aWireData->Wire();
TopExp::Vertices(aWire, aV1, aV2);
if (aV1.IsSame(aV2))
{
occ::handle<ShapeExtend_WireData> asewd = new ShapeExtend_WireData(aWire);
occ::handle<ShapeFix_Wire> asfw = new ShapeFix_Wire;
asfw->Load(asewd);
asfw->FixReorder();
TopoDS_Wire awire2 = asfw->Wire();
aResWires.Append(awire2);
}
else
{
aSeqWires.Append(aWireData->Wire());
}
}
}
if (aSeqWires.Length() == 1)
{
aResWires.Append(aSeqWires.Value(1));
}
else
{
// collecting whole wire from two not closed wires having two common vertices.
for (i = 1; i <= aSeqWires.Length(); i++)
{
TopoDS_Vertex aV1, aV2;
TopoDS_Wire aWire = TopoDS::Wire(aSeqWires.Value(i));
TopExp::Vertices(aWire, aV1, aV2);
int j = i + 1;
for (; j <= aSeqWires.Length(); j++)
{
TopoDS_Vertex aV21, aV22;
TopoDS_Wire aWire2 = TopoDS::Wire(aSeqWires.Value(j));
TopExp::Vertices(aWire2, aV21, aV22);
if ((aV1.IsSame(aV21) || aV1.IsSame(aV22)) && (aV2.IsSame(aV21) || aV2.IsSame(aV22)))
{
occ::handle<ShapeExtend_WireData> asewd = new ShapeExtend_WireData(aWire);
asewd->Add(aWire2);
occ::handle<ShapeFix_Wire> asfw = new ShapeFix_Wire;
asfw->Load(asewd);
asfw->FixReorder();
aResWires.Append(asfw->Wire());
aSeqWires.Remove(j--);
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE7);
break;
}
}
if (j <= aSeqWires.Length())
{
aSeqWires.Remove(i--);
}
}
if (aSeqWires.Length() < 3)
{
for (i = 1; i <= aSeqWires.Length(); i++)
{
aResWires.Append(aSeqWires.Value(i));
}
}
else
{
// collecting wires having one common vertex
for (i = 1; i <= aSeqWires.Length(); i++)
{
TopoDS_Vertex aV1, aV2;
TopoDS_Wire aWire = TopoDS::Wire(aSeqWires.Value(i));
TopExp::Vertices(aWire, aV1, aV2);
int j = i + 1;
for (; j <= aSeqWires.Length(); j++)
{
TopoDS_Vertex aV21, aV22;
TopoDS_Wire aWire2 = TopoDS::Wire(aSeqWires.Value(j));
TopExp::Vertices(aWire2, aV21, aV22);
if ((aV1.IsSame(aV21) || aV1.IsSame(aV22)) || (aV2.IsSame(aV21) || aV2.IsSame(aV22)))
{
occ::handle<ShapeExtend_WireData> asewd = new ShapeExtend_WireData(aWire);
asewd->Add(aWire2);
occ::handle<ShapeFix_Wire> asfw = new ShapeFix_Wire;
asfw->Load(asewd);
asfw->FixReorder();
aWire = asfw->Wire();
TopExp::Vertices(aWire, aV1, aV2);
aSeqWires.Remove(j--);
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE7);
}
}
aResWires.Append(aWire);
}
}
}
bool isClosed = true;
// checking that obtained wires is closed in 2D space
if (mySurf && mySurf->Adaptor3d()->GetType() != GeomAbs_Plane)
{
TopoDS_Shape emptyCopied = myFace.EmptyCopied();
TopoDS_Face tmpFace = TopoDS::Face(emptyCopied);
tmpFace.Orientation(TopAbs_FORWARD);
for (i = 1; i <= aResWires.Length() && isClosed; i++)
{
TopoDS_Wire awire = TopoDS::Wire(aResWires.Value(i));
isClosed = isClosed2D(tmpFace, awire);
}
}
return !aResWires.IsEmpty() && isClosed;
}
//=================================================================================================
bool ShapeFix_Face::SplitEdge(
const occ::handle<ShapeExtend_WireData>& sewd,
const int num,
const double param,
const TopoDS_Vertex& vert,
const double preci,
NCollection_DataMap<TopoDS_Shape, Bnd_Box2d, TopTools_ShapeMapHasher>& boxes)
{
TopoDS_Edge edge = sewd->Edge(num);
TopoDS_Edge newE1, newE2;
ShapeFix_SplitTool aTool;
if (aTool.SplitEdge(edge, param, vert, myFace, newE1, newE2, preci, 0.01 * preci))
{
// change context
occ::handle<ShapeExtend_WireData> wd = new ShapeExtend_WireData;
wd->Add(newE1);
wd->Add(newE2);
if (!Context().IsNull())
{
Context()->Replace(edge, wd->Wire());
}
for (TopExp_Explorer exp(wd->Wire(), TopAbs_EDGE); exp.More(); exp.Next())
{
TopoDS_Edge E = TopoDS::Edge(exp.Current());
BRepTools::Update(E);
}
// change sewd and boxes
sewd->Set(newE1, num);
if (num == sewd->NbEdges())
{
sewd->Add(newE2);
}
else
{
sewd->Add(newE2, num + 1);
}
boxes.UnBind(edge);
TopLoc_Location L;
const occ::handle<Geom_Surface>& S = BRep_Tool::Surface(myFace, L);
occ::handle<Geom2d_Curve> c2d;
double cf, cl;
ShapeAnalysis_Edge sae;
if (sae.PCurve(newE1, S, L, c2d, cf, cl, false))
{
Bnd_Box2d box;
Geom2dAdaptor_Curve gac;
double aFirst = c2d->FirstParameter();
double aLast = c2d->LastParameter();
if (c2d->IsKind(STANDARD_TYPE(Geom2d_BSplineCurve)) && (cf < aFirst || cl > aLast))
{
// pdn avoiding problems with segment in Bnd_Box
gac.Load(c2d);
}
else
{
gac.Load(c2d, cf, cl);
}
BndLib_Add2dCurve::Add(gac, ::Precision::Confusion(), box);
boxes.Bind(newE1, box);
}
if (sae.PCurve(newE2, S, L, c2d, cf, cl, false))
{
Bnd_Box2d box;
Geom2dAdaptor_Curve gac;
double aFirst = c2d->FirstParameter();
double aLast = c2d->LastParameter();
if (c2d->IsKind(STANDARD_TYPE(Geom2d_BSplineCurve)) && (cf < aFirst || cl > aLast))
{
// pdn avoiding problems with segment in Bnd_Box
gac.Load(c2d);
}
else
{
gac.Load(c2d, cf, cl);
}
BndLib_Add2dCurve::Add(gac, ::Precision::Confusion(), box);
boxes.Bind(newE2, box);
}
return true;
}
return false;
}
//=================================================================================================
bool ShapeFix_Face::SplitEdge(
const occ::handle<ShapeExtend_WireData>& sewd,
const int num,
const double param1,
const double param2,
const TopoDS_Vertex& vert,
const double preci,
NCollection_DataMap<TopoDS_Shape, Bnd_Box2d, TopTools_ShapeMapHasher>& boxes)
{
TopoDS_Edge edge = sewd->Edge(num);
TopoDS_Edge newE1, newE2;
ShapeFix_SplitTool aTool;
if (aTool.SplitEdge(edge, param1, param2, vert, myFace, newE1, newE2, preci, 0.01 * preci))
{
// change context
occ::handle<ShapeExtend_WireData> wd = new ShapeExtend_WireData;
wd->Add(newE1);
wd->Add(newE2);
if (!Context().IsNull())
{
Context()->Replace(edge, wd->Wire());
}
for (TopExp_Explorer exp(wd->Wire(), TopAbs_EDGE); exp.More(); exp.Next())
{
TopoDS_Edge E = TopoDS::Edge(exp.Current());
BRepTools::Update(E);
}
// change sewd and boxes
sewd->Set(newE1, num);
if (num == sewd->NbEdges())
{
sewd->Add(newE2);
}
else
{
sewd->Add(newE2, num + 1);
}
boxes.UnBind(edge);
TopLoc_Location L;
const occ::handle<Geom_Surface>& S = BRep_Tool::Surface(myFace, L);
occ::handle<Geom2d_Curve> c2d;
double cf, cl;
ShapeAnalysis_Edge sae;
if (sae.PCurve(newE1, S, L, c2d, cf, cl, false))
{
Bnd_Box2d box;
Geom2dAdaptor_Curve gac;
double aFirst = c2d->FirstParameter();
double aLast = c2d->LastParameter();
if (c2d->IsKind(STANDARD_TYPE(Geom2d_BSplineCurve)) && (cf < aFirst || cl > aLast))
{
// pdn avoiding problems with segment in Bnd_Box
gac.Load(c2d);
}
else
{
gac.Load(c2d, cf, cl);
}
BndLib_Add2dCurve::Add(gac, ::Precision::Confusion(), box);
boxes.Bind(newE1, box);
}
if (sae.PCurve(newE2, S, L, c2d, cf, cl, false))
{
Bnd_Box2d box;
Geom2dAdaptor_Curve gac;
double aFirst = c2d->FirstParameter();
double aLast = c2d->LastParameter();
if (c2d->IsKind(STANDARD_TYPE(Geom2d_BSplineCurve)) && (cf < aFirst || cl > aLast))
{
// pdn avoiding problems with segment in Bnd_Box
gac.Load(c2d);
}
else
{
gac.Load(c2d, cf, cl);
}
BndLib_Add2dCurve::Add(gac, ::Precision::Confusion(), box);
boxes.Bind(newE2, box);
}
return true;
}
return false;
}
//=================================================================================================
bool ShapeFix_Face::FixIntersectingWires()
{
ShapeFix_IntersectionTool ITool(Context(), Precision(), MaxTolerance());
return ITool.FixIntersectingWires(myFace);
}
//=================================================================================================
bool ShapeFix_Face::FixWiresTwoCoincEdges()
{
if (!Context().IsNull())
{
TopoDS_Shape S = Context()->Apply(myFace);
myFace = TopoDS::Face(S);
}
TopAbs_Orientation ori = myFace.Orientation();
TopoDS_Shape emptyCopied = myFace.EmptyCopied();
TopoDS_Face face = TopoDS::Face(emptyCopied);
face.Orientation(TopAbs_FORWARD);
int nbWires = 0;
BRep_Builder B;
for (TopoDS_Iterator it(myFace, false); it.More(); it.Next())
{
if (it.Value().ShapeType() != TopAbs_WIRE
|| (it.Value().Orientation() != TopAbs_FORWARD
&& it.Value().Orientation() != TopAbs_REVERSED))
{
continue;
}
nbWires++;
}
if (nbWires < 2)
{
return false;
}
bool isFixed = false;
for (TopoDS_Iterator wi(myFace, false); wi.More(); wi.Next())
{
if (wi.Value().ShapeType() != TopAbs_WIRE
|| (wi.Value().Orientation() != TopAbs_FORWARD
&& wi.Value().Orientation() != TopAbs_REVERSED))
{
B.Add(face, wi.Value());
continue;
}
TopoDS_Wire wire = TopoDS::Wire(wi.Value());
occ::handle<ShapeExtend_WireData> sewd = new ShapeExtend_WireData(wire);
if (sewd->NbEdges() == 2)
{
TopoDS_Edge E1 = sewd->Edge(1);
TopoDS_Edge E2 = sewd->Edge(2);
E1.Orientation(TopAbs_FORWARD);
E2.Orientation(TopAbs_FORWARD);
if (!(E1 == E2))
{
B.Add(face, wire);
}
else
{
isFixed = true;
}
}
else
{
B.Add(face, wire);
}
}
if (isFixed)
{
face.Orientation(ori);
if (!Context().IsNull())
{
Context()->Replace(myFace, face);
}
myFace = face;
}
return isFixed;
}
//=================================================================================================
bool ShapeFix_Face::FixSplitFace(
const NCollection_DataMap<TopoDS_Shape, NCollection_List<TopoDS_Shape>, TopTools_ShapeMapHasher>&
MapWires)
{
BRep_Builder B;
NCollection_Sequence<TopoDS_Shape> faces;
TopoDS_Shape S = myFace;
if (!Context().IsNull())
{
S = Context()->Apply(myFace);
}
int NbWires = 0, NbWiresNew = 0, NbEdges;
for (TopoDS_Iterator iter(S, false); iter.More(); iter.Next())
{
const TopoDS_Shape& aShape = iter.Value();
if (aShape.ShapeType() != TopAbs_WIRE
|| (aShape.Orientation() != TopAbs_FORWARD && aShape.Orientation() != TopAbs_REVERSED))
{
continue;
}
TopoDS_Wire wire = TopoDS::Wire(aShape);
NbWires++;
if (MapWires.IsBound(wire))
{
// if wire not closed --> stop split and return false
occ::handle<ShapeExtend_WireData> sewd = new ShapeExtend_WireData(wire);
NbEdges = sewd->NbEdges();
if (NbEdges == 0)
{
continue;
}
//
TopoDS_Edge E1 = sewd->Edge(1);
TopoDS_Edge E2 = sewd->Edge(NbEdges);
TopoDS_Vertex V1, V2;
ShapeAnalysis_Edge sae;
V1 = sae.FirstVertex(E1);
V2 = sae.LastVertex(E2);
if (!V1.IsSame(V2))
{
return false;
}
// create face
TopoDS_Shape emptyCopied = S.EmptyCopied();
TopoDS_Face tmpFace = TopoDS::Face(emptyCopied);
tmpFace.Orientation(TopAbs_FORWARD);
B.Add(tmpFace, wire);
NbWiresNew++;
const NCollection_List<TopoDS_Shape>& IntWires = MapWires.Find(wire);
NCollection_List<TopoDS_Shape>::Iterator liter(IntWires);
for (; liter.More(); liter.Next())
{
TopoDS_Shape aShapeEmptyCopied = tmpFace.EmptyCopied();
TopoDS_Face aFace = TopoDS::Face(aShapeEmptyCopied);
aFace.Orientation(TopAbs_FORWARD);
B.Add(aFace, liter.Value());
BRepTopAdaptor_FClass2d clas(aFace, ::Precision::PConfusion());
TopAbs_State staout = clas.PerformInfinitePoint();
if (staout == TopAbs_IN)
{
B.Add(tmpFace, liter.Value());
}
else
{
B.Add(tmpFace, liter.Value().Reversed());
}
NbWiresNew++;
}
if (!myFwd)
{
tmpFace.Orientation(TopAbs_REVERSED);
}
faces.Append(tmpFace);
}
}
if (NbWires != NbWiresNew)
{
return false;
}
if (faces.Length() > 1)
{
TopoDS_Compound Comp;
B.MakeCompound(Comp);
for (int i = 1; i <= faces.Length(); i++)
{
B.Add(Comp, faces(i));
}
myResult = Comp;
if (!Context().IsNull())
{
Context()->Replace(myFace, myResult);
}
for (TopExp_Explorer exp(myResult, TopAbs_FACE); exp.More(); exp.Next())
{
myFace = TopoDS::Face(exp.Current());
BRepTools::Update(myFace);
}
return true;
}
return false;
}
//=======================================================================
// function : IsPeriodicConicalLoop
// purpose : Checks whether the passed wire makes up a periodic loop on
// passed conical surface
//=======================================================================
static bool IsPeriodicConicalLoop(const occ::handle<Geom_ConicalSurface>& theSurf,
const TopoDS_Wire& theWire,
const double theTolerance,
double& theMinU,
double& theMaxU,
double& theMinV,
double& theMaxV,
bool& isUDecrease)
{
if (theSurf.IsNull())
{
return false;
}
ShapeAnalysis_Edge aSAE;
TopLoc_Location aLoc;
double aCumulDeltaU = 0.0, aCumulDeltaUAbs = 0.0;
double aMinU = RealLast();
double aMinV = aMinU;
double aMaxU = -aMinU;
double aMaxV = aMaxU;
// Iterate over the edges to check whether the wire is periodic on conical surface
TopoDS_Iterator aWireIter(theWire, false);
for (; aWireIter.More(); aWireIter.Next())
{
const TopoDS_Edge& aCurrentEdge = TopoDS::Edge(aWireIter.Value());
occ::handle<Geom2d_Curve> aC2d;
double aPFirst, aPLast;
aSAE.PCurve(aCurrentEdge, theSurf, aLoc, aC2d, aPFirst, aPLast, true);
if (aC2d.IsNull())
{
return false;
}
gp_Pnt2d aUVFirst = aC2d->Value(aPFirst), aUVLast = aC2d->Value(aPLast);
double aUFirst = aUVFirst.X(), aULast = aUVLast.X();
double aVFirst = aUVFirst.Y(), aVLast = aUVLast.Y();
double aCurMaxU = std::max(aUFirst, aULast), aCurMinU = std::min(aUFirst, aULast);
double aCurMaxV = std::max(aVFirst, aVLast), aCurMinV = std::min(aVFirst, aVLast);
if (aCurMinU < aMinU)
{
aMinU = aCurMinU;
}
if (aCurMaxU > aMaxU)
{
aMaxU = aCurMaxU;
}
if (aCurMinV < aMinV)
{
aMinV = aCurMinV;
}
if (aCurMaxV > aMaxV)
{
aMaxV = aCurMaxV;
}
double aDeltaU = aULast - aUFirst;
aCumulDeltaU += aDeltaU;
aCumulDeltaUAbs += std::abs(aDeltaU);
}
theMinU = aMinU;
theMaxU = aMaxU;
theMinV = aMinV;
theMaxV = aMaxV;
isUDecrease = (aCumulDeltaU < 0);
bool is2PIDelta = std::abs(aCumulDeltaUAbs - 2 * M_PI) <= theTolerance;
bool isAroundApex = std::abs(theMaxU - theMinU) > 2 * M_PI - theTolerance;
return is2PIDelta && isAroundApex;
}
//=================================================================================================
bool 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
NCollection_Sequence<TopoDS_Shape> aWireSeq;
for (TopoDS_Iterator aWireIt(myFace, 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
int aNbWires = aWireSeq.Length();
occ::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 false;
}
// Get the single wire
TopoDS_Wire aSoleWire = TopoDS::Wire(aWireSeq.Value(1));
// Check whether this wire is belting the conical surface by period
occ::handle<Geom_ConicalSurface> aConeSurf = occ::down_cast<Geom_ConicalSurface>(aSurface);
double aMinLoopU = 0.0, aMaxLoopU = 0.0, aMinLoopV = 0.0, aMaxLoopV = 0.0;
bool isUDecrease = false;
bool isConicLoop = IsPeriodicConicalLoop(aConeSurf,
aSoleWire,
Precision(),
aMinLoopU,
aMaxLoopU,
aMinLoopV,
aMaxLoopV,
isUDecrease);
if (!isConicLoop)
{
return false;
}
/* ===============
* Retrieve apex
* =============== */
// Get base circle of the conical surface (the circle it was built from)
occ::handle<Geom_Curve> aConeBaseCrv = aConeSurf->VIso(0.0);
occ::handle<Geom_Circle> aConeBaseCirc = occ::down_cast<Geom_Circle>(aConeBaseCrv);
// Retrieve conical props
double aConeBaseR = aConeBaseCirc->Radius();
double aSemiAngle = aConeSurf->SemiAngle();
if (fabs(aSemiAngle) <= Precision::Confusion())
{
return false; // Bad surface
}
// Find the V parameter of the apex
double aConeBaseH = aConeBaseR / std::sin(aSemiAngle);
double 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 false;
}
occ::handle<Geom2d_Line> anApexCurve2d;
// Apex curve below the wire
if (anApexV < aMinLoopV)
{
anApexCurve2d = new Geom2d_Line(gp_Pnt2d(aMinLoopU, anApexV), gp_Dir2d(gp_Dir2d::D::X));
if (!isUDecrease)
{
aSoleWire.Reverse();
}
}
// Apex curve above the wire
if (anApexV > aMaxLoopV)
{
anApexCurve2d = new Geom2d_Line(gp_Pnt2d(aMaxLoopU, anApexV), gp_Dir2d(gp_Dir2d::D::NX));
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, 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
NCollection_Sequence<TopoDS_Shape> 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 (int 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;
if (!Context().IsNull())
{
Context()->Replace(myFace, myResult);
}
return true;
}
b8f597c67This file is pinned to the selected release. Download the original to open it in your design tool.