Files
OCCT/src/ModelingAlgorithms/TKShHealing/ShapeConstruct/ShapeConstruct_ProjectCurveOnSurface.cxx
Dmitrii Kulikov 6f6be66842 Shape Healing - Crash in ShapeConstruct_ProjectCurveOnSurface::insertAdditionalPointOrAdjust() (#1267)
To input parameters of ShapeConstruct_ProjectCurveOnSurface::insertAdditionalPointOrAdjust() new arrays were assigned. The size of new arrays are guaranteed to be 1 element larger that original array. NCollection_Array1::Assign() is guaranteed to throw exception when arrays size mismatch. So, given that assignment code is reached, function will always throw.
Fixed by calling move assignment instead - size mismatch is allowed in this case. Also it is more optimal.
2026-05-05 18:16:32 +01:00

2786 lines
82 KiB
C++

// 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.
#include <ShapeConstruct_ProjectCurveOnSurface.hxx>
#include <Approx_CurveOnSurface.hxx>
#include <ElCLib.hxx>
#include <Geom2d_BSplineCurve.hxx>
#include <Geom2d_Circle.hxx>
#include <Geom2d_Curve.hxx>
#include <Geom2d_Ellipse.hxx>
#include <Geom2d_Hyperbola.hxx>
#include <Geom2d_Line.hxx>
#include <Geom2d_Parabola.hxx>
#include <Geom2d_TrimmedCurve.hxx>
#include <Geom2dAdaptor.hxx>
#include <Geom2dAPI_Interpolate.hxx>
#include <Geom_BezierSurface.hxx>
#include <Geom_BSplineCurve.hxx>
#include <Geom_BSplineSurface.hxx>
#include <Geom_Curve.hxx>
#include <Geom_OffsetSurface.hxx>
#include <Geom_Plane.hxx>
#include <Geom_RectangularTrimmedSurface.hxx>
#include <Geom_SphericalSurface.hxx>
#include <Geom_Surface.hxx>
#include <Geom_SurfaceOfLinearExtrusion.hxx>
#include <Geom_TrimmedCurve.hxx>
#include <GeomAdaptor_Curve.hxx>
#include <GeomAdaptor_Surface.hxx>
#include <GeomAPI_Interpolate.hxx>
#include <GeomAPI_PointsToBSpline.hxx>
#include <GeomProjLib.hxx>
#include <gp_Pnt2d.hxx>
#include <IntCurve_IntConicConic.hxx>
#include <IntRes2d_Domain.hxx>
#include <NCollection_IncAllocator.hxx>
#include <NCollection_Sequence.hxx>
#include <NCollection_DynamicArray.hxx>
#include <Precision.hxx>
#include <ProjLib_ProjectedCurve.hxx>
#include <ShapeAnalysis.hxx>
#include <ShapeAnalysis_Curve.hxx>
#include <ShapeAnalysis_Surface.hxx>
#include <ShapeExtend.hxx>
#include <Standard_ErrorHandler.hxx>
#include <Standard_Failure.hxx>
#include <Standard_Type.hxx>
#include <gp_Pnt.hxx>
#include <NCollection_Array1.hxx>
#include <NCollection_HArray1.hxx>
#include <Standard_Integer.hxx>
#include <algorithm>
IMPLEMENT_STANDARD_RTTIEXT(ShapeConstruct_ProjectCurveOnSurface, Standard_Transient)
namespace
{
//! Default number of control points for discretization.
constexpr int THE_NCONTROL = 23;
//! Utility class for projecting points onto a surface with B-spline corner cache optimization.
//! For clamped B-spline surfaces, caches corner pole positions and their exact UV parameters
//! to avoid expensive ValueOfUV calls when projected points coincide with surface corners.
//! Only corner poles are cached because B-spline surfaces pass through corners only
//! (when end multiplicities equal degree + 1).
class SurfaceProjectorWithCache
{
public:
//! Constructor - initializes the projector with a surface.
//! For B-spline surfaces, builds the corner cache automatically.
//! @param[in] theSurf the surface analysis object
SurfaceProjectorWithCache(const occ::handle<ShapeAnalysis_Surface>& theSurf)
: mySurf(theSurf)
{
if (theSurf.IsNull())
{
return;
}
occ::handle<Geom_BSplineSurface> aBSplineSurf =
occ::down_cast<Geom_BSplineSurface>(theSurf->Surface());
if (!aBSplineSurf.IsNull())
{
buildCornerCache(aBSplineSurf);
}
}
//! Projects a 3D point onto the surface.
//! First checks B-spline corner cache, then falls back to ValueOfUV.
//! @param[in] thePoint the 3D point to project
//! @param[in] theTol the tolerance for projection
//! @param[in] theTolSq squared tolerance for corner matching
//! @return the UV coordinates on the surface
gp_Pnt2d ValueOfUV(const gp_Pnt& thePoint, const double theTol, const double theTolSq) const
{
gp_Pnt2d aResult;
if (findInCornerCache(thePoint, theTolSq, aResult))
{
// Corner UV is exact, but refine for numerical stability
return mySurf->NextValueOfUV(aResult, thePoint, theTol, theTol);
}
return mySurf->ValueOfUV(thePoint, theTol);
}
//! Projects a 3D point onto the surface using a hint from previous projection.
//! First checks B-spline corner cache, then falls back to NextValueOfUV.
//! @param[in] theHint the UV hint from previous projection
//! @param[in] thePoint the 3D point to project
//! @param[in] theTol the tolerance for projection
//! @param[in] theTolSq squared tolerance for corner matching
//! @param[in] theStep the step tolerance
//! @return the UV coordinates on the surface
gp_Pnt2d NextValueOfUV(const gp_Pnt2d& theHint,
const gp_Pnt& thePoint,
const double theTol,
const double theTolSq,
const double theStep) const
{
gp_Pnt2d aResult;
if (findInCornerCache(thePoint, theTolSq, aResult))
{
// Corner UV is exact, but refine for numerical stability
return mySurf->NextValueOfUV(aResult, thePoint, theTol, theTol);
}
return mySurf->NextValueOfUV(theHint, thePoint, theTol, theStep);
}
//! Returns the gap from the last projection
double Gap() const { return mySurf->Gap(); }
private:
//! Builds cache from B-spline surface corner poles.
//! For clamped B-splines (multiplicity = degree + 1 at ends), the surface passes
//! exactly through corner poles, so we can use their UV parameters directly.
//! @param[in] theSurface the B-spline surface
void buildCornerCache(const occ::handle<Geom_BSplineSurface>& theSurface)
{
const int aNbPolesU = theSurface->NbUPoles();
const int aNbPolesV = theSurface->NbVPoles();
const int aDegreeU = theSurface->UDegree();
const int aDegreeV = theSurface->VDegree();
// Check if surface is clamped (end multiplicities = degree + 1)
const int aFirstUMult = theSurface->UMultiplicity(1);
const int aLastUMult = theSurface->UMultiplicity(theSurface->NbUKnots());
const int aFirstVMult = theSurface->VMultiplicity(1);
const int aLastVMult = theSurface->VMultiplicity(theSurface->NbVKnots());
const bool isUFirstClamped = (aFirstUMult >= aDegreeU + 1);
const bool isULastClamped = (aLastUMult >= aDegreeU + 1);
const bool isVFirstClamped = (aFirstVMult >= aDegreeV + 1);
const bool isVLastClamped = (aLastVMult >= aDegreeV + 1);
// Get parameter bounds
const double aUFirst = theSurface->UKnot(1);
const double aULast = theSurface->UKnot(theSurface->NbUKnots());
const double aVFirst = theSurface->VKnot(1);
const double aVLast = theSurface->VKnot(theSurface->NbVKnots());
// Cache corner poles where surface passes through them
// Corner (1, 1) - UFirst, VFirst
if (isUFirstClamped && isVFirstClamped)
{
myCorners3d.Append(theSurface->Pole(1, 1));
myCorners2d.Append(gp_Pnt2d(aUFirst, aVFirst));
}
// Corner (NbPolesU, 1) - ULast, VFirst
if (isULastClamped && isVFirstClamped)
{
myCorners3d.Append(theSurface->Pole(aNbPolesU, 1));
myCorners2d.Append(gp_Pnt2d(aULast, aVFirst));
}
// Corner (1, NbPolesV) - UFirst, VLast
if (isUFirstClamped && isVLastClamped)
{
myCorners3d.Append(theSurface->Pole(1, aNbPolesV));
myCorners2d.Append(gp_Pnt2d(aUFirst, aVLast));
}
// Corner (NbPolesU, NbPolesV) - ULast, VLast
if (isULastClamped && isVLastClamped)
{
myCorners3d.Append(theSurface->Pole(aNbPolesU, aNbPolesV));
myCorners2d.Append(gp_Pnt2d(aULast, aVLast));
}
}
//! Finds the closest corner to a 3D point within tolerance.
//! @param[in] thePoint the 3D point to find
//! @param[in] theTolSq squared tolerance for matching
//! @param[out] theUV the UV parameter if found
//! @return true if a matching corner was found
bool findInCornerCache(const gp_Pnt& thePoint, const double theTolSq, gp_Pnt2d& theUV) const
{
for (int i = 0; i < myCorners3d.Length(); ++i)
{
if (myCorners3d(i).SquareDistance(thePoint) < theTolSq)
{
theUV = myCorners2d(i);
return true;
}
}
return false;
}
private:
occ::handle<ShapeAnalysis_Surface> mySurf; //!< Surface to project on
NCollection_DynamicArray<gp_Pnt> myCorners3d; //!< 3D positions of B-spline surface corners
NCollection_DynamicArray<gp_Pnt2d> myCorners2d; //!< UV parameters of B-spline surface corners
};
//=================================================================================================
//! Extracts B-spline curve from a possibly nested trimmed curve.
//! Recursively unwraps trimmed curves to find B-spline basis.
//! @param[in] theCurve the curve to extract from
//! @return the extracted B-spline curve, or null if not a B-spline
occ::handle<Geom_BSplineCurve> extractBSplineCurve(const occ::handle<Geom_Curve>& theCurve)
{
occ::handle<Geom_Curve> aCurve = theCurve;
// Recursively unwrap trimmed curves
while (!aCurve.IsNull() && aCurve->IsKind(STANDARD_TYPE(Geom_TrimmedCurve)))
{
aCurve = occ::down_cast<Geom_TrimmedCurve>(aCurve)->BasisCurve();
}
return occ::down_cast<Geom_BSplineCurve>(aCurve);
}
//=================================================================================================
//! Adjusts second point to first point considering surface periodicity.
//! For periodic surfaces, adjusts the second point coordinates to be within
//! half-period of the first point.
//! @param[in] theFirstPoint the reference point
//! @param[in,out] theSecondPoint the point to adjust
//! @param[in] theSurf the surface to check for periodicity
void adjustSecondToFirstPoint(const gp_Pnt2d& theFirstPoint,
gp_Pnt2d& theSecondPoint,
const occ::handle<Geom_Surface>& theSurf)
{
if (theSurf->IsUPeriodic())
{
const double anUPeriod = theSurf->UPeriod();
const double aNewU = ElCLib::InPeriod(theSecondPoint.X(),
theFirstPoint.X() - anUPeriod / 2,
theFirstPoint.X() + anUPeriod / 2);
theSecondPoint.SetX(aNewU);
}
if (theSurf->IsVPeriodic())
{
const double aVPeriod = theSurf->VPeriod();
const double aNewV = ElCLib::InPeriod(theSecondPoint.Y(),
theFirstPoint.Y() - aVPeriod / 2,
theFirstPoint.Y() + aVPeriod / 2);
theSecondPoint.SetY(aNewV);
}
}
//=================================================================================================
//! Fixes possible period jumps in an array of 4 points.
//! Handles the walking period parameter to ensure smooth transition
//! across periodic boundaries.
//! @param[in,out] thePnt array of 4 points to fix
//! @param[in] theIdx coordinate index (1 for U, 2 for V)
//! @param[in] thePeriod the period value
//! @param[in] theSavedPoint index of the reference point (-1 for none)
//! @param[in] theSavedParam the saved parameter value
//! @return true if a period jump was detected and fixed
bool fixPeriodicityTroubles(gp_Pnt2d* thePnt,
const int theIdx,
const double thePeriod,
const int theSavedPoint,
const double theSavedParam)
{
double aSavedParam;
int aSavedPoint;
double aMinParam = 0.0, aMaxParam = thePeriod;
if (theSavedPoint < 0)
{
aSavedParam = 0.5 * thePeriod;
aSavedPoint = 0;
}
else
{
aSavedParam = theSavedParam;
aSavedPoint = theSavedPoint;
while (aMinParam > aSavedParam)
{
aMinParam -= thePeriod;
aMaxParam -= thePeriod;
}
while (aMaxParam < aSavedParam)
{
aMinParam += thePeriod;
aMaxParam += thePeriod;
}
}
double aFixIsoParam = aMinParam;
bool isIsoLine = false;
if (aMaxParam - aSavedParam < Precision::PConfusion()
|| aSavedParam - aMinParam < Precision::PConfusion())
{
aFixIsoParam = aSavedParam;
isIsoLine = true;
}
// Normalize all coordinates to [aMinParam, aMaxParam)
for (int i = 0; i < 4; i++)
{
double aParam = thePnt[i].Coord(theIdx);
double aShift = ShapeAnalysis::AdjustToPeriod(aParam, aMinParam, aMaxParam);
aParam += aShift;
if (isIsoLine)
{
if (aMaxParam - aParam < Precision::PConfusion()
|| aParam - aMinParam < Precision::PConfusion())
{
aParam = aFixIsoParam;
}
}
else
{
if (aMaxParam - aParam < Precision::PConfusion())
{
aParam = aMaxParam;
}
if (aParam - aMinParam < Precision::PConfusion())
{
aParam = aMinParam;
}
}
thePnt[i].SetCoord(theIdx, aParam);
}
// Find possible period jump and increasing or decreasing coordinates vector
bool isJump = false;
double aPrevDiff = 0.0;
double aSumDiff = 1.0;
for (int i = 0; i < 3; i++)
{
double aDiff = thePnt[i + 1].Coord(theIdx) - thePnt[i].Coord(theIdx);
if (aDiff < -Precision::PConfusion())
{
aSumDiff *= -1.0;
}
if (aDiff * aPrevDiff < -Precision::PConfusion())
{
isJump = true;
}
aPrevDiff = aDiff;
}
if (!isJump)
{
return false;
}
if (aSumDiff > 0)
{
for (int i = aSavedPoint; i > 0; i--)
{
if (thePnt[i].Coord(theIdx) > thePnt[i - 1].Coord(theIdx))
{
thePnt[i - 1].SetCoord(theIdx, thePnt[i - 1].Coord(theIdx) + thePeriod);
}
}
for (int i = aSavedPoint; i < 3; i++)
{
if (thePnt[i].Coord(theIdx) < thePnt[i + 1].Coord(theIdx))
{
thePnt[i + 1].SetCoord(theIdx, thePnt[i + 1].Coord(theIdx) - thePeriod);
}
}
}
else
{
for (int i = aSavedPoint; i > 0; i--)
{
if (thePnt[i].Coord(theIdx) < thePnt[i - 1].Coord(theIdx))
{
thePnt[i - 1].SetCoord(theIdx, thePnt[i - 1].Coord(theIdx) - thePeriod);
}
}
for (int i = aSavedPoint; i < 3; i++)
{
if (thePnt[i].Coord(theIdx) > thePnt[i + 1].Coord(theIdx))
{
thePnt[i + 1].SetCoord(theIdx, thePnt[i + 1].Coord(theIdx) + thePeriod);
}
}
}
return true;
}
//=================================================================================================
//! Checks if B-spline curve has uneven parameterization requiring special handling.
//! Computes the ratio of maximum to minimum parameterization speed across knot intervals.
//! If this ratio exceeds a threshold, the curve should be projected using ProjLib instead
//! of the standard approximation approach.
//! @param[in] theCurve the curve to check (may be trimmed)
//! @param[in] theFirst the first parameter of the working range
//! @param[in] theLast the last parameter of the working range
//! @param[out] theBSpline the extracted B-spline curve (if any)
//! @return true if the curve has uneven parameterization requiring ProjLib
bool isBSplineCurveInvalid(const occ::handle<Geom_Curve>& theCurve,
const double theFirst,
const double theLast,
occ::handle<Geom_BSplineCurve>& theBSpline)
{
theBSpline = extractBSplineCurve(theCurve);
if (theBSpline.IsNull())
{
return false;
}
// Compute parametrization speed on each knot interval inside [theFirst, theLast].
// If quotient = (MaxSpeed / MinSpeed) >= aMaxQuotientCoeff then use PerformByProjLib.
double aFirstParam = theFirst;
double aLastParam = theLast;
// Find first knot index
int anIdx = 1;
for (; anIdx <= theBSpline->NbKnots() && aFirstParam < theLast; anIdx++)
{
if (theBSpline->Knot(anIdx) > theFirst)
{
break;
}
}
GeomAdaptor_Curve aC3DAdaptor(theCurve);
double aMinParSpeed = Precision::Infinite();
NCollection_Sequence<double> aKnotCoeffs;
for (; anIdx <= theBSpline->NbKnots() && aFirstParam < theLast; anIdx++)
{
// Fill current knot interval
aLastParam = std::min(theLast, theBSpline->Knot(anIdx));
int aNbIntPnts = THE_NCONTROL;
// Adapt number of inner points according to the length of the interval
// to avoid a lot of calculations on small range of parameters.
if (anIdx > 1)
{
const double aLenThres = 1.e-2;
const double aLenRatio =
(aLastParam - aFirstParam) / (theBSpline->Knot(anIdx) - theBSpline->Knot(anIdx - 1));
if (aLenRatio < aLenThres)
{
aNbIntPnts = int(aLenRatio / aLenThres * aNbIntPnts);
if (aNbIntPnts < 2)
{
aNbIntPnts = 2;
}
}
}
double aStep = (aLastParam - aFirstParam) / (aNbIntPnts - 1);
gp_Pnt p3d1, p3d2;
// Start filling from first point
aC3DAdaptor.D0(aFirstParam, p3d1);
double aLength3d = 0.0;
for (int anIntIdx = 1; anIntIdx < aNbIntPnts; anIntIdx++)
{
double aParam = aFirstParam + aStep * anIntIdx;
aC3DAdaptor.D0(aParam, p3d2);
const double aDist = p3d2.Distance(p3d1);
aLength3d += aDist;
p3d1 = p3d2;
aMinParSpeed = std::min(aMinParSpeed, aDist / aStep);
}
const double aCoeff = aLength3d / (aLastParam - aFirstParam);
if (std::abs(aCoeff) > gp::Resolution())
{
aKnotCoeffs.Append(aCoeff);
}
aFirstParam = aLastParam;
}
double anEvenlyCoeff = 0;
if (aKnotCoeffs.Size() > 0)
{
anEvenlyCoeff = *std::max_element(aKnotCoeffs.begin(), aKnotCoeffs.end())
/ *std::min_element(aKnotCoeffs.begin(), aKnotCoeffs.end());
}
const double aMaxQuotientCoeff = 1500.0;
return (anEvenlyCoeff > aMaxQuotientCoeff && aMinParSpeed > Precision::Confusion());
}
//=================================================================================================
//! Generates discretization points for a curve.
//! Uses uniform distribution for general curves, and adjusts the number of points
//! for B-splines based on their knot structure to ensure adequate sampling.
//! @param[in] theCurve the curve to discretize
//! @param[in] theFirst the first parameter
//! @param[in] theLast the last parameter
//! @param[in] theNbControlPoints the minimum number of points
//! @param[out] thePoints the generated 3D points (resized as needed)
//! @param[out] theParams the corresponding parameters (resized as needed)
//! @return the actual number of generated points
int generateCurvePoints(const occ::handle<Geom_Curve>& theCurve,
const double theFirst,
const double theLast,
const int theNbControlPoints,
ShapeConstruct_ProjectCurveOnSurface::ArrayOfPnt& thePoints,
ShapeConstruct_ProjectCurveOnSurface::ArrayOfReal& theParams)
{
occ::handle<Geom_BSplineCurve> aBSpline = extractBSplineCurve(theCurve);
int aNbPini = theNbControlPoints;
if (!aBSpline.IsNull())
{
int aUsedKnots = 0;
for (int i = 1; i < aBSpline->NbKnots(); i++)
{
if (aBSpline->Knot(i + 1) > theFirst && aBSpline->Knot(i) < theLast)
{
aUsedKnots++;
}
}
int aMinPnt = aUsedKnots * (aBSpline->Degree() + 1);
while (aNbPini < aMinPnt)
{
aNbPini += THE_NCONTROL - 1;
}
}
thePoints.Resize(1, aNbPini, false);
theParams.Resize(1, aNbPini, false);
const double aDeltaParam = (theLast - theFirst) / (aNbPini - 1);
for (int i = 1; i <= aNbPini; ++i)
{
double aParam;
if (i == 1)
{
aParam = theFirst;
}
else if (i == aNbPini)
{
aParam = theLast;
}
else
{
aParam = theFirst + (i - 1) * aDeltaParam;
}
gp_Pnt aPoint;
theCurve->D0(aParam, aPoint);
thePoints.SetValue(i, aPoint);
theParams.SetValue(i, aParam);
}
return aNbPini;
}
//=================================================================================================
//! Wrapper for ShapeAnalysis_Surface::ProjectDegenerated.
//! Converts NCollection_Array1 containers to sequences, performs projection,
//! then copies results back. Required because ShapeAnalysis_Surface uses
//! sequence containers.
//! @param[in] theSurf the analysis surface
//! @param[in] theNbPnt the number of points
//! @param[in] thePoints the 3D points array
//! @param[in,out] thePoints2d the 2D points array (adjusted for degenerate regions)
//! @param[in] thePreci the precision
//! @param[in] theDirect true to check from start, false to check from end
void projectDegeneratedPoints(const occ::handle<ShapeAnalysis_Surface>& theSurf,
const int theNbPnt,
const NCollection_Array1<gp_Pnt>& thePoints,
NCollection_Array1<gp_Pnt2d>& thePoints2d,
const double thePreci,
const bool theDirect)
{
// Use incremental allocator for sequences when there are enough elements
// to benefit from pooled memory allocation.
constexpr int THE_ALLOC_THRESHOLD = 100;
occ::handle<NCollection_BaseAllocator> anAlloc;
if (theNbPnt > THE_ALLOC_THRESHOLD)
{
anAlloc = new NCollection_IncAllocator(NCollection_IncAllocator::THE_MINIMUM_BLOCK_SIZE);
}
// Convert arrays to sequences for ShapeAnalysis_Surface::ProjectDegenerated
NCollection_Sequence<gp_Pnt> aPoints3d(anAlloc);
NCollection_Sequence<gp_Pnt2d> aPoints2d(anAlloc);
for (int i = 1; i <= theNbPnt; ++i)
{
aPoints3d.Append(thePoints(i));
aPoints2d.Append(thePoints2d(i));
}
// Call the method that expects sequences
theSurf->ProjectDegenerated(theNbPnt, aPoints3d, aPoints2d, thePreci, theDirect);
// Copy results back to array
for (int i = 1; i <= theNbPnt; ++i)
{
thePoints2d.SetValue(i, aPoints2d.Value(i));
}
}
} // namespace
//=================================================================================================
ShapeConstruct_ProjectCurveOnSurface::ShapeConstruct_ProjectCurveOnSurface()
: myPreci(Precision::Confusion()),
myStatus(ShapeExtend::EncodeStatus(ShapeExtend_OK)),
myAdjustOverDegen(1)
{
}
//=================================================================================================
void ShapeConstruct_ProjectCurveOnSurface::Init(const occ::handle<Geom_Surface>& theSurf,
const double thePreci)
{
Init(new ShapeAnalysis_Surface(theSurf), thePreci);
}
//=================================================================================================
void ShapeConstruct_ProjectCurveOnSurface::Init(const occ::handle<ShapeAnalysis_Surface>& theSurf,
const double thePreci)
{
SetSurface(theSurf);
SetPrecision(thePreci);
}
//=================================================================================================
void ShapeConstruct_ProjectCurveOnSurface::SetSurface(const occ::handle<Geom_Surface>& theSurf)
{
SetSurface(new ShapeAnalysis_Surface(theSurf));
}
//=================================================================================================
void ShapeConstruct_ProjectCurveOnSurface::SetSurface(
const occ::handle<ShapeAnalysis_Surface>& theSurf)
{
if (mySurf == theSurf)
{
return;
}
mySurf = theSurf;
myCache = CacheArray();
}
//=================================================================================================
void ShapeConstruct_ProjectCurveOnSurface::SetPrecision(const double thePreci)
{
myPreci = thePreci;
}
//=================================================================================================
int& ShapeConstruct_ProjectCurveOnSurface::AdjustOverDegenMode()
{
return myAdjustOverDegen;
}
//=================================================================================================
bool ShapeConstruct_ProjectCurveOnSurface::Status(const ShapeExtend_Status theStatus) const
{
return ShapeExtend::DecodeStatus(myStatus, theStatus);
}
//=================================================================================================
bool ShapeConstruct_ProjectCurveOnSurface::Perform(const occ::handle<Geom_Curve>& theC3D,
const double theFirst,
const double theLast,
occ::handle<Geom2d_Curve>& theC2D,
const double theTolFirst,
const double theTolLast)
{
myStatus = ShapeExtend::EncodeStatus(ShapeExtend_OK);
if (mySurf.IsNull() || theC3D.IsNull())
{
theC2D.Nullify();
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_FAIL1);
return false;
}
// Try analytical projection first
occ::handle<Geom_Curve> aCurve3DTrim = theC3D;
if (!theC3D->IsKind(STANDARD_TYPE(Geom_BoundedCurve)))
{
aCurve3DTrim = new Geom_TrimmedCurve(theC3D, theFirst, theLast);
}
theC2D = projectAnalytic(aCurve3DTrim);
if (!theC2D.IsNull())
{
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE1);
return true;
}
// Handle problematic B-spline curves with uneven parameterization
occ::handle<Geom_BSplineCurve> aBSpline;
if (isBSplineCurveInvalid(theC3D, theFirst, theLast, aBSpline))
{
// Use ProjLib for curves with highly uneven parameterization speed
if (PerformByProjLib(theC3D, theFirst, theLast, theC2D))
{
return Status(ShapeExtend_DONE);
}
}
// Generate curve points
ArrayOfPnt aPoints;
ArrayOfReal aParams;
const int aNbPnt = generateCurvePoints(theC3D, theFirst, theLast, THE_NCONTROL, aPoints, aParams);
// Approximate pcurve
ArrayOfPnt2d aPoints2d(1, aNbPnt);
for (int i = 1; i <= aNbPnt; i++)
{
aPoints2d.SetValue(i, gp_Pnt2d(0., 0.));
}
approxPCurve(aNbPnt, theC3D, theTolFirst, theTolLast, aPoints, aParams, aPoints2d, theC2D);
const int aNbPini = aPoints.Length();
if (!theC2D.IsNull())
{
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE2);
return true;
}
// Interpolate the result
theC2D = interpolatePCurve(aNbPini, aPoints2d, aParams);
myStatus |= ShapeExtend::EncodeStatus(theC2D.IsNull() ? ShapeExtend_FAIL1 : ShapeExtend_DONE2);
return Status(ShapeExtend_DONE);
}
//=================================================================================================
bool ShapeConstruct_ProjectCurveOnSurface::PerformByProjLib(const occ::handle<Geom_Curve>& theC3D,
const double theFirst,
const double theLast,
occ::handle<Geom2d_Curve>& theC2D)
{
theC2D.Nullify();
if (mySurf.IsNull())
{
myStatus = ShapeExtend::EncodeStatus(ShapeExtend_FAIL1);
return false;
}
try
{
OCC_CATCH_SIGNALS
occ::handle<GeomAdaptor_Surface> aGAS = mySurf->Adaptor3d();
occ::handle<GeomAdaptor_Curve> aGAC = new GeomAdaptor_Curve(theC3D, theFirst, theLast);
ProjLib_ProjectedCurve aProjector(aGAS, aGAC);
switch (aProjector.GetType())
{
case GeomAbs_Line:
theC2D = new Geom2d_Line(aProjector.Line());
break;
case GeomAbs_Circle:
theC2D = new Geom2d_Circle(aProjector.Circle());
break;
case GeomAbs_Ellipse:
theC2D = new Geom2d_Ellipse(aProjector.Ellipse());
break;
case GeomAbs_Parabola:
theC2D = new Geom2d_Parabola(aProjector.Parabola());
break;
case GeomAbs_Hyperbola:
theC2D = new Geom2d_Hyperbola(aProjector.Hyperbola());
break;
case GeomAbs_BSplineCurve:
theC2D = aProjector.BSpline();
break;
default:
break;
}
if (theC2D.IsNull())
{
myStatus = ShapeExtend::EncodeStatus(ShapeExtend_FAIL2);
return false;
}
else
{
myStatus = ShapeExtend::EncodeStatus(ShapeExtend_DONE1);
return true;
}
}
catch (Standard_Failure const& anException)
{
#ifdef OCCT_DEBUG
std::cout << "Warning: ShapeConstruct_ProjectCurveOnSurface::PerformByProjLib(): Exception: ";
anException.Print(std::cout);
std::cout << std::endl;
#endif
(void)anException;
myStatus = ShapeExtend::EncodeStatus(ShapeExtend_FAIL3);
theC2D.Nullify();
}
return false;
}
//=================================================================================================
occ::handle<Geom2d_Curve> ShapeConstruct_ProjectCurveOnSurface::projectAnalytic(
const occ::handle<Geom_Curve>& theC3D) const
{
occ::handle<Geom2d_Curve> aResult;
occ::handle<Geom_Surface> aSurf = mySurf->Surface();
occ::handle<Geom_Plane> aPlane = occ::down_cast<Geom_Plane>(aSurf);
if (aPlane.IsNull())
{
occ::handle<Geom_RectangularTrimmedSurface> aRTS =
occ::down_cast<Geom_RectangularTrimmedSurface>(aSurf);
if (!aRTS.IsNull())
{
aPlane = occ::down_cast<Geom_Plane>(aRTS->BasisSurface());
}
else
{
occ::handle<Geom_OffsetSurface> anOS = occ::down_cast<Geom_OffsetSurface>(aSurf);
if (!anOS.IsNull())
{
aPlane = occ::down_cast<Geom_Plane>(anOS->BasisSurface());
}
}
}
if (!aPlane.IsNull())
{
occ::handle<Geom_Curve> aProjOnPlane =
GeomProjLib::ProjectOnPlane(theC3D, aPlane, aPlane->Position().Direction(), true);
occ::handle<GeomAdaptor_Curve> aHC = new GeomAdaptor_Curve(aProjOnPlane);
ProjLib_ProjectedCurve aProj(mySurf->Adaptor3d(), aHC);
aResult = Geom2dAdaptor::MakeCurve(aProj);
if (aResult.IsNull())
{
return aResult;
}
if (aResult->IsKind(STANDARD_TYPE(Geom2d_TrimmedCurve)))
{
occ::handle<Geom2d_TrimmedCurve> aTC = occ::down_cast<Geom2d_TrimmedCurve>(aResult);
aResult = aTC->BasisCurve();
}
return aResult;
}
return aResult;
}
//=================================================================================================
occ::handle<Geom2d_Curve> ShapeConstruct_ProjectCurveOnSurface::getLine(
const ArrayOfPnt& thePoints,
const ArrayOfReal& theParams,
ArrayOfPnt2d& thePoints2d,
const double theTol,
bool& theIsRecompute,
bool& theIsFromCache) const
{
const int aNb = thePoints.Length();
gp_Pnt aP[4];
aP[0] = thePoints(1);
aP[1] = thePoints(2);
aP[2] = thePoints(aNb - 1);
aP[3] = thePoints(aNb);
gp_Pnt2d aP2d[4];
int i = 0;
double aTol2 = theTol * theTol;
double aTolWorking = theTol;
const bool isPeriodicU = mySurf->Surface()->IsUPeriodic();
const bool isPeriodicV = mySurf->Surface()->IsVPeriodic();
// Protection against bad tolerance shapes
if (aTol2 > 1.0)
{
aTolWorking = Precision::Confusion();
aTol2 = aTolWorking * aTolWorking;
}
if (aTol2 < Precision::SquareConfusion())
{
aTol2 = Precision::SquareConfusion();
}
const double anOldTol2 = aTol2;
int aSavedPointNum = -1;
gp_Pnt2d aSavedPoint;
// Create projector with B-spline surface pole cache optimization
SurfaceProjectorWithCache aProjector(mySurf);
// Helper lambda to project a point with cache lookup
auto projectPoint = [&](const gp_Pnt& thePoint, gp_Pnt2d& theResult, const int theIndex) -> void {
// Try existing endpoint cache first
const int aNbCache = myCache.Length();
for (int j = 0; j < aNbCache; ++j)
{
const CachePoint& aCachePnt = myCache(j);
if (aCachePnt.first.SquareDistance(thePoint) < aTol2)
{
theResult =
aProjector.NextValueOfUV(aCachePnt.second, thePoint, aTolWorking, aTol2, aTolWorking);
aSavedPointNum = theIndex;
aSavedPoint = aCachePnt.second;
if (theIndex == 0)
{
theIsFromCache = true;
}
return;
}
}
// Fall back to full projection (with B-spline pole cache optimization inside)
theResult = aProjector.ValueOfUV(thePoint, aTolWorking, aTol2);
};
// Project first and last points
for (; i < 4; i += 3)
{
projectPoint(aP[i], aP2d[i], i);
const double aDist = aProjector.Gap();
const double aCurDist = aDist * aDist;
if (aTol2 < aCurDist)
{
aTol2 = aCurDist;
}
}
if (isPeriodicU || isPeriodicV)
{
// Compute second and last but one c2d points
for (i = 1; i < 3; i++)
{
projectPoint(aP[i], aP2d[i], i);
const double aDist = aProjector.Gap();
const double aCurDist = aDist * aDist;
if (aTol2 < aCurDist)
{
aTol2 = aCurDist;
}
}
if (isPeriodicU)
{
theIsRecompute = fixPeriodicityTroubles(&aP2d[0],
1,
mySurf->Surface()->UPeriod(),
aSavedPointNum,
aSavedPoint.X());
}
if (isPeriodicV)
{
theIsRecompute = fixPeriodicityTroubles(&aP2d[0],
2,
mySurf->Surface()->VPeriod(),
aSavedPointNum,
aSavedPoint.Y());
}
}
if (theIsRecompute && mySurf->Surface()->IsKind(STANDARD_TYPE(Geom_SphericalSurface)))
{
return nullptr;
}
thePoints2d.SetValue(1, aP2d[0]);
thePoints2d.SetValue(aNb, aP2d[3]);
// Restore old tolerance
aTol2 = anOldTol2;
const double dPar = theParams(aNb) - theParams(1);
if (std::abs(dPar) < Precision::PConfusion())
{
return nullptr;
}
const gp_Vec2d aVec0(aP2d[0], aP2d[3]);
const gp_Vec2d aVec = aVec0 / dPar;
occ::handle<Geom_Surface> aSurf = mySurf->Surface();
bool isNormalCheck = aSurf->IsCNu(1) && aSurf->IsCNv(1);
if (isNormalCheck)
{
for (i = 1; i <= aNb; i++)
{
const gp_XY aCurPoint = aP2d[0].XY() + aVec.XY() * (theParams(i) - theParams(1));
gp_Pnt aCurP;
gp_Vec aNormalVec, aDu, aDv;
aSurf->D1(aCurPoint.X(), aCurPoint.Y(), aCurP, aDu, aDv);
aNormalVec = aDu ^ aDv;
if (aNormalVec.SquareMagnitude() < Precision::SquareConfusion())
{
isNormalCheck = false;
break;
}
const gp_Lin aNormalLine(aCurP, gp_Dir(aNormalVec));
const double aDist = aNormalLine.Distance(thePoints(i));
if (aDist > aTolWorking)
{
return nullptr;
}
}
}
if (!isNormalCheck)
{
const double aFirstPointDist =
mySurf->Surface()->Value(aP2d[0].X(), aP2d[0].Y()).SquareDistance(thePoints(1));
aTol2 = std::max(aTol2, aTol2 * 2 * aFirstPointDist);
for (i = 2; i < aNb; i++)
{
const gp_XY aCurPoint = aP2d[0].XY() + aVec.XY() * (theParams(i) - theParams(1));
gp_Pnt aCurP;
aSurf->D0(aCurPoint.X(), aCurPoint.Y(), aCurP);
const double aDist1 = aCurP.SquareDistance(thePoints(i));
if (std::abs(aFirstPointDist - aDist1) > aTol2)
{
return nullptr;
}
}
}
// Check if pcurve can be represented by Geom2d_Line
const double aLLength = aVec0.Magnitude();
if (std::abs(aLLength - dPar) <= Precision::PConfusion())
{
const gp_XY aDirL = aVec0.XY() / aLLength;
const gp_Pnt2d aPL(aP2d[0].XY() - theParams(1) * aDirL);
return new Geom2d_Line(aPL, gp_Dir2d(aDirL));
}
// Create straight bspline
NCollection_Array1<gp_Pnt2d> aPoles(1, 2);
aPoles(1) = aP2d[0];
aPoles(2) = aP2d[3];
NCollection_Array1<double> aKnots(1, 2);
aKnots(1) = theParams(1);
aKnots(2) = theParams(theParams.Length());
NCollection_Array1<int> aMults(1, 2);
aMults(1) = 2;
aMults(2) = 2;
return new Geom2d_BSplineCurve(aPoles, aKnots, aMults, 1);
}
//=================================================================================================
bool ShapeConstruct_ProjectCurveOnSurface::approxPCurve(const int theNbPnt,
const occ::handle<Geom_Curve>& theC3D,
const double theTolFirst,
const double theTolLast,
ArrayOfPnt& thePoints,
ArrayOfReal& theParams,
ArrayOfPnt2d& thePoints2d,
occ::handle<Geom2d_Curve>& theC2D)
{
// For performance, first try to handle typical case when pcurve is straight
bool isRecompute = false;
bool isFromCacheLine = false;
theC2D = getLine(thePoints, theParams, thePoints2d, myPreci, isRecompute, isFromCacheLine);
if (!theC2D.IsNull())
{
// Fill cache
bool aChangeCycle = false;
if (!myCache.IsEmpty()
&& myCache(0).first.Distance(thePoints(1)) > myCache(0).first.Distance(thePoints(theNbPnt))
&& myCache(0).first.Distance(thePoints(theNbPnt)) < Precision::Confusion())
{
aChangeCycle = true;
}
myCache.Resize(0, 1, false);
if (aChangeCycle)
{
myCache(0) = CachePoint(thePoints(1), thePoints2d(1));
myCache(1) = CachePoint(thePoints(theNbPnt), thePoints2d(theNbPnt));
}
else
{
myCache(0) = CachePoint(thePoints(theNbPnt), thePoints2d(theNbPnt));
myCache(1) = CachePoint(thePoints(1), thePoints2d(1));
}
return true;
}
bool isDone = true;
// Test if the curve 3d is a boundary of the surface
bool isoParam, isoPar2d3d, isoTypeU, p1OnIso, p2OnIso, isoClosed;
gp_Pnt2d valueP1, valueP2;
occ::handle<Geom_Curve> cIso;
double t1, t2;
occ::handle<Standard_Type> sType = mySurf->Surface()->DynamicType();
bool isAnalytic = true;
if (sType == STANDARD_TYPE(Geom_BezierSurface) || sType == STANDARD_TYPE(Geom_BSplineSurface))
{
isAnalytic = false;
}
double uf, ul, vf, vl;
mySurf->Surface()->Bounds(uf, ul, vf, vl);
isoClosed = false;
ArrayOfReal pout(1, theNbPnt);
for (int i = 1; i <= theNbPnt; i++)
{
pout.SetValue(i, 0.0);
}
isoParam = isAnIsoparametric(theNbPnt,
thePoints,
theParams,
isoTypeU,
p1OnIso,
valueP1,
p2OnIso,
valueP2,
isoPar2d3d,
cIso,
t1,
t2,
pout);
// Projection of the points on surfaces
gp_Pnt p3d;
gp_Pnt2d p2d;
double isoValue = 0., isoPar1 = 0., isoPar2 = 0., tPar = 0., tdeb, tfin;
double Cf, Cl, parf, parl;
if (isoParam)
{
if (isoTypeU)
{
isoValue = valueP1.X();
isoPar1 = valueP1.Y();
isoPar2 = valueP2.Y();
isoClosed = mySurf->IsVClosed(myPreci);
parf = vf;
parl = vl;
}
else
{
isoValue = valueP1.Y();
isoPar1 = valueP1.X();
isoPar2 = valueP2.X();
isoClosed = mySurf->IsUClosed(myPreci);
parf = uf;
parl = ul;
}
if (!isoPar2d3d && !isAnalytic)
{
Cf = cIso->FirstParameter();
Cl = cIso->LastParameter();
if (Precision::IsInfinite(Cf))
{
Cf = -1000;
}
if (Precision::IsInfinite(Cl))
{
Cl = +1000;
}
tdeb = pout(2);
if (isoClosed && (isoPar1 == parf || isoPar1 == parl))
{
if (std::abs(tdeb - parf) < std::abs(tdeb - parl))
{
isoPar1 = parf;
}
else
{
isoPar1 = parl;
}
if (isoTypeU)
{
valueP1.SetY(isoPar1);
}
else
{
valueP1.SetX(isoPar1);
}
}
if (isoClosed && (isoPar2 == parf || isoPar2 == parl))
{
tfin = pout(theNbPnt - 1);
if (std::abs(tfin - parf) < std::abs(tfin - parl))
{
isoPar2 = parf;
}
else
{
isoPar2 = parl;
}
if (isoTypeU)
{
valueP2.SetY(isoPar2);
}
else
{
valueP2.SetX(isoPar2);
}
}
if (!isoClosed)
{
if ((std::abs(tdeb - isoPar1) > std::abs(tdeb - isoPar2))
&& (std::abs(pout(theNbPnt - 1) - isoPar2) > std::abs(pout(theNbPnt - 1) - isoPar1)))
{
gp_Pnt2d valueTmp = valueP1;
valueP1 = valueP2;
valueP2 = valueTmp;
if (isoTypeU)
{
isoValue = valueP1.X();
isoPar1 = valueP1.Y();
isoPar2 = valueP2.Y();
}
else
{
isoValue = valueP1.Y();
isoPar1 = valueP1.X();
isoPar2 = valueP2.X();
}
}
}
}
}
const double Up = ul - uf;
const double Vp = vl - vf;
double gap = myPreci;
bool aChangeCycle = false;
bool isFromCache = false;
gp_Pnt2d aSavedPoint;
if (!myCache.IsEmpty()
&& myCache(0).first.Distance(thePoints(1)) > myCache(0).first.Distance(thePoints(theNbPnt)))
{
if (myCache(0).first.Distance(thePoints(theNbPnt)) < Precision::Confusion())
{
aChangeCycle = true;
}
}
bool needResolveUJump = false;
bool needResolveVJump = false;
gp_Pnt prevP3d;
gp_Pnt2d prevP2d;
for (int ii = 1; ii <= theNbPnt; ii++)
{
const int aPntIndex = aChangeCycle ? (theNbPnt - ii + 1) : ii;
p3d = thePoints(aPntIndex);
if (isoParam)
{
if (isoPar2d3d)
{
if (isoPar2 > isoPar1)
{
tPar = theParams(aPntIndex);
}
else
{
tPar = t1 + t2 - theParams(aPntIndex);
}
}
else if (!isAnalytic)
{
if (aPntIndex == 1)
{
tPar = isoPar1;
}
else if (aPntIndex == theNbPnt)
{
tPar = isoPar2;
}
else
{
tPar = pout(aPntIndex);
}
}
if (!isoPar2d3d && isAnalytic)
{
if (aPntIndex == 1)
{
p2d = valueP1;
}
else if (aPntIndex == theNbPnt)
{
p2d = valueP2;
}
else
{
p2d = mySurf->NextValueOfUV(p2d, p3d, myPreci, Precision::Confusion() + 1000 * gap);
gap = mySurf->Gap();
}
}
else
{
if (isoTypeU)
{
p2d.SetX(isoValue);
p2d.SetY(tPar);
}
else
{
p2d.SetX(tPar);
p2d.SetY(isoValue);
}
}
}
else
{
if ((aPntIndex == 1) && p1OnIso)
{
p2d = valueP1;
}
else if ((aPntIndex == theNbPnt) && p2OnIso)
{
p2d = valueP2;
}
else
{
if (aPntIndex == 1 || aPntIndex == theNbPnt)
{
if (!isRecompute)
{
p2d = thePoints2d(aPntIndex);
gap = mySurf->Gap();
if (aPntIndex == 1)
{
isFromCache = isFromCacheLine;
aSavedPoint = p2d;
}
continue;
}
else
{
int j = 0;
const int aNbCache = myCache.Length();
for (; j < aNbCache; ++j)
{
const CachePoint& aCachePnt = myCache(j);
if (aCachePnt.first.SquareDistance(p3d) < myPreci * myPreci)
{
p2d = mySurf->NextValueOfUV(aCachePnt.second,
p3d,
myPreci,
Precision::Confusion() + gap);
if (aPntIndex == 1)
{
isFromCache = true;
aSavedPoint = aCachePnt.second;
}
break;
}
}
if (j >= aNbCache)
{
p2d = mySurf->ValueOfUV(p3d, myPreci);
}
}
}
else
{
p2d = mySurf->NextValueOfUV(p2d, p3d, myPreci, Precision::Confusion() + 1000 * gap);
}
gap = mySurf->Gap();
}
}
thePoints2d.SetValue(aPntIndex, p2d);
if (theNbPnt > 23 && ii > 2 && ii < theNbPnt)
{
if (fabs(p2d.X() - prevP2d.X()) > 0.95 * Up && prevP3d.Distance(p3d) < myPreci
&& !mySurf->IsUClosed(myPreci) && mySurf->NbSingularities(myPreci) > 0
&& mySurf->Surface()->IsKind(STANDARD_TYPE(Geom_BSplineSurface)))
{
needResolveUJump = true;
}
if (fabs(p2d.Y() - prevP2d.Y()) > 0.95 * Vp && prevP3d.Distance(p3d) < myPreci
&& !mySurf->IsVClosed(myPreci) && mySurf->NbSingularities(myPreci) > 0
&& mySurf->Surface()->IsKind(STANDARD_TYPE(Geom_BSplineSurface)))
{
needResolveVJump = true;
}
}
prevP3d = p3d;
prevP2d = p2d;
if (ii > 1)
{
if (aChangeCycle)
{
p2d.SetXY(2. * p2d.XY() - thePoints2d(aPntIndex + 1).XY());
}
else
{
p2d.SetXY(2. * p2d.XY() - thePoints2d(aPntIndex - 1).XY());
}
}
}
if (!isoPar2d3d)
{
projectDegeneratedPoints(mySurf, theNbPnt, thePoints, thePoints2d, myPreci, true);
projectDegeneratedPoints(mySurf, theNbPnt, thePoints, thePoints2d, myPreci, false);
}
// Check extremities for singularities
const gp_Pnt aPointFirst = thePoints.First();
const gp_Pnt aPointLast = thePoints.Last();
const double aTolFirst = (theTolFirst < 0) ? Precision::Confusion() : theTolFirst;
const double aTolLast = (theTolLast < 0) ? Precision::Confusion() : theTolLast;
for (int i = 1;; i++)
{
double aPreci, aFirstPar, aLastPar;
gp_Pnt aP3d;
gp_Pnt2d aFirstP2d, aLastP2d;
bool IsUiso;
if (!mySurf->Singularity(i, aPreci, aP3d, aFirstP2d, aLastP2d, aFirstPar, aLastPar, IsUiso))
{
break;
}
if (aPreci <= Precision::Confusion() && aPointFirst.Distance(aP3d) <= aTolFirst)
{
correctExtremity(theC3D, theParams, thePoints2d, true, aFirstP2d, IsUiso);
}
if (aPreci <= Precision::Confusion() && aPointLast.Distance(aP3d) <= aTolLast)
{
correctExtremity(theC3D, theParams, thePoints2d, false, aFirstP2d, IsUiso);
}
}
// Handle U-closed surfaces
double dist2d;
const double TolOnUPeriod = Precision::Confusion() * Up;
const double TolOnVPeriod = Precision::Confusion() * Vp;
if (mySurf->IsUClosed(myPreci) || needResolveUJump)
{
double prevX, firstX = thePoints2d(1).X();
if (!isFromCache)
{
while (firstX < uf)
{
firstX += Up;
thePoints2d.ChangeValue(1).SetX(firstX);
}
while (firstX > ul)
{
firstX -= Up;
thePoints2d.ChangeValue(1).SetX(firstX);
}
}
if (mySurf->Surface()->IsUPeriodic() && isFromCache)
{
double aMinParam = uf, aMaxParam = ul;
while (aMinParam > aSavedPoint.X())
{
aMinParam -= Up;
aMaxParam -= Up;
}
while (aMaxParam < aSavedPoint.X())
{
aMinParam += Up;
aMaxParam += Up;
}
double aShift = ShapeAnalysis::AdjustToPeriod(firstX, aMinParam, aMaxParam);
firstX += aShift;
thePoints2d.ChangeValue(1).SetX(firstX);
}
prevX = firstX;
double minX = firstX, maxX = firstX;
bool ToAdjust = false;
for (int aPntIter = 2; aPntIter <= thePoints2d.Length(); ++aPntIter)
{
double CurX = thePoints2d(aPntIter).X();
dist2d = std::abs(CurX - prevX);
if (dist2d > (Up / 2))
{
insertAdditionalPointOrAdjust(ToAdjust,
1,
Up,
TolOnUPeriod,
CurX,
prevX,
theC3D,
aPntIter,
thePoints,
theParams,
thePoints2d);
}
prevX = CurX;
if (minX > CurX)
{
minX = CurX;
}
else if (maxX < CurX)
{
maxX = CurX;
}
}
if (!isFromCache)
{
double midX = 0.5 * (minX + maxX);
double shiftX = 0.;
if (midX > ul)
{
shiftX = -Up;
}
else if (midX < uf)
{
shiftX = Up;
}
if (shiftX != 0.)
{
for (int aPntIter = 1; aPntIter <= thePoints2d.Length(); ++aPntIter)
{
thePoints2d.ChangeValue(aPntIter).SetX(thePoints2d(aPntIter).X() + shiftX);
}
}
}
}
// Handle V-closed surfaces
if (mySurf->IsVClosed(myPreci) || needResolveVJump
|| mySurf->Surface()->IsKind(STANDARD_TYPE(Geom_SphericalSurface)))
{
double prevY, firstY = thePoints2d(1).Y();
if (!isFromCache)
{
while (firstY < vf)
{
firstY += Vp;
thePoints2d.ChangeValue(1).SetY(firstY);
}
while (firstY > vl)
{
firstY -= Vp;
thePoints2d.ChangeValue(1).SetY(firstY);
}
}
if (mySurf->Surface()->IsVPeriodic() && isFromCache)
{
double aMinParam = vf, aMaxParam = vl;
while (aMinParam > aSavedPoint.Y())
{
aMinParam -= Vp;
aMaxParam -= Vp;
}
while (aMaxParam < aSavedPoint.Y())
{
aMinParam += Vp;
aMaxParam += Vp;
}
double aShift = ShapeAnalysis::AdjustToPeriod(firstY, aMinParam, aMaxParam);
firstY += aShift;
thePoints2d.ChangeValue(1).SetY(firstY);
}
prevY = firstY;
double minY = firstY, maxY = firstY;
bool ToAdjust = false;
for (int aPntIter = 2; aPntIter <= thePoints2d.Length(); ++aPntIter)
{
double CurY = thePoints2d(aPntIter).Y();
dist2d = std::abs(CurY - prevY);
if (dist2d > (Vp / 2))
{
insertAdditionalPointOrAdjust(ToAdjust,
2,
Vp,
TolOnVPeriod,
CurY,
prevY,
theC3D,
aPntIter,
thePoints,
theParams,
thePoints2d);
}
prevY = CurY;
if (minY > CurY)
{
minY = CurY;
}
else if (maxY < CurY)
{
maxY = CurY;
}
}
if (!isFromCache)
{
double midY = 0.5 * (minY + maxY);
double shiftY = 0.;
if (midY > vl)
{
shiftY = -Vp;
}
else if (midY < vf)
{
shiftY = Vp;
}
if (shiftY != 0.)
{
for (int aPntIter = 1; aPntIter <= thePoints2d.Length(); ++aPntIter)
{
thePoints2d.ChangeValue(aPntIter).SetY(thePoints2d(aPntIter).Y() + shiftY);
}
}
}
}
// Handle V-closed seam adjustment
if (mySurf->IsVClosed(myPreci) || mySurf->Surface()->IsKind(STANDARD_TYPE(Geom_SphericalSurface)))
{
for (int aPntIter = 2; aPntIter <= thePoints2d.Length(); ++aPntIter)
{
dist2d = std::abs(thePoints2d(aPntIter).Y() - thePoints2d(aPntIter - 1).Y());
if (dist2d > (Vp / 2))
{
bool prevOnFirst = false;
bool prevOnLast = false;
bool currOnFirst = false;
bool currOnLast = false;
double distPrevVF = std::abs(thePoints2d(aPntIter - 1).Y() - vf);
double distPrevVL = std::abs(thePoints2d(aPntIter - 1).Y() - vl);
double distCurrVF = std::abs(thePoints2d(aPntIter).Y() - vf);
double distCurrVL = std::abs(thePoints2d(aPntIter).Y() - vl);
double theMin = distPrevVF;
prevOnFirst = true;
if (distPrevVL < theMin)
{
theMin = distPrevVL;
prevOnFirst = false;
prevOnLast = true;
}
if (distCurrVF < theMin)
{
theMin = distCurrVF;
prevOnFirst = false;
prevOnLast = false;
currOnFirst = true;
}
if (distCurrVL < theMin)
{
prevOnFirst = false;
prevOnLast = false;
currOnFirst = false;
currOnLast = true;
}
if (prevOnFirst)
{
thePoints2d.ChangeValue(aPntIter - 1).SetY(vf);
}
else if (prevOnLast)
{
thePoints2d.ChangeValue(aPntIter - 1).SetY(vl);
}
else if (currOnFirst)
{
thePoints2d.ChangeValue(aPntIter).SetY(vf);
}
else if (currOnLast)
{
thePoints2d.ChangeValue(aPntIter).SetY(vl);
}
}
}
}
// Handle AdjustOverDegen
if (myAdjustOverDegen != -1)
{
if (mySurf->IsUClosed(myPreci))
{
mySurf->IsDegenerated(gp_Pnt(0, 0, 0), myPreci);
if (mySurf->NbSingularities(myPreci) > 0)
{
double PrevX = 0.;
int OnBound = 0, PrevOnBound = 0;
int ind;
bool start = true;
for (ind = 1; ind <= thePoints2d.Length(); ind++)
{
double CurX = thePoints2d(ind).X();
if (mySurf->IsDegenerated(thePoints(ind), Precision::Confusion()))
{
continue;
}
OnBound =
(std::abs(std::abs(CurX - 0.5 * (ul + uf)) - Up / 2) <= Precision::PConfusion());
if (!start && std::abs(std::abs(CurX - PrevX) - Up / 2) <= 0.01 * Up)
{
break;
}
start = false;
PrevX = CurX;
PrevOnBound = OnBound;
}
if (ind <= thePoints2d.Length())
{
PrevX = (myAdjustOverDegen ? uf : ul);
double dU = Up / 2 + Precision::PConfusion();
if (PrevOnBound)
{
thePoints2d.ChangeValue(ind - 1).SetX(PrevX);
for (int j = ind - 2; j > 0; j--)
{
double CurX = thePoints2d(j).X();
while (CurX < PrevX - dU)
{
CurX += Up;
thePoints2d.ChangeValue(j).SetX(CurX);
}
while (CurX > PrevX + dU)
{
CurX -= Up;
thePoints2d.ChangeValue(j).SetX(CurX);
}
}
}
else if (OnBound)
{
thePoints2d.ChangeValue(ind).SetX(PrevX);
for (int j = ind + 1; j <= thePoints2d.Length(); j++)
{
double CurX = thePoints2d(j).X();
while (CurX < PrevX - dU)
{
CurX += Up;
thePoints2d.ChangeValue(j).SetX(CurX);
}
while (CurX > PrevX + dU)
{
CurX -= Up;
thePoints2d.ChangeValue(j).SetX(CurX);
}
}
}
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE4);
}
}
}
else if (mySurf->IsVClosed(myPreci))
{
mySurf->IsDegenerated(gp_Pnt(0, 0, 0), myPreci);
if (mySurf->NbSingularities(myPreci) > 0)
{
double PrevY = 0.;
int OnBound = 0, PrevOnBound = 0;
int ind;
bool start = true;
for (ind = 1; ind <= thePoints2d.Length(); ind++)
{
double CurY = thePoints2d(ind).Y();
if (mySurf->IsDegenerated(thePoints(ind), Precision::Confusion()))
{
continue;
}
OnBound =
(std::abs(std::abs(CurY - 0.5 * (vl + vf)) - Vp / 2) <= Precision::PConfusion());
if (!start && std::abs(std::abs(CurY - PrevY) - Vp / 2) <= 0.01 * Vp)
{
break;
}
start = false;
PrevY = CurY;
PrevOnBound = OnBound;
}
if (ind <= thePoints2d.Length())
{
PrevY = (myAdjustOverDegen ? vf : vl);
double dV = Vp / 2 + Precision::PConfusion();
if (PrevOnBound)
{
thePoints2d.ChangeValue(ind - 1).SetY(PrevY);
for (int j = ind - 2; j > 0; j--)
{
double CurY = thePoints2d(j).Y();
while (CurY < PrevY - dV)
{
CurY += Vp;
thePoints2d.ChangeValue(j).SetY(CurY);
}
while (CurY > PrevY + dV)
{
CurY -= Vp;
thePoints2d.ChangeValue(j).SetY(CurY);
}
}
}
else if (OnBound)
{
thePoints2d.ChangeValue(ind).SetY(PrevY);
for (int j = ind + 1; j <= thePoints2d.Length(); j++)
{
double CurY = thePoints2d(j).Y();
while (CurY < PrevY - dV)
{
CurY += Vp;
thePoints2d.ChangeValue(j).SetY(CurY);
}
while (CurY > PrevY + dV)
{
CurY -= Vp;
thePoints2d.ChangeValue(j).SetY(CurY);
}
}
}
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE4);
}
}
}
}
// Fill cache
myCache.Resize(0, 1, false);
if (aChangeCycle)
{
myCache(0) = CachePoint(thePoints(1), thePoints2d(1));
myCache(1) = CachePoint(thePoints.Last(), thePoints2d.Last());
}
else
{
myCache(0) = CachePoint(thePoints.Last(), thePoints2d.Last());
myCache(1) = CachePoint(thePoints(1), thePoints2d(1));
}
return isDone;
}
//=================================================================================================
void ShapeConstruct_ProjectCurveOnSurface::correctExtremity(const occ::handle<Geom_Curve>& theC3D,
const ArrayOfReal& theParams,
ArrayOfPnt2d& thePoints2d,
const bool theIsFirstPoint,
const gp_Pnt2d& thePointOnIsoLine,
const bool theIsUIso)
{
const int NbPnt = thePoints2d.Length();
const int IndCoord = (theIsUIso) ? 2 : 1;
const double SingularityCoord = thePointOnIsoLine.Coord(3 - IndCoord);
gp_Pnt2d EndPoint = (theIsFirstPoint) ? thePoints2d(1) : thePoints2d(NbPnt);
const double FinishCoord = EndPoint.Coord(3 - IndCoord);
gp_Dir2d aDir = (theIsUIso) ? gp::DY2d() : gp::DX2d();
gp_Lin2d anIsoLine(EndPoint, aDir);
IntRes2d_Domain Dom1, Dom2;
const bool IsPeriodic =
(theIsUIso) ? mySurf->Surface()->IsVPeriodic() : mySurf->Surface()->IsUPeriodic();
gp_Pnt2d FirstPointOfLine, SecondPointOfLine;
double FinishParam, FirstParam, SecondParam;
if (theIsFirstPoint)
{
FirstPointOfLine = thePoints2d(3);
SecondPointOfLine = thePoints2d(2);
FinishParam = theParams(1);
FirstParam = theParams(3);
SecondParam = theParams(2);
}
else
{
FirstPointOfLine = thePoints2d(NbPnt - 2);
SecondPointOfLine = thePoints2d(NbPnt - 1);
FinishParam = theParams(NbPnt);
FirstParam = theParams(NbPnt - 2);
SecondParam = theParams(NbPnt - 1);
}
if (SingularityCoord > FinishCoord && SecondPointOfLine.Coord(3 - IndCoord) > FinishCoord)
{
return;
}
if (SingularityCoord < FinishCoord && SecondPointOfLine.Coord(3 - IndCoord) < FinishCoord)
{
return;
}
{
const double aPrevDist =
std::abs(SecondPointOfLine.Coord(IndCoord) - FirstPointOfLine.Coord(IndCoord));
const double aCurDist = std::abs(EndPoint.Coord(IndCoord) - SecondPointOfLine.Coord(IndCoord));
if (aCurDist <= 2 * aPrevDist)
{
return;
}
}
gp_Pnt2d FinishPoint = (theIsUIso) ? gp_Pnt2d(FinishCoord, SecondPointOfLine.Y())
: gp_Pnt2d(SecondPointOfLine.X(), FinishCoord);
for (;;)
{
if (std::abs(SecondPointOfLine.Coord(3 - IndCoord) - FinishCoord)
<= 2 * Precision::PConfusion())
{
break;
}
gp_Vec2d aVec(FirstPointOfLine, SecondPointOfLine);
const double aSqMagnitude = aVec.SquareMagnitude();
if (aSqMagnitude <= 1.e-32)
{
break;
}
aDir.SetCoord(aVec.X(), aVec.Y());
gp_Lin2d aLine(FirstPointOfLine, aDir);
IntCurve_IntConicConic Intersector(anIsoLine, Dom1, aLine, Dom2, 1.e-10, 1.e-10);
if (Intersector.IsDone() && !Intersector.IsEmpty())
{
IntRes2d_IntersectionPoint IntPoint = Intersector.Point(1);
FinishPoint = IntPoint.Value();
}
else
{
FinishPoint = (theIsUIso) ? gp_Pnt2d(FinishCoord, SecondPointOfLine.Y())
: gp_Pnt2d(SecondPointOfLine.X(), FinishCoord);
}
gp_Pnt2d PrevPoint = FirstPointOfLine;
FirstPointOfLine = SecondPointOfLine;
FirstParam = SecondParam;
SecondParam = (FirstParam + FinishParam) / 2;
if (std::abs(SecondParam - FirstParam) <= 2 * Precision::PConfusion())
{
break;
}
gp_Pnt aP3d;
theC3D->D0(SecondParam, aP3d);
SecondPointOfLine =
mySurf->NextValueOfUV(FirstPointOfLine, aP3d, myPreci, Precision::Confusion());
if (IsPeriodic)
{
adjustSecondToFirstPoint(FirstPointOfLine, SecondPointOfLine, mySurf->Surface());
}
const double aPrevDist = std::abs(FirstPointOfLine.Coord(IndCoord) - PrevPoint.Coord(IndCoord));
const double aCurDist =
std::abs(SecondPointOfLine.Coord(IndCoord) - FirstPointOfLine.Coord(IndCoord));
if (aCurDist > 2 * aPrevDist)
{
break;
}
}
if (theIsFirstPoint)
{
thePoints2d.ChangeValue(1) = FinishPoint;
}
else
{
thePoints2d.ChangeValue(NbPnt) = FinishPoint;
}
}
//=================================================================================================
void ShapeConstruct_ProjectCurveOnSurface::insertAdditionalPointOrAdjust(
bool& theToAdjust,
const int theIndCoord,
const double thePeriod,
const double theTolOnPeriod,
double& theCurCoord,
const double thePrevCoord,
const occ::handle<Geom_Curve>& theC3D,
int& theIndex,
ArrayOfPnt& thePoints,
ArrayOfReal& theParams,
ArrayOfPnt2d& thePoints2d)
{
const double CorrectedCurCoord =
ElCLib::InPeriod(theCurCoord, thePrevCoord - thePeriod / 2, thePrevCoord + thePeriod / 2);
if (!theToAdjust)
{
const double CurPar = theParams(theIndex);
const double PrevPar = theParams(theIndex - 1);
double MidPar = (PrevPar + CurPar) / 2;
gp_Pnt MidP3d;
theC3D->D0(MidPar, MidP3d);
gp_Pnt2d MidP2d = mySurf->ValueOfUV(MidP3d, myPreci);
double MidCoord = MidP2d.Coord(theIndCoord);
MidCoord =
ElCLib::InPeriod(MidCoord, thePrevCoord - thePeriod / 2, thePrevCoord + thePeriod / 2);
double FirstCoord = thePrevCoord, LastCoord = CorrectedCurCoord;
if (LastCoord < FirstCoord)
{
double tmp = FirstCoord;
FirstCoord = LastCoord;
LastCoord = tmp;
}
if (LastCoord - FirstCoord <= theTolOnPeriod)
{
theToAdjust = true;
}
else if (FirstCoord <= MidCoord && MidCoord <= LastCoord)
{
theToAdjust = true;
}
else
{
bool Success = true;
double FirstT = PrevPar;
double LastT = CurPar;
MidCoord = MidP2d.Coord(theIndCoord);
while (std::abs(MidCoord - thePrevCoord) >= thePeriod / 2 - theTolOnPeriod
|| std::abs(theCurCoord - MidCoord) >= thePeriod / 2 - theTolOnPeriod)
{
if (MidPar - FirstT <= Precision::PConfusion() || LastT - MidPar <= Precision::PConfusion())
{
Success = false;
break;
}
if (std::abs(MidCoord - thePrevCoord) >= thePeriod / 2 - theTolOnPeriod)
{
LastT = (FirstT + LastT) / 2;
}
else
{
FirstT = (FirstT + LastT) / 2;
}
MidPar = (FirstT + LastT) / 2;
theC3D->D0(MidPar, MidP3d);
MidP2d = mySurf->ValueOfUV(MidP3d, myPreci);
MidCoord = MidP2d.Coord(theIndCoord);
}
if (Success)
{
// Insert additional point - need to resize arrays
const int aNewLength = thePoints.Length() + 1;
ArrayOfPnt aNewPoints(1, aNewLength);
ArrayOfReal aNewParams(1, aNewLength);
ArrayOfPnt2d aNewPoints2d(1, aNewLength);
for (int i = 1; i < theIndex; i++)
{
aNewPoints.SetValue(i, thePoints(i));
aNewParams.SetValue(i, theParams(i));
aNewPoints2d.SetValue(i, thePoints2d(i));
}
aNewPoints.SetValue(theIndex, MidP3d);
aNewParams.SetValue(theIndex, MidPar);
aNewPoints2d.SetValue(theIndex, MidP2d);
for (int i = theIndex; i <= thePoints.Length(); i++)
{
aNewPoints.SetValue(i + 1, thePoints(i));
aNewParams.SetValue(i + 1, theParams(i));
aNewPoints2d.SetValue(i + 1, thePoints2d(i));
}
thePoints = std::move(aNewPoints);
theParams = std::move(aNewParams);
thePoints2d = std::move(aNewPoints2d);
theIndex++;
}
else
{
theToAdjust = true;
}
}
}
if (theToAdjust)
{
theCurCoord = CorrectedCurCoord;
thePoints2d.ChangeValue(theIndex).SetCoord(theIndCoord, theCurCoord);
}
}
//=================================================================================================
occ::handle<Geom2d_Curve> ShapeConstruct_ProjectCurveOnSurface::interpolatePCurve(
const int theNbPnt,
const ArrayOfPnt2d& thePoints2d,
const ArrayOfReal& theParams) const
{
occ::handle<Geom2d_Curve> aC2D;
const double theTolerance2d = myPreci / (100 * theNbPnt);
try
{
OCC_CATCH_SIGNALS
// Convert to HArrays for Geom2dAPI_Interpolate
occ::handle<NCollection_HArray1<gp_Pnt2d>> aPnts2d =
new NCollection_HArray1<gp_Pnt2d>(1, theNbPnt);
occ::handle<NCollection_HArray1<double>> aParams = new NCollection_HArray1<double>(1, theNbPnt);
for (int i = 1; i <= theNbPnt; i++)
{
aPnts2d->SetValue(i, thePoints2d(i));
aParams->SetValue(i, theParams(i));
}
// Check for coincident points
ArrayOfPnt2d aTmpPnts2d = thePoints2d;
ArrayOfReal aTmpParams = theParams;
double aPreci = theTolerance2d;
checkPoints2d(aTmpPnts2d, aTmpParams, aPreci);
if (aTmpPnts2d.Length() < 2)
{
return aC2D;
}
// Rebuild HArrays if points were removed
if (aTmpPnts2d.Length() != theNbPnt)
{
aPnts2d = new NCollection_HArray1<gp_Pnt2d>(1, aTmpPnts2d.Length());
aParams = new NCollection_HArray1<double>(1, aTmpParams.Length());
for (int i = 1; i <= aTmpPnts2d.Length(); i++)
{
aPnts2d->SetValue(i, aTmpPnts2d(i));
aParams->SetValue(i, aTmpParams(i));
}
}
Geom2dAPI_Interpolate myInterPol2d(aPnts2d, aParams, false, aPreci);
myInterPol2d.Perform();
if (myInterPol2d.IsDone())
{
aC2D = myInterPol2d.Curve();
}
}
catch (Standard_Failure const& anException)
{
#ifdef OCCT_DEBUG
std::cout << "Warning: ShapeConstruct_ProjectCurveOnSurface::interpolatePCurve(): Exception: ";
anException.Print(std::cout);
std::cout << std::endl;
#endif
(void)anException;
aC2D.Nullify();
}
return aC2D;
}
//=================================================================================================
occ::handle<Geom2d_Curve> ShapeConstruct_ProjectCurveOnSurface::approximatePCurve(
const ArrayOfPnt2d& thePoints2d,
const ArrayOfReal& theParams) const
{
const double theTolerance2d = myPreci;
occ::handle<Geom2d_Curve> aC2D;
try
{
OCC_CATCH_SIGNALS
ArrayOfPnt2d aTmpPnts2d = thePoints2d;
ArrayOfReal aTmpParams = theParams;
double aPreci = theTolerance2d;
checkPoints2d(aTmpPnts2d, aTmpParams, aPreci);
const int numberPnt = aTmpPnts2d.Length();
if (numberPnt < 2)
{
return aC2D;
}
NCollection_Array1<gp_Pnt> points3d(1, numberPnt);
NCollection_Array1<double> params(1, numberPnt);
for (int i = 1; i <= numberPnt; i++)
{
const gp_Pnt2d& pnt2d = aTmpPnts2d(i);
points3d(i).SetCoord(pnt2d.X(), pnt2d.Y(), 0);
params(i) = aTmpParams(i);
}
GeomAPI_PointsToBSpline appr(points3d, params, 1, 10, GeomAbs_C1, theTolerance2d);
const occ::handle<Geom_BSplineCurve>& crv3d = appr.Curve();
const int NbPoles = crv3d->NbPoles();
const NCollection_Array1<gp_Pnt>& poles3d = crv3d->Poles();
NCollection_Array1<gp_Pnt2d> poles2d(1, NbPoles);
for (int i = 1; i <= NbPoles; i++)
{
poles2d(i).SetCoord(poles3d(i).X(), poles3d(i).Y());
}
const NCollection_Array1<double>& weights = crv3d->WeightsArray();
const NCollection_Array1<double>& knots = crv3d->Knots();
const NCollection_Array1<int>& multiplicities = crv3d->Multiplicities();
aC2D = new Geom2d_BSplineCurve(poles2d,
weights,
knots,
multiplicities,
crv3d->Degree(),
crv3d->IsPeriodic());
return aC2D;
}
catch (Standard_Failure const& anException)
{
#ifdef OCCT_DEBUG
std::cout << "Warning: ShapeConstruct_ProjectCurveOnSurface::approximatePCurve(): Exception: ";
anException.Print(std::cout);
std::cout << std::endl;
#endif
(void)anException;
aC2D.Nullify();
}
return aC2D;
}
//=================================================================================================
void ShapeConstruct_ProjectCurveOnSurface::checkPoints(ArrayOfPnt& thePoints,
ArrayOfReal& theParams,
double& thePreci) const
{
const int firstElem = thePoints.Lower();
const int lastElem = thePoints.Upper();
int nbPntDropped = 0;
int lastValid = firstElem;
NCollection_Array1<int> tmpParam(firstElem, lastElem);
for (int i = firstElem; i <= lastElem; i++)
{
tmpParam.SetValue(i, 1);
}
double DistMin2 = RealLast();
gp_Pnt Prev = thePoints.Value(lastValid);
for (int i = firstElem + 1; i <= lastElem; i++)
{
const gp_Pnt& Curr = thePoints.Value(i);
const double CurDist2 = Prev.SquareDistance(Curr);
if (CurDist2 < gp::Resolution())
{
nbPntDropped++;
if (i == lastElem)
{
tmpParam.SetValue(lastValid, 0);
}
else
{
tmpParam.SetValue(i, 0);
}
}
else
{
if (CurDist2 < DistMin2)
{
DistMin2 = CurDist2;
}
lastValid = i;
Prev = Curr;
}
}
if (DistMin2 < RealLast())
{
thePreci = 0.9 * std::sqrt(DistMin2);
}
if (nbPntDropped == 0)
{
return;
}
const int newLast = lastElem - nbPntDropped;
if ((newLast - firstElem + 1) < 2)
{
return;
}
ArrayOfPnt newPnts(firstElem, newLast);
ArrayOfReal newParams(firstElem, newLast);
int newCurr = firstElem;
for (int i = firstElem; i <= lastElem; i++)
{
if (tmpParam.Value(i) == 1)
{
newPnts.SetValue(newCurr, thePoints.Value(i));
newParams.SetValue(newCurr, theParams.Value(i));
newCurr++;
}
}
thePoints = newPnts;
theParams = newParams;
}
//=================================================================================================
void ShapeConstruct_ProjectCurveOnSurface::checkPoints2d(ArrayOfPnt2d& thePoints2d,
ArrayOfReal& theParams,
double& thePreci) const
{
const int firstElem = thePoints2d.Lower();
const int lastElem = thePoints2d.Upper();
int nbPntDropped = 0;
int lastValid = firstElem;
NCollection_Array1<int> tmpParam(firstElem, lastElem);
for (int i = firstElem; i <= lastElem; i++)
{
tmpParam.SetValue(i, 1);
}
double DistMin2 = RealLast();
gp_Pnt2d Prev = thePoints2d.Value(lastValid);
for (int i = firstElem + 1; i <= lastElem; i++)
{
const gp_Pnt2d& Curr = thePoints2d.Value(i);
const double CurDist2 = Prev.SquareDistance(Curr);
if (CurDist2 < gp::Resolution())
{
nbPntDropped++;
if (i == lastElem)
{
tmpParam.SetValue(lastValid, 0);
}
else
{
tmpParam.SetValue(i, 0);
}
}
else
{
if (CurDist2 < DistMin2)
{
DistMin2 = CurDist2;
}
lastValid = i;
Prev = Curr;
}
}
if (DistMin2 < RealLast())
{
thePreci = 0.9 * std::sqrt(DistMin2);
}
if (nbPntDropped == 0)
{
return;
}
int newLast = lastElem - nbPntDropped;
if ((newLast - firstElem + 1) < 2)
{
// Create minimal length pcurve
tmpParam.SetValue(firstElem, 1);
tmpParam.SetValue(lastElem, 1);
gp_XY lastPnt = thePoints2d.Value(lastElem).XY();
lastPnt.Add(gp_XY(thePreci, thePreci));
thePoints2d.ChangeValue(lastElem).SetXY(lastPnt);
newLast = firstElem + 1;
}
ArrayOfPnt2d newPnts(firstElem, newLast);
ArrayOfReal newParams(firstElem, newLast);
int newCurr = firstElem;
for (int i = firstElem; i <= lastElem; i++)
{
if (tmpParam.Value(i) == 1)
{
newPnts.SetValue(newCurr, thePoints2d.Value(i));
newParams.SetValue(newCurr, theParams.Value(i));
newCurr++;
}
}
thePoints2d = std::move(newPnts);
theParams = std::move(newParams);
}
//=================================================================================================
bool ShapeConstruct_ProjectCurveOnSurface::isAnIsoparametric(const int theNbPnt,
const ArrayOfPnt& thePoints,
const ArrayOfReal& theParams,
bool& theIsTypeU,
bool& theP1OnIso,
gp_Pnt2d& theValueP1,
bool& theP2OnIso,
gp_Pnt2d& theValueP2,
bool& theIsoPar2d3d,
occ::handle<Geom_Curve>& theCIso,
double& theT1,
double& theT2,
ArrayOfReal& theParamsOut) const
{
try
{
OCC_CATCH_SIGNALS
constexpr double prec = Precision::Confusion();
bool isoParam = false;
theIsoPar2d3d = false;
double U1, U2, V1, V2;
mySurf->Bounds(U1, U2, V1, V2);
if (mySurf->Surface()->IsKind(STANDARD_TYPE(Geom_RectangularTrimmedSurface)))
{
occ::handle<Geom_RectangularTrimmedSurface> sTrim =
occ::down_cast<Geom_RectangularTrimmedSurface>(mySurf->Surface());
sTrim->Bounds(U1, U2, V1, V2);
}
gp_Pnt pt;
int mpt[2];
mpt[0] = mpt[1] = 0;
double t, tpar[2] = {0.0, 0.0}, isoValue = 0.;
double mindist2;
double mind2[2];
mindist2 = mind2[0] = mind2[1] = 4 * prec * prec;
theP1OnIso = false;
theP2OnIso = false;
const Bnd_Box* aBox = nullptr;
for (int j = 1; j <= 4; j++)
{
double isoVal = 0.;
bool isoU = false;
occ::handle<Geom_Curve> cI;
double tt1, tt2;
if (j == 1)
{
if (Precision::IsInfinite(U1))
{
continue;
}
cI = mySurf->UIso(U1);
isoU = true;
isoVal = U1;
aBox = &mySurf->GetBoxUF();
}
else if (j == 2)
{
if (Precision::IsInfinite(U2))
{
continue;
}
cI = mySurf->UIso(U2);
isoU = true;
isoVal = U2;
aBox = &mySurf->GetBoxUL();
}
else if (j == 3)
{
if (Precision::IsInfinite(V1))
{
continue;
}
cI = mySurf->VIso(V1);
isoU = false;
isoVal = V1;
aBox = &mySurf->GetBoxVF();
}
else if (j == 4)
{
if (Precision::IsInfinite(V2))
{
continue;
}
cI = mySurf->VIso(V2);
isoU = false;
isoVal = V2;
aBox = &mySurf->GetBoxVL();
}
if (cI.IsNull())
{
continue;
}
if (isoU)
{
tt1 = V1;
tt2 = V2;
}
else
{
tt1 = U1;
tt2 = U2;
}
gp_Pnt ext1, ext2;
cI->D0(tt1, ext1);
cI->D0(tt2, ext2);
gp_Pnt extmi;
cI->D0((tt1 + tt2) / 2, extmi);
if (ext1.IsEqual(ext2, prec) && ext1.IsEqual(extmi, prec))
{
continue;
}
bool PtEQext1 = false;
bool PtEQext2 = false;
double currd2[2], tp[2] = {0, 0};
int mp[2];
for (int i = 0; i < 2; i++)
{
mp[i] = 0;
const int k = (i == 0 ? 1 : theNbPnt);
currd2[i] = thePoints(k).SquareDistance(ext1);
if (currd2[i] <= prec * prec && !PtEQext1)
{
mp[i] = 1;
tp[i] = tt1;
PtEQext1 = true;
continue;
}
currd2[i] = thePoints(k).SquareDistance(ext2);
if (currd2[i] <= prec * prec && !PtEQext2)
{
mp[i] = 2;
tp[i] = tt2;
PtEQext2 = true;
continue;
}
if (mySurf->Surface()->IsKind(STANDARD_TYPE(Geom_SphericalSurface)) && !isoU)
{
continue;
}
if (aBox->IsOut(thePoints(k)))
{
continue;
}
double Cf = cI->FirstParameter();
double Cl = cI->LastParameter();
if (Precision::IsInfinite(Cf))
{
Cf = -1000;
}
if (Precision::IsInfinite(Cl))
{
Cl = +1000;
}
ShapeAnalysis_Curve sac;
const double dist = sac.Project(cI, thePoints(k), prec, pt, t, Cf, Cl);
currd2[i] = dist * dist;
if ((dist <= prec) && (t >= Cf) && (t <= Cl))
{
mp[i] = 3;
tp[i] = t;
}
}
if (mp[0] > 0 && mp[1] > 0 && std::abs(tp[0] - tp[1]) < Precision::PConfusion())
{
continue;
}
if (mp[0] > 0 && (!theP1OnIso || currd2[0] < mind2[0]))
{
theP1OnIso = true;
mind2[0] = currd2[0];
if (isoU)
{
theValueP1.SetCoord(isoVal, tp[0]);
}
else
{
theValueP1.SetCoord(tp[0], isoVal);
}
}
if (mp[1] > 0 && (!theP2OnIso || currd2[1] < mind2[1]))
{
theP2OnIso = true;
mind2[1] = currd2[1];
if (isoU)
{
theValueP2.SetCoord(isoVal, tp[1]);
}
else
{
theValueP2.SetCoord(tp[1], isoVal);
}
}
if (mp[0] <= 0 || mp[1] <= 0)
{
continue;
}
const double md2 = currd2[0] + currd2[1];
if (mindist2 <= md2)
{
continue;
}
mindist2 = md2;
mpt[0] = mp[0];
mpt[1] = mp[1];
tpar[0] = tp[0];
tpar[1] = tp[1];
theIsTypeU = isoU;
isoValue = isoVal;
theCIso = cI;
theT1 = tt1;
theT2 = tt2;
}
if (mpt[0] > 0 && mpt[1] > 0)
{
theP1OnIso = theP2OnIso = true;
if (theIsTypeU)
{
theValueP1.SetCoord(isoValue, tpar[0]);
theValueP2.SetCoord(isoValue, tpar[1]);
}
else
{
theValueP1.SetCoord(tpar[0], isoValue);
theValueP2.SetCoord(tpar[1], isoValue);
}
if (mpt[0] != 3 && mpt[1] != 3)
{
theIsoPar2d3d = true;
for (int i = 2; i < theNbPnt && theIsoPar2d3d; i++)
{
if (tpar[1] > tpar[0])
{
t = theParams(i);
}
else
{
t = theT1 + theT2 - theParams(i);
}
theCIso->D0(t, pt);
if (!thePoints(i).IsEqual(pt, prec))
{
theIsoPar2d3d = false;
}
}
}
if (theIsoPar2d3d)
{
isoParam = true;
}
else
{
double prevParam = tpar[0];
double Cf, Cl;
bool isoByDistance = true;
Cf = theCIso->FirstParameter();
Cl = theCIso->LastParameter();
if (Precision::IsInfinite(Cf))
{
Cf = -1000;
}
if (Precision::IsInfinite(Cl))
{
Cl = +1000;
}
ShapeAnalysis_Curve sac;
for (int i = 2; i < theNbPnt && isoByDistance; i++)
{
const double dist =
sac.NextProject(prevParam, theCIso, thePoints(i), prec, pt, t, Cf, Cl, false);
prevParam = t;
theParamsOut(i) = t;
if ((dist > prec) || (t < Cf) || (t > Cl))
{
isoByDistance = false;
}
}
if (isoByDistance)
{
isoParam = true;
}
}
}
return isoParam;
}
catch (Standard_Failure const& anException)
{
#ifdef OCCT_DEBUG
std::cout << "Warning: ShapeConstruct_ProjectCurveOnSurface::isAnIsoparametric(): Exception: ";
anException.Print(std::cout);
std::cout << std::endl;
#endif
(void)anException;
return false;
}
}