// Created: 2009-01-21 // // Copyright (c) 2009-2013 OPEN CASCADE SAS // // This file is part of commercial software by OPEN CASCADE SAS, // furnished in accordance with the terms and conditions of the contract // and with the inclusion of this copyright notice. // This file or any part thereof may not be provided or otherwise // made available to any third party. // // No ownership title to the software is transferred hereby. // // OPEN CASCADE SAS makes no representation or warranties with respect to the // performance of this software, and specifically disclaims any responsibility // for any damages, special or consequential, connected with its use. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const Standard_Integer MAXPOINTS = 100; static const Standard_Real MyCurvatureTolerance = 0.0001; static Standard_Boolean checkContinuity (const Handle(Geom2d_Curve)& theCurve1, const Handle(Geom2d_Curve)& theCurve2, const Standard_Real theAnglTol); static Geom2dConvert_PPoint getParameter (const gp_XY& theXY1, const Standard_Real theFirstPar, const Standard_Real theLastPar, const Adaptor2d_Curve2d& theCurve); static Standard_Boolean isInflectionPoint (const Standard_Real theParam, const Adaptor2d_Curve2d& theCurve); static Standard_Boolean isInflectionPoint (const Standard_Real theParam, const Geom2dConvert_PPoint& theFirstInf, const Adaptor2d_Curve2d& theCurve, const Standard_Real theAnglTol); //======================================================================= //function : Geom2dConvert_ApproxArcsSegments() //purpose : Constructor //======================================================================= Geom2dConvert_ApproxArcsSegments::Geom2dConvert_ApproxArcsSegments (const Adaptor2d_Curve2d& theCurve, const Standard_Real theTolerance, const Standard_Real theAngleTol) : myCurve (theCurve), myAlloc (new NCollection_IncAllocator(4000)), myTolerance (theTolerance), myAngleTolerance (theAngleTol), mySeqParams (myAlloc), myStatus (StatusNotDone) { myExt[0] = Geom2dConvert_PPoint(myCurve.FirstParameter(), myCurve); myExt[1] = Geom2dConvert_PPoint(myCurve.LastParameter(), myCurve); switch (myCurve.GetType()) { case GeomAbs_Line: { // Create a single line segment. const Standard_Real aDist = myExt[0].Dist(myExt[1]); if (aDist > Precision::Confusion()) { const gp_Ax2d anAx2d(myExt[0].Point(), gp_Vec2d(myExt[0].Point(), myExt[1].Point())); const Handle(Geom2d_Line) aLine = new Geom2d_Line(anAx2d); mySeqCurves.Append(new Geom2d_TrimmedCurve(aLine, 0., aDist)); myStatus = StatusOK; } } break; case GeomAbs_Circle: { // Create a couple of arcs of equal size. const Geom2dConvert_PPoint aPP(.5 *(myExt[0].Parameter() + myExt[1].Parameter()), myCurve); Handle(Geom2d_Curve) aCurve = makeCircle (myExt[0], aPP); if (aCurve.IsNull() == Standard_False) { mySeqCurves.Append(aCurve); aCurve = makeCircle (aPP, myExt[1]); if (aCurve.IsNull() == Standard_False) mySeqCurves.Append(aCurve); } } break; default: makeFreeform(); } // Check status of the calculation if (myStatus == StatusNotDone) { if (mySeqCurves.IsEmpty() == Standard_False) myStatus = StatusOK; else { //std::cout << "GeomConv2d_Approx: no geometry converted." << std::endl; myStatus = StatusError; } } } //======================================================================= //function : makeCircle //purpose : method for creation of circle //======================================================================= Handle(Geom2d_Curve) Geom2dConvert_ApproxArcsSegments::makeCircle (const Geom2dConvert_PPoint& theFirst, const Geom2dConvert_PPoint& theLast) const { Handle(Geom2d_Curve) aResult; gp_Pnt2d aPointM (0.0,0.0); const Standard_Real aParaM = (theFirst.Parameter() + theLast.Parameter()) *.5; myCurve.D0(aParaM, aPointM); GCE2d_MakeArcOfCircle aMakeArc1(theFirst.Point(), aPointM, theLast.Point()); if (aMakeArc1.IsDone()) aResult = aMakeArc1.Value(); //else //std::cout << "makeCircle(): Circle not built" << std::endl; return aResult; } //======================================================================= //function : makeArc //purpose : creation arcs by two points and derivative in the first point /// : parameter isFirst specified direction of the arc. //======================================================================= Standard_Boolean Geom2dConvert_ApproxArcsSegments::makeArc (const Geom2dConvert_PPoint& theParam1, Geom2dConvert_PPoint& theParam2, const Standard_Boolean isFirst, Handle(Geom2d_TrimmedCurve)& theCurve) const { const gp_XY aP1 (theParam1.Point()); const gp_XY aP2 (theParam2.Point()); const gp_XY aVec (isFirst? theParam1.D1() : -theParam1.D1()); // Detect the sense (CCW means positive) const gp_XY aDelta = aP2 - aP1; Standard_Real aSense = aVec ^ aDelta; if (aSense > Precision::Angular()) aSense = 1.; else if (aSense < -Precision::Angular()) aSense = -1.; else { //std::cout << "makeArc(): Arc Not Built" << std::endl; return Standard_False; } // Find the centre of the circle const gp_XY aMiddle = (aP2 + aP1) * 0.5; const Standard_Real prodP1V = aP1 * aVec; const Standard_Real prodDeM = aDelta * aMiddle; const Standard_Real vprodVD = aVec ^ aDelta; const Standard_Real aResolution = gp::Resolution(); if (vprodVD < -aResolution || vprodVD > aResolution) { const gp_Pnt2d aCenter((prodP1V * aDelta.Y() - prodDeM * aVec.Y())/vprodVD, (prodDeM * aVec.X() - prodP1V * aDelta.X())/vprodVD); const Standard_Real aRad = (aCenter.Distance(aP1) + aCenter.Distance(aP2)) * 0.5; const gp_Ax22d ax22d (aCenter, gp_Dir2d(1., 0.), gp_Dir2d(0., 1.)); const gp_Circ2d aCir (ax22d, aRad); const Handle(Geom2d_Circle) Circ = new Geom2d_Circle(aCir); //calculation parameters first and last points of arc. Standard_Real anAlpha1, anAlpha2; if (isFirst) { anAlpha1 = ElCLib::Parameter(aCir, aP1); anAlpha2 = ElCLib::Parameter(aCir, aP2); } else { anAlpha1 = ElCLib::Parameter(aCir, aP2); anAlpha2 = ElCLib::Parameter(aCir, aP1); aSense = -aSense; } if (fabs (anAlpha1 - anAlpha2) < 1e-100) // very small value, just to avoid exact match return Standard_False; // Reverse the circle if the sense is negative if (aSense < 0.) { anAlpha1 = Circ->ReversedParameter(anAlpha1); anAlpha2 = Circ->ReversedParameter(anAlpha2); Circ->Reverse(); } theCurve = new Geom2d_TrimmedCurve(Circ, anAlpha1, anAlpha2); // Correct the direction in the opposite point const gp_XY aRadV = theParam2.Point() - aCenter.XY(); theParam2.SetD1(gp_XY(- aRadV.Y() * aSense, aRadV.X() * aSense)); return Standard_True; } // Algorithm failed, possibly because aVec is normal to the chorde return Standard_False; } //======================================================================= //function : makeLine //purpose : method for creation of line //======================================================================= Handle(Geom2d_TrimmedCurve) Geom2dConvert_ApproxArcsSegments::makeLine (Geom2dConvert_PPoint& theFirst, Geom2dConvert_PPoint& theLast, const Standard_Boolean isCheck) const { Handle(Geom2d_TrimmedCurve) aResult; gp_XY aSlope = theLast.Point() - theFirst.Point(); if (fabs(aSlope.SquareModulus()) < gp::Resolution()) return aResult; gp_Dir2d aDirLine(aSlope); if (isCheck) { if (theFirst.D1().SquareModulus() < gp::Resolution() || theLast.D1().SquareModulus() < gp::Resolution()) return aResult; // Angular continuity (G1) is only checked when the end of the line is not // on the extremity of the curve Standard_Real absAngle[2] = { 0., 0. }; if (theFirst != myExt[0]) { const Standard_Real anAng = aDirLine.Angle(theFirst.D1()); absAngle[0] = (anAng > 0. ? anAng : -anAng); } if (theLast != myExt[1]) { const Standard_Real anAng = aDirLine.Angle(theLast.D1()); absAngle[1] = (anAng > 0. ? anAng : -anAng); } // if the derivatives in the end points differ from the derivative line // more than value of the specified continuity tolerance // then a biarc should be built instead of a line. const Standard_Real aContTolerance = ::Max(myAngleTolerance, 0.01); if (absAngle[0] > aContTolerance || absAngle[1] > aContTolerance) { //std::cout << "makeLine(): Line not built" << std::endl; return aResult; } } // end if (isCheck) //building segment of line GCE2d_MakeSegment aMakeSeg (theFirst.Point(), theLast.Point()); if (aMakeSeg.IsDone()) { Handle(Geom2d_TrimmedCurve) aCurve = aMakeSeg.Value(); if (checkCurve (aCurve, theFirst.Parameter(), theLast.Parameter())) { aResult = aCurve; // correct the derivatives fields in both arguments const gp_XY aNewD1 (theLast.Point() - theFirst.Point()); theFirst.SetD1(aNewD1); theLast.SetD1(aNewD1); } } //else //std::cout << "makeLine(): Line not built" << std::endl; return aResult; } //======================================================================= //function : makeFreeform //purpose : get a sequence of Geom curves from one curve //======================================================================= Standard_Boolean Geom2dConvert_ApproxArcsSegments::makeFreeform() { Geom2dConvert_SequenceOfPPoint seqParamPoints(myAlloc); Geom2dConvert_PPoint* aPrevParam = &myExt[0]; //calculation of the inflection points. getLinearParts(seqParamPoints); const Standard_Boolean isNoInfPoints = seqParamPoints.IsEmpty(); TColGeom2d_SequenceOfCurve aSeqLinearParts; Standard_Boolean isDone (Standard_True); Standard_Integer i; for (i = 1; i < seqParamPoints.Length(); i += 2) { Handle(Geom2d_Curve) aLineCurve; Geom2dConvert_PPoint& aParam0 = seqParamPoints.ChangeValue(i); Geom2dConvert_PPoint& aParam1 = seqParamPoints.ChangeValue(i+1); if (aParam0 != aParam1) //linear part of the curve lies between odd and even values of i. //parameters from parameter's sequence. aLineCurve = makeLine (aParam0, aParam1, Standard_False); aSeqLinearParts.Append(aLineCurve); } for (i = 1; i < seqParamPoints.Length(); i += 2) { //approximation for non-linear part preceding the linear part if (seqParamPoints(i) != * aPrevParam) { const Standard_Integer aLastInd = mySeqCurves.Length(); isDone = makeApproximation (* aPrevParam, seqParamPoints(i)); if (isDone && aLastInd && mySeqCurves.Length() > aLastInd) isDone = checkContinuity(mySeqCurves.Value(aLastInd), mySeqCurves.Value(aLastInd+1), myAngleTolerance); if (!isDone) { myStatus = StatusError; break; } } const Handle(Geom2d_Curve)& aCurve = aSeqLinearParts.Value((i+1)/2); if (aCurve.IsNull() == Standard_False) mySeqCurves.Append(aCurve); else { Geom2dConvert_PPoint& aParam0 = seqParamPoints.ChangeValue(i); Geom2dConvert_PPoint& aParam1 = seqParamPoints.ChangeValue(i+1); const Standard_Integer aLastInd = mySeqCurves.Length(); isDone = makeApproximation (aParam0, aParam1); if (isDone && aLastInd && mySeqCurves.Length() > aLastInd) isDone = checkContinuity(mySeqCurves.Value(aLastInd), mySeqCurves.Value(aLastInd+1), myAngleTolerance); if (!isDone) { myStatus = StatusError; //std::cout << "makeOther: Line not built" << std::endl; break; } } aPrevParam = &seqParamPoints(i+1); } //approximation for non-linear part following the last linear part if (isDone && (* aPrevParam != myExt[1])) { // Case of a closed edge like an ellipse if (isNoInfPoints && (myExt[0].Point() - myExt[1].Point()).Modulus() < myTolerance) { Geom2dConvert_PPoint aPPoint(0.5 * (myExt[0].Parameter() + myExt[1].Parameter()), myCurve); isDone = makeApproximation (myExt[0], aPPoint); if (isDone) isDone = makeApproximation (aPPoint, myExt[1]); } else { isDone = makeApproximation (* aPrevParam, myExt[1]); } if (!isDone) { myStatus = StatusError; //std::cout << "makeOther: Line not built" << std::endl; } } return (mySeqCurves.Length() && myStatus != StatusError); } //======================================================================= //function : getLinearParts //purpose : method for getting inflection points //======================================================================= void Geom2dConvert_ApproxArcsSegments::getLinearParts (Geom2dConvert_SequenceOfPPoint& theSeqPar) { Standard_Integer i; // Fill the sequences with values along the curve mySeqParams.Clear(); Adaptor2d_Curve2d& myCurveMut = const_cast(myCurve); GCPnts_QuasiUniformDeflection aQUDefAlgo (myCurveMut, myTolerance * 0.5); Standard_Boolean isUniformDone = aQUDefAlgo.IsDone(); gp_XY aLastPnt(myExt[0].Point()); if (isUniformDone) { for (i = 1; i <= aQUDefAlgo.NbPoints(); i++) { const Geom2dConvert_PPoint aPP (aQUDefAlgo.Parameter(i), myCurve); mySeqParams.Append(aPP); aLastPnt = aPP.Point(); } } else { const Standard_Real aParamStep = (myExt[1].Parameter() - myExt[0].Parameter()) / MAXPOINTS; for (i = 1; i <= MAXPOINTS; i++) { const Standard_Real aParam = myExt[0].Parameter() + aParamStep * i; const Geom2dConvert_PPoint aPP (aParam, myCurve); mySeqParams.Append(aPP); aLastPnt = aPP.Point(); } } //check if the curve may be linearised gp_XY aDir = myExt[1].Point() - myExt[0].Point(); const Standard_Real aMod2 = aDir.SquareModulus(); if (aMod2 > Precision::Confusion()) { Standard_Boolean isLinear = Standard_True; aDir /= sqrt(aMod2); for (i = 1; i <= mySeqParams.Length(); i++) { // Distance from point "i" to the segment between two extremities const Standard_Real aDist = aDir ^ (mySeqParams(i).Point() - myExt[0].Point()); if (aDist > myTolerance * 0.5 || aDist < -myTolerance * 0.5) { isLinear = Standard_False; break; } } if (isLinear) { theSeqPar.Append(myExt[0]); theSeqPar.Append(myExt[1]); return; } } //check if point for First Parameter is inflection point. Standard_Integer indStartLinear (0); Geom2dConvert_PPoint aLastInflParam = myExt[0]; Geom2dConvert_PPoint aFirstInflParam = myExt[0]; // Getting further inflection points with step by parameter. // The point with index 1 is the same as myExt[0] for (i = 1; i <= mySeqParams.Length(); i++) { const Geom2dConvert_PPoint& aCurParam = mySeqParams(i); if (indStartLinear) { Standard_Boolean isStillInflectionFirst = isInflectionPoint (aFirstInflParam.Parameter(), aCurParam, myCurve, myAngleTolerance); if (isInflectionPoint (aCurParam.Parameter(), aFirstInflParam, myCurve, myAngleTolerance)) { aLastInflParam = mySeqParams(i); while (isStillInflectionFirst == Standard_False) { if (++indStartLinear >= i) { indStartLinear = 0; break; } aFirstInflParam = mySeqParams(indStartLinear); isStillInflectionFirst = isInflectionPoint (aFirstInflParam.Parameter(), aCurParam, myCurve, myAngleTolerance); } } else { // Add the interval in the output sequence // The interval is added only if it is more than 10 times the tolerance aLastInflParam = findInflection (aLastInflParam, aCurParam); if (!isInflectionPoint (aFirstInflParam.Parameter(), aLastInflParam, myCurve, myAngleTolerance)) { aFirstInflParam = findInflection (aLastInflParam, aFirstInflParam); } const Standard_Real aDist((aFirstInflParam.Point() - aLastInflParam.Point()).Modulus()); if (aFirstInflParam.Parameter() < aLastInflParam.Parameter() && aDist > 10 * myTolerance) { theSeqPar.Append(aFirstInflParam); theSeqPar.Append(aLastInflParam); } indStartLinear = 0; } } else if (isInflectionPoint (aCurParam.Parameter(), myCurve)) { aLastInflParam = aCurParam; if (i > 1) aFirstInflParam = findInflection (aCurParam, mySeqParams(i-1)); indStartLinear = i; } } const Standard_Real aDist((aFirstInflParam.Point() - myExt[1].Point()).Modulus()); if (indStartLinear && aDist > 10 * myTolerance) { theSeqPar.Append(aFirstInflParam); theSeqPar.Append(myExt[1]); } } //======================================================================= //function : findInflection //purpose : Dichotomic search of the boundary of inflection interval, between // two parameters on the Curve //======================================================================= Geom2dConvert_PPoint Geom2dConvert_ApproxArcsSegments::findInflection (const Geom2dConvert_PPoint& theParamIsInfl, const Geom2dConvert_PPoint& theParamNoInfl) const { Standard_Real aLower (theParamIsInfl.Parameter()); Standard_Real anUpper (theParamNoInfl.Parameter()); Standard_Real aTest(0.); for (Standard_Integer i = 0; i < 3; i++) { // 3 iterations aTest = (aLower + anUpper) * 0.5; if (isInflectionPoint (aTest, theParamIsInfl, myCurve, myAngleTolerance)) aLower = aTest; else anUpper = aTest; } return Geom2dConvert_PPoint(aTest, myCurve); } //======================================================================= //function : makeApproximation //purpose : make approximation non-linear part of the other curve //======================================================================= Standard_Boolean Geom2dConvert_ApproxArcsSegments::makeApproximation (Geom2dConvert_PPoint& theFirstParam, Geom2dConvert_PPoint& theLastParam) { // if difference between parameters is less than Precision::PConfusion //approximation was not made. Standard_Boolean isDone = Standard_False; if (theLastParam != theFirstParam) { const Standard_Real aDistance = (theFirstParam.Point() - theLastParam.Point()).Modulus(); if (aDistance < myTolerance) { const Handle(Geom2d_Curve) aCurve = makeLine(theFirstParam, theLastParam, Standard_True); isDone = !aCurve.IsNull(); if (isDone && mySeqCurves.Length()) isDone = checkContinuity(mySeqCurves.Last(), aCurve, myAngleTolerance); if (isDone || aDistance < Precision::Confusion()) { mySeqCurves.Append(aCurve); return isDone; } } //calculate biarc isDone = calculateBiArcs (theFirstParam, theLastParam); // if biarc was not calculated calculation is repeated on half the interval. if (!isDone) { Geom2dConvert_PPoint aParaM (theFirstParam.Parameter() + (theLastParam.Parameter() - theFirstParam.Parameter()) * 0.55, myCurve); isDone = makeApproximation (theFirstParam, aParaM); if (isDone) isDone = makeApproximation (aParaM, theLastParam); } } return isDone; } //======================================================================= //function : calculateBiArcs //purpose : method for calculation of the biarcs. //======================================================================= Standard_Boolean Geom2dConvert_ApproxArcsSegments::calculateBiArcs (Geom2dConvert_PPoint& theFirstParam, Geom2dConvert_PPoint& theLastParam) { const Standard_Real aResolution = gp::Resolution(); if (theFirstParam.D1().SquareModulus() < aResolution || theLastParam.D1().SquareModulus() < aResolution) { //std::cout << "calculateBiArcs(): bad initial data" << std::endl; return Standard_False; } const gp_XY aPnt[2] = { theFirstParam.Point(), theLastParam.Point() }; gp_Dir2d aDir[2] = { theFirstParam.D1(), theLastParam.D1() }; // Try to approximate the curve by a single arc. The criterion for that is // more rigid if the curve is the entire input curve // (possible pb. connecting with other boundaries) const gp_Vec2d aDelta (aPnt[1] - aPnt[0]); Standard_Real anAngle1 = aDelta.Angle(gp_Vec2d(aDir[0])); if (anAngle1 < 0.) anAngle1 = -anAngle1; Standard_Real anAngle2 = aDelta.Angle(gp_Vec2d(aDir[1])); if (anAngle2 < 0.) anAngle2 = -anAngle2; //in the case when two angles are equal one arc can be built. Standard_Real anAngleThreshold (Precision::Angular() * 10.); if (theFirstParam != myExt[0] || theLastParam != myExt[1]) anAngleThreshold = myAngleTolerance * 0.1; if (fabs(anAngle1 - anAngle2) < anAngleThreshold) { Handle(Geom2d_TrimmedCurve) aCurve; // protect the theLastParam from modification of D1, when // the created arc is rejected. Geom2dConvert_PPoint aLastParam (theLastParam); if (!makeArc (theFirstParam, aLastParam, Standard_True, aCurve)) return Standard_False; if (checkCurve(aCurve, theFirstParam.Parameter(), aLastParam.Parameter())) { theLastParam = aLastParam; mySeqCurves.Append(aCurve); return Standard_True; } } // if one arc was not built or for other cases biarc will be built // method for building biarc was taken from article Ahmad H. Nasri et al. // "A Recursive Subdivision Algorithm for Piecewise Circular Spline", // Computer Graphics Forum, 2001. // definition of point of intersection two tangent directions in the points // corresponding FirstParameter and LastParameter. aDir[1].Reverse(); // Direct calculation of intersection point, replaces a class call below const Standard_Real aProd [3] = { aPnt[0] ^ aDir[0].XY(), aPnt[1] ^ aDir[1].XY(), aDir[1] ^ aDir[0].XY() }; gp_XY aIntPoint((aProd[0] * aDir[1].X() - aProd[1] * aDir[0].X()) / aProd[2], (aProd[0] * aDir[1].Y() - aProd[1] * aDir[0].Y()) / aProd[2]); const gp_XY aDiff[2] = { aIntPoint - aPnt[0], aIntPoint - aPnt[1] }; if (aDiff[0] * aDir[0].XY() < 0. || aDiff[1] * aDir[1].XY() < 0.) { return Standard_False; } //calculation middle point for building biarc. const Standard_Real ad1 = aDiff[0].Modulus(); const Standard_Real ad2 = aDiff[1].Modulus(); const Standard_Real ad12 = aDelta.Magnitude(); const Standard_Real aB1 = ad1 / (ad1 + ad2); if (fabs(aB1 - 0.5) < 0.0001) return Standard_False; gp_XY aXY[2] = { aPnt[0] + aDir[0].XY() * ad12 * ad1 / (ad12 + ad1 + ad2), aPnt[1] + aDir[1].XY() * ad12 * ad2 / (ad12 + ad1 + ad2) }; const gp_XY aXYmidArc (aXY[0] + aB1*(aXY[1] - aXY[0])); Geom2dConvert_PPoint aParamMidArc = getParameter (aXYmidArc, theFirstParam.Parameter(), theLastParam.Parameter(), myCurve); //building first arc from biarc. Handle(Geom2d_TrimmedCurve) aCurve1, aCurve2; if (!makeArc (theFirstParam, aParamMidArc, Standard_True, aCurve1)) return Standard_False; if (!checkCurve (aCurve1, theFirstParam.Parameter(), aParamMidArc.Parameter())) return Standard_False; //building second arc from biarc. if (makeArc (theLastParam, aParamMidArc, Standard_False, aCurve2)) { if (checkCurve (aCurve2, aParamMidArc.Parameter(), theLastParam.Parameter())) { mySeqCurves.Append(aCurve1); mySeqCurves.Append(aCurve2); return Standard_True; } } return Standard_False; } //======================================================================= //function : calculateLines //purpose : method for calculation of the linear interpolation. //======================================================================= Standard_Boolean Geom2dConvert_ApproxArcsSegments::calculateLines (Geom2dConvert_PPoint& theFirstParam, Geom2dConvert_PPoint& theLastParam) { Geom2dConvert_PPoint* aPrevParam = &theFirstParam; for (int i = 1; i <= mySeqParams.Length(); i++) { Geom2dConvert_PPoint& aCurParam = mySeqParams.ChangeValue(i); if (aCurParam.Parameter() < (*aPrevParam).Parameter()) { continue; } if (aCurParam.Parameter() > theLastParam.Parameter()) { break; } // build line segment if (aCurParam != *aPrevParam) { const Standard_Real aDistance = (aCurParam.Point() - (*aPrevParam).Point()).Modulus(); if (aDistance > myTolerance) { const Handle(Geom2d_Curve) aCurve = makeLine(*aPrevParam, aCurParam, Standard_False); if (aCurve.IsNull()) { return Standard_False; } mySeqCurves.Append(aCurve); aPrevParam = &mySeqParams(i); } } } return Standard_True; } //======================================================================= //function : checkCurve //purpose : method for checking max deflection Geom curve from Adaptor Curve //======================================================================= Standard_Boolean Geom2dConvert_ApproxArcsSegments::checkCurve (const Handle(Geom2d_Curve)& aCurve, const Standard_Real theFirstParam, const Standard_Real theLastParam) const { if (aCurve.IsNull()) return Standard_False; // check fails on empty input Standard_Boolean isUniformDone = !mySeqParams.IsEmpty(); //calculation sequence of the parameters or step by parameter. Standard_Integer aNbPnts = (isUniformDone ? mySeqParams.Length() :MAXPOINTS); Standard_Real aParamStep = (theLastParam - theFirstParam)/MAXPOINTS; Handle(Geom2d_Curve) aCurve1 = aCurve; Handle(Geom2d_TrimmedCurve) aTrCurve = Handle(Geom2d_TrimmedCurve)::DownCast(aCurve); if (!aTrCurve.IsNull()) aCurve1 = aTrCurve->BasisCurve(); gp_Lin2d aLin2d; gp_Circ2d aCirc2d; Handle(Geom2d_Line) aGeomLine = Handle(Geom2d_Line)::DownCast(aCurve1); Standard_Boolean isLine = (!aGeomLine.IsNull()); Standard_Boolean isCircle = (!isLine); if (isLine) aLin2d = aGeomLine->Lin2d(); else { Handle(Geom2d_Circle) aGeomCircle = Handle(Geom2d_Circle)::DownCast(aCurve1); isCircle = (!aGeomCircle.IsNull()); if (isCircle) aCirc2d = aGeomCircle->Circ2d(); else return Standard_False; } //calculation of the max deflection points from CurveAdaptor from Geom curve. Standard_Boolean isLess = Standard_True; Standard_Integer i = 1; for (; i <= aNbPnts && isLess; i++) { Standard_Real aParam = (isUniformDone ? mySeqParams.Value(i).Parameter() : (theFirstParam + i*aParamStep)); if (aParam < (theFirstParam - Precision::PConfusion()) || aParam > (theLastParam + Precision::PConfusion())) continue; //getting point from adaptor curve by specified parameter. gp_Pnt2d aPointAdaptor(0., 0.); gp_Pnt2d aProjPoint(0., 0.); myCurve.D0(aParam, aPointAdaptor); Standard_Real aParameterCurve = 0.0; //getting point from geom curve by specified parameter. if (isLine) { aParameterCurve = ElCLib::Parameter(aLin2d, aPointAdaptor); aProjPoint = ElCLib::Value(aParameterCurve, aLin2d); } else if (isCircle) { aParameterCurve = ElCLib::Parameter(aCirc2d, aPointAdaptor); aProjPoint = ElCLib::Value(aParameterCurve, aCirc2d); } isLess = (aProjPoint.Distance(aPointAdaptor) < myTolerance + Precision::PConfusion()); } return isLess; } //======================================================================= //function : checkContinuity //purpose : check continuty first derivative between two curves. //======================================================================= Standard_Boolean checkContinuity (const Handle(Geom2d_Curve)& theCurve1, const Handle(Geom2d_Curve)& theCurve2, const Standard_Real theAngleTol) { gp_Vec2d v11,v21; gp_Pnt2d p1, p2; theCurve1->D1(theCurve1->LastParameter(), p1, v11); theCurve2->D1(theCurve2->FirstParameter(), p2, v21); //check continuity with the specified tolerance. return (v11.IsParallel(v21, theAngleTol)); } //======================================================================= //function : getParameter //purpose : getting the nearest point on AdaptorCurve to the specified point. //======================================================================= Geom2dConvert_PPoint getParameter (const gp_XY& theXY1, const Standard_Real theFirstParam, const Standard_Real theLastParam, const Adaptor2d_Curve2d& theCurve) { Geom2dConvert_PPoint aResult; Standard_Real prevParam = theFirstParam; Standard_Real af1 = theFirstParam; Standard_Real af2 = theLastParam; // for finding nearest point use method half division. Standard_Real aMinDist = RealLast(); Standard_Integer i = 1; for (; i <= MAXPOINTS; i++) { aResult = Geom2dConvert_PPoint(af1, theCurve); Standard_Real adist1 = (theXY1 - aResult.Point()).Modulus(); if (adist1 < Precision::Confusion()) { return aResult; } aResult = Geom2dConvert_PPoint(af2, theCurve); Standard_Real adist2 = (theXY1 - aResult.Point()).Modulus(); if (adist2 < Precision::Confusion()) { return aResult; } if (aMinDist <= adist2 -Precision::Confusion() && aMinDist <= adist1 -Precision::Confusion()) { break; } if (adist1 < adist2 -Precision::Confusion()) { prevParam = af1; aMinDist = adist1; af2 = (af1 + af2) * 0.5; } else { prevParam = af2; aMinDist = adist2; af1 = (af1 + af2) * 0.5; } } aResult = Geom2dConvert_PPoint(prevParam, theCurve); return aResult; } //======================================================================= //function : isInflectionPoint //purpose : method calculating that point specified by parameter // is inflection point //======================================================================= Standard_Boolean isInflectionPoint (const Standard_Real theParam, const Adaptor2d_Curve2d& theCurve) { gp_Pnt2d aP1; gp_Vec2d aD1, aD2; theCurve.D2(theParam, aP1, aD1, aD2); const Standard_Real aSqMod = aD1.XY().SquareModulus(); const Standard_Real aCurvature = fabs (aD1.XY() ^ aD2.XY()) / (aSqMod * sqrt(aSqMod)); return (aCurvature < MyCurvatureTolerance); } //======================================================================= //function : isInflectionPoint //purpose : method calculating that point specified by parameter // is inflection point //======================================================================= Standard_Boolean isInflectionPoint (const Standard_Real theParam, const Geom2dConvert_PPoint& theFirstInfl, const Adaptor2d_Curve2d& theCurve, const Standard_Real theAngleTol) { gp_Pnt2d aP1; gp_Vec2d aD1, aD2; theCurve.D2(theParam, aP1, aD1, aD2); const Standard_Real aSqMod = aD1.XY().SquareModulus(); const Standard_Real aCurvature = fabs (aD1.XY() ^ aD2.XY()) / (aSqMod * sqrt(aSqMod)); Standard_Real aContAngle = fabs(gp_Vec2d(aP1.XY() - theFirstInfl.Point()).Angle(aD1)); aContAngle = ::Min(aContAngle, fabs(M_PI - aContAngle)); return (aCurvature < MyCurvatureTolerance && aContAngle < theAngleTol); }