1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-04-21 10:13:43 +03:00
occt/src/BRepLib/BRepLib_1.cxx
emv 88c3accd1a 0030787: BRepOffsetAPI_MakePipeShell: hangs on the attached model
Protect IntTools_EdgeEdge::FindSolutions method from cases where splitting edge on parts does not give bounding box decreasing.
Use at least Epsilon from edges parameters as a step in BRepLib::FindValidRange.
Test case for the issue.
2019-06-25 19:47:09 +03:00

285 lines
9.2 KiB
C++

// Created on: 2017-03-24
// Created by: Mikhail Sazonov
// Copyright (c) 2017 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.
#include <BRepLib.hxx>
#include <BRep_Builder.hxx>
#include <BRep_Tool.hxx>
#include <BRepAdaptor_Curve.hxx>
#include <Geom_OffsetCurve.hxx>
#include <Precision.hxx>
#include <TopExp.hxx>
#include <TopoDS_Vertex.hxx>
//=======================================================================
// function: findNearestValidPoint
// purpose : Starting from the appointed end of the curve, find the nearest
// point on the curve that is an intersection with the sphere with
// center theVertPnt and radius theTol.
//=======================================================================
static Standard_Boolean findNearestValidPoint(
const Adaptor3d_Curve& theCurve,
const Standard_Real theFirst, const Standard_Real theLast,
const Standard_Boolean isFirst,
const gp_Pnt& theVertPnt,
const Standard_Real theTol,
const Standard_Real theEps,
Standard_Real& thePar)
{
// 1. Check that the needed end is inside the sphere
Standard_Real aStartU = theFirst;
Standard_Real anEndU = theLast;
if (!isFirst)
std::swap(aStartU, anEndU);
gp_Pnt aP = theCurve.Value(aStartU);
const Standard_Real aSqTol = theTol * theTol;
if (aP.SquareDistance(theVertPnt) > aSqTol)
// the vertex does not cover the corresponding to this vertex end of the curve
return Standard_False;
// 2. Find a nearest point that is outside
// stepping along the curve by theTol till go out
//
// the general step is computed using general curve resolution
Standard_Real aStep = theCurve.Resolution(theTol) * 1.01;
if (aStep < theEps)
aStep = theEps;
// aD1Mag is a threshold to consider local derivative magnitude too small
// and to accelerate going out of sphere
// (inverse of resolution is the maximal derivative);
// this is actual for bezier and b-spline types only
Standard_Real aD1Mag = 0.;
GeomAbs_CurveType aType = theCurve.GetType();
if (aType == GeomAbs_OffsetCurve)
{
Handle(Geom_OffsetCurve) anOffsetCurve = theCurve.OffsetCurve();
Handle(Geom_Curve) aBaseCurve = anOffsetCurve->BasisCurve();
aType = GeomAdaptor_Curve(aBaseCurve).GetType();
}
if (aType == GeomAbs_BezierCurve || aType == GeomAbs_BSplineCurve)
{
aD1Mag = 1. / theCurve.Resolution(1.) * 0.01;
aD1Mag *= aD1Mag;
}
if (!isFirst)
aStep = -aStep;
Standard_Boolean isOut = Standard_False;
Standard_Real anUIn = aStartU;
Standard_Real anUOut = anUIn;
while (!isOut)
{
anUIn = anUOut;
anUOut += aStep;
if ((isFirst && anUOut > anEndU) || (!isFirst && anUOut < anEndU))
{
// step is too big and we go out of bounds,
// check if the opposite bound is outside
aP = theCurve.Value(anEndU);
isOut = (aP.SquareDistance(theVertPnt) > aSqTol);
if (!isOut)
// all range is inside sphere
return Standard_False;
anUOut = anEndU;
break;
}
if (aD1Mag > 0.)
{
Standard_Real aStepLocal = aStep;
for (;;)
{
// cycle to go out of local singularity
gp_Vec aD1;
theCurve.D1(anUOut, aP, aD1);
isOut = (aP.SquareDistance(theVertPnt) > aSqTol);
if (!isOut && aD1.SquareMagnitude() < aD1Mag)
{
aStepLocal *= 2.;
anUOut += aStepLocal;
if ((isFirst && anUOut < anEndU) || (!isFirst && anUOut > anEndU))
// still in range
continue;
// went out of range, so check if the end point has out state
anUOut = anEndU;
aP = theCurve.Value(anUOut);
isOut = (aP.SquareDistance(theVertPnt) > aSqTol);
if (!isOut)
// all range is inside sphere
return Standard_False;
}
break;
}
}
else
{
aP = theCurve.Value(anUOut);
}
if (!isOut)
isOut = (aP.SquareDistance(theVertPnt) > aSqTol);
}
// 3. Precise solution with binary search
Standard_Real aDelta = Abs(anUOut - anUIn);
while (aDelta > theEps)
{
Standard_Real aMidU = (anUIn + anUOut) * 0.5;
aP = theCurve.Value(aMidU);
isOut = (aP.SquareDistance(theVertPnt) > aSqTol);
if (isOut)
anUOut = aMidU;
else
anUIn = aMidU;
aDelta = Abs(anUOut - anUIn);
}
thePar = (anUIn + anUOut) * 0.5;
return Standard_True;
}
//=======================================================================
// function: FindValidRange
// purpose :
//=======================================================================
Standard_Boolean BRepLib::FindValidRange
(const Adaptor3d_Curve& theCurve, const Standard_Real theTolE,
const Standard_Real theParV1, const gp_Pnt& thePntV1, const Standard_Real theTolV1,
const Standard_Real theParV2, const gp_Pnt& thePntV2, const Standard_Real theTolV2,
Standard_Real& theFirst, Standard_Real& theLast)
{
if (theParV2 - theParV1 < Precision::PConfusion())
return Standard_False;
Standard_Boolean isInfParV1 = Precision::IsInfinite (theParV1),
isInfParV2 = Precision::IsInfinite (theParV2);
Standard_Real aMaxPar = 0.0;
if (!isInfParV1)
aMaxPar = Abs (theParV1);
if (!isInfParV2)
aMaxPar = Max (aMaxPar, Abs (theParV2));
Standard_Real anEps = Max (Max (theCurve.Resolution (theTolE) * 0.1, Epsilon (aMaxPar)),
Precision::PConfusion());
if (isInfParV1)
theFirst = theParV1;
else
{
if (!findNearestValidPoint(theCurve, theParV1, theParV2, Standard_True,
thePntV1, theTolV1, anEps, theFirst))
return Standard_False;
if (theParV2 - theFirst < anEps)
return Standard_False;
}
if (isInfParV2)
theLast = theParV2;
else
{
if (!findNearestValidPoint(theCurve, theParV1, theParV2, Standard_False,
thePntV2, theTolV2, anEps, theLast))
return Standard_False;
if (theLast - theParV1 < anEps)
return Standard_False;
}
// check found parameters
if (theFirst > theLast)
{
// overlapping, not valid range
return Standard_False;
}
return Standard_True;
}
//=======================================================================
// function: FindValidRange
// purpose :
//=======================================================================
Standard_Boolean BRepLib::FindValidRange
(const TopoDS_Edge& theEdge, Standard_Real& theFirst, Standard_Real& theLast)
{
TopLoc_Location aLoc;
Standard_Real f, l;
if (BRep_Tool::Curve(theEdge, aLoc, f, l).IsNull())
return Standard_False;
BRepAdaptor_Curve anAC(theEdge);
Standard_Real aParV[2] = { anAC.FirstParameter(), anAC.LastParameter() };
if (aParV[1] - aParV[0] < Precision::PConfusion())
return Standard_False;
// get vertices
TopoDS_Vertex aV[2];
TopExp::Vertices(theEdge, aV[0], aV[1]);
Standard_Real aTolE = BRep_Tool::Tolerance(theEdge);
// to have correspondence with intersection precision
// the tolerances of vertices are increased on Precision::Confusion()
Standard_Real aTolV[2] = { Precision::Confusion(), Precision::Confusion() };
gp_Pnt aPntV[2];
for (Standard_Integer i = 0; i < 2; i++)
{
if (!aV[i].IsNull())
{
aTolV[i] += BRep_Tool::Tolerance(aV[i]);
aPntV[i] = BRep_Tool::Pnt(aV[i]);
}
else if (!Precision::IsInfinite(aParV[i]))
{
aTolV[i] += aTolE;
aPntV[i] = anAC.Value(aParV[i]);
}
}
return FindValidRange(anAC, aTolE,
aParV[0], aPntV[0], aTolV[0],
aParV[1], aPntV[1], aTolV[1],
theFirst, theLast);
}
//=======================================================================
//function : BuildPCurveForEdgeOnPlane
//purpose :
//=======================================================================
void BRepLib::BuildPCurveForEdgeOnPlane(const TopoDS_Edge& aE,
const TopoDS_Face& aF)
{
Standard_Boolean bToUpdate;
Standard_Real aTolE;
Handle(Geom2d_Curve) aC2D;
BRep_Builder aBB;
//
BuildPCurveForEdgeOnPlane(aE, aF, aC2D, bToUpdate);
if (bToUpdate) {
aTolE = BRep_Tool::Tolerance(aE);
aBB.UpdateEdge(aE, aC2D, aF, aTolE);
}
}
//=======================================================================
//function : BuildPCurveForEdgeOnPlane
//purpose :
//=======================================================================
void BRepLib::BuildPCurveForEdgeOnPlane(const TopoDS_Edge& aE,
const TopoDS_Face& aF,
Handle(Geom2d_Curve)& aC2D,
Standard_Boolean& bToUpdate)
{
Standard_Real aT1, aT2;
Standard_Boolean isStored;
aC2D = BRep_Tool::CurveOnSurface(aE, aF, aT1, aT2, &isStored);
bToUpdate = !isStored && !aC2D.IsNull();
}