1 // Created on: 2015-09-21
2 // Copyright (c) 2015 OPEN CASCADE SAS
4 // This file is part of Open CASCADE Technology software library.
6 // This library is free software; you can redistribute it and/or modify it under
7 // the terms of the GNU Lesser General Public License version 2.1 as published
8 // by the Free Software Foundation, with special exception defined in the file
9 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
10 // distribution for complete text of the license and disclaimer of any warranty.
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
15 #include <GeomEvaluator_OffsetCurve.hxx>
17 #include <GeomAdaptor_HCurve.hxx>
18 #include <Standard_NullValue.hxx>
21 IMPLEMENT_STANDARD_RTTIEXT(GeomEvaluator_OffsetCurve,GeomEvaluator_Curve)
23 GeomEvaluator_OffsetCurve::GeomEvaluator_OffsetCurve(
24 const Handle(Geom_Curve)& theBase,
25 const Standard_Real theOffset,
26 const gp_Dir& theDirection)
27 : GeomEvaluator_Curve(),
30 myOffsetDir(theDirection)
34 GeomEvaluator_OffsetCurve::GeomEvaluator_OffsetCurve(
35 const Handle(GeomAdaptor_HCurve)& theBase,
36 const Standard_Real theOffset,
37 const gp_Dir& theDirection)
38 : GeomEvaluator_Curve(),
39 myBaseAdaptor(theBase),
41 myOffsetDir(theDirection)
45 void GeomEvaluator_OffsetCurve::D0(const Standard_Real theU,
46 gp_Pnt& theValue) const
49 BaseD1(theU, theValue, aD1);
50 CalculateD0(theValue, aD1);
53 void GeomEvaluator_OffsetCurve::D1(const Standard_Real theU,
58 BaseD2(theU, theValue, theD1, aD2);
59 CalculateD1(theValue, theD1, aD2);
62 void GeomEvaluator_OffsetCurve::D2(const Standard_Real theU,
68 BaseD3(theU, theValue, theD1, theD2, aD3);
70 Standard_Boolean isDirectionChange = Standard_False;
71 if (theD1.SquareMagnitude() <= gp::Resolution())
74 isDirectionChange = AdjustDerivative(3, theU, theD1, theD2, aD3, aDummyD4);
77 CalculateD2(theValue, theD1, theD2, aD3, isDirectionChange);
80 void GeomEvaluator_OffsetCurve::D3(const Standard_Real theU,
87 BaseD4(theU, theValue, theD1, theD2, theD3, aD4);
89 Standard_Boolean isDirectionChange = Standard_False;
90 if (theD1.SquareMagnitude() <= gp::Resolution())
91 isDirectionChange = AdjustDerivative(4, theU, theD1, theD2, theD3, aD4);
93 CalculateD3(theValue, theD1, theD2, theD3, aD4, isDirectionChange);
96 gp_Vec GeomEvaluator_OffsetCurve::DN(const Standard_Real theU,
97 const Standard_Integer theDeriv) const
99 Standard_RangeError_Raise_if(theDeriv < 1, "GeomEvaluator_OffsetCurve::DN(): theDeriv < 1");
109 D2(theU, aPnt, aDummy, aDN);
112 D3(theU, aPnt, aDummy, aDummy, aDN);
115 aDN = BaseDN(theU, theDeriv);
121 void GeomEvaluator_OffsetCurve::BaseD0(const Standard_Real theU,
122 gp_Pnt& theValue) const
124 if (!myBaseAdaptor.IsNull())
125 myBaseAdaptor->D0(theU, theValue);
127 myBaseCurve->D0(theU, theValue);
130 void GeomEvaluator_OffsetCurve::BaseD1(const Standard_Real theU,
134 if (!myBaseAdaptor.IsNull())
135 myBaseAdaptor->D1(theU, theValue, theD1);
137 myBaseCurve->D1(theU, theValue, theD1);
140 void GeomEvaluator_OffsetCurve::BaseD2(const Standard_Real theU,
145 if (!myBaseAdaptor.IsNull())
146 myBaseAdaptor->D2(theU, theValue, theD1, theD2);
148 myBaseCurve->D2(theU, theValue, theD1, theD2);
151 void GeomEvaluator_OffsetCurve::BaseD3(const Standard_Real theU,
157 if (!myBaseAdaptor.IsNull())
158 myBaseAdaptor->D3(theU, theValue, theD1, theD2, theD3);
160 myBaseCurve->D3(theU, theValue, theD1, theD2, theD3);
163 void GeomEvaluator_OffsetCurve::BaseD4(const Standard_Real theU,
170 if (!myBaseAdaptor.IsNull())
172 myBaseAdaptor->D3(theU, theValue, theD1, theD2, theD3);
173 theD4 = myBaseAdaptor->DN(theU, 4);
177 myBaseCurve->D3(theU, theValue, theD1, theD2, theD3);
178 theD4 = myBaseCurve->DN(theU, 4);
182 gp_Vec GeomEvaluator_OffsetCurve::BaseDN(const Standard_Real theU,
183 const Standard_Integer theDeriv) const
185 if (!myBaseAdaptor.IsNull())
186 return myBaseAdaptor->DN(theU, theDeriv);
187 return myBaseCurve->DN(theU, theDeriv);
191 void GeomEvaluator_OffsetCurve::CalculateD0( gp_Pnt& theValue,
192 const gp_Vec& theD1) const
194 gp_XYZ Ndir = (theD1.XYZ()).Crossed(myOffsetDir.XYZ());
195 Standard_Real R = Ndir.Modulus();
196 if (R <= gp::Resolution())
197 throw Standard_NullValue("GeomEvaluator_OffsetCurve: Undefined normal vector "
198 "because tangent vector has zero-magnitude!");
200 Ndir.Multiply(myOffset / R);
201 theValue.ChangeCoord().Add(Ndir);
204 void GeomEvaluator_OffsetCurve::CalculateD1( gp_Pnt& theValue,
206 const gp_Vec& theD2) const
208 // P(u) = p(u) + Offset * Ndir / R
209 // with R = || p' ^ V|| and Ndir = P' ^ direction (local normal direction)
211 // P'(u) = p'(u) + (Offset / R**2) * (DNdir/DU * R - Ndir * (DR/R))
213 gp_XYZ Ndir = (theD1.XYZ()).Crossed(myOffsetDir.XYZ());
214 gp_XYZ DNdir = (theD2.XYZ()).Crossed(myOffsetDir.XYZ());
215 Standard_Real R2 = Ndir.SquareModulus();
216 Standard_Real R = Sqrt(R2);
217 Standard_Real R3 = R * R2;
218 Standard_Real Dr = Ndir.Dot(DNdir);
219 if (R3 <= gp::Resolution()) {
220 if (R2 <= gp::Resolution())
221 throw Standard_NullValue("GeomEvaluator_OffsetCurve: Null derivative");
222 //We try another computation but the stability is not very good.
224 DNdir.Subtract(Ndir.Multiplied(Dr / R));
225 DNdir.Multiply(myOffset / R2);
228 // Same computation as IICURV in EUCLID-IS because the stability is better
229 DNdir.Multiply(myOffset / R);
230 DNdir.Subtract(Ndir.Multiplied(myOffset * Dr / R3));
233 Ndir.Multiply(myOffset / R);
235 theValue.ChangeCoord().Add(Ndir);
237 theD1.Add(gp_Vec(DNdir));
240 void GeomEvaluator_OffsetCurve::CalculateD2( gp_Pnt& theValue,
244 const Standard_Boolean theIsDirChange) const
246 // P(u) = p(u) + Offset * Ndir / R
247 // with R = || p' ^ V|| and Ndir = P' ^ direction (local normal direction)
249 // P'(u) = p'(u) + (Offset / R**2) * (DNdir/DU * R - Ndir * (DR/R))
251 // P"(u) = p"(u) + (Offset / R) * (D2Ndir/DU - DNdir * (2.0 * Dr/ R**2) +
252 // Ndir * ( (3.0 * Dr**2 / R**4) - (D2r / R**2)))
254 gp_XYZ Ndir = (theD1.XYZ()).Crossed(myOffsetDir.XYZ());
255 gp_XYZ DNdir = (theD2.XYZ()).Crossed(myOffsetDir.XYZ());
256 gp_XYZ D2Ndir = (theD3.XYZ()).Crossed(myOffsetDir.XYZ());
257 Standard_Real R2 = Ndir.SquareModulus();
258 Standard_Real R = Sqrt(R2);
259 Standard_Real R3 = R2 * R;
260 Standard_Real R4 = R2 * R2;
261 Standard_Real R5 = R3 * R2;
262 Standard_Real Dr = Ndir.Dot(DNdir);
263 Standard_Real D2r = Ndir.Dot(D2Ndir) + DNdir.Dot(DNdir);
265 if (R5 <= gp::Resolution()) {
266 if (R4 <= gp::Resolution())
267 throw Standard_NullValue("GeomEvaluator_OffsetCurve: Null derivative");
268 //We try another computation but the stability is not very good
272 D2Ndir.Subtract(DNdir.Multiplied(2.0 * Dr / R2));
273 D2Ndir.Add(Ndir.Multiplied(((3.0 * Dr * Dr) / R4) - (D2r / R2)));
274 D2Ndir.Multiply(myOffset / R);
278 DNdir.Subtract(Ndir.Multiplied(Dr / R));
279 DNdir.Multiply(myOffset / R2);
282 // Same computation as IICURV in EUCLID-IS because the stability is better.
284 D2Ndir.Multiply(myOffset / R);
285 D2Ndir.Subtract(DNdir.Multiplied(2.0 * myOffset * Dr / R3));
286 D2Ndir.Add(Ndir.Multiplied(myOffset * (((3.0 * Dr * Dr) / R5) - (D2r / R3))));
289 DNdir.Multiply(myOffset / R);
290 DNdir.Subtract(Ndir.Multiplied(myOffset * Dr / R3));
293 Ndir.Multiply(myOffset / R);
295 theValue.ChangeCoord().Add(Ndir);
297 theD1.Add(gp_Vec(DNdir));
301 theD2.Add(gp_Vec(D2Ndir));
304 void GeomEvaluator_OffsetCurve::CalculateD3( gp_Pnt& theValue,
309 const Standard_Boolean theIsDirChange) const
311 // P(u) = p(u) + Offset * Ndir / R
312 // with R = || p' ^ V|| and Ndir = P' ^ direction (local normal direction)
314 // P'(u) = p'(u) + (Offset / R**2) * (DNdir/DU * R - Ndir * (DR/R))
316 // P"(u) = p"(u) + (Offset / R) * (D2Ndir/DU - DNdir * (2.0 * Dr/ R**2) +
317 // Ndir * ( (3.0 * Dr**2 / R**4) - (D2r / R**2)))
319 //P"'(u) = p"'(u) + (Offset / R) * (D3Ndir - (3.0 * Dr/R**2) * D2Ndir -
320 // (3.0 * D2r / R2) * DNdir + (3.0 * Dr * Dr / R4) * DNdir -
321 // (D3r/R2) * Ndir + (6.0 * Dr * Dr / R4) * Ndir +
322 // (6.0 * Dr * D2r / R4) * Ndir - (15.0 * Dr* Dr* Dr /R6) * Ndir
324 gp_XYZ Ndir = (theD1.XYZ()).Crossed(myOffsetDir.XYZ());
325 gp_XYZ DNdir = (theD2.XYZ()).Crossed(myOffsetDir.XYZ());
326 gp_XYZ D2Ndir = (theD3.XYZ()).Crossed(myOffsetDir.XYZ());
327 gp_XYZ D3Ndir = (theD4.XYZ()).Crossed(myOffsetDir.XYZ());
328 Standard_Real R2 = Ndir.SquareModulus();
329 Standard_Real R = Sqrt(R2);
330 Standard_Real R3 = R2 * R;
331 Standard_Real R4 = R2 * R2;
332 Standard_Real R5 = R3 * R2;
333 Standard_Real R6 = R3 * R3;
334 Standard_Real R7 = R5 * R2;
335 Standard_Real Dr = Ndir.Dot(DNdir);
336 Standard_Real D2r = Ndir.Dot(D2Ndir) + DNdir.Dot(DNdir);
337 Standard_Real D3r = Ndir.Dot(D3Ndir) + 3.0 * DNdir.Dot(D2Ndir);
338 if (R7 <= gp::Resolution()) {
339 if (R6 <= gp::Resolution())
340 throw Standard_NullValue("CSLib_Offset: Null derivative");
342 D3Ndir.Subtract(D2Ndir.Multiplied(3.0 * Dr / R2));
343 D3Ndir.Subtract(DNdir.Multiplied(3.0 * ((D2r / R2) + (Dr*Dr / R4))));
344 D3Ndir.Add(Ndir.Multiplied(6.0*Dr*Dr / R4 + 6.0*Dr*D2r / R4 - 15.0*Dr*Dr*Dr / R6 - D3r));
345 D3Ndir.Multiply(myOffset / R);
348 D2Ndir.Subtract(DNdir.Multiplied(2.0 * Dr / R2));
349 D2Ndir.Subtract(Ndir.Multiplied((3.0 * Dr * Dr / R4) - (D2r / R2)));
350 D2Ndir.Multiply(myOffset / R);
353 DNdir.Subtract(Ndir.Multiplied(Dr / R));
354 DNdir.Multiply(myOffset / R2);
359 D3Ndir.Subtract(D2Ndir.Multiplied(3.0 * Dr / R3));
360 D3Ndir.Subtract(DNdir.Multiplied((3.0 * ((D2r / R3) + (Dr*Dr) / R5))));
361 D3Ndir.Add(Ndir.Multiplied(6.0*Dr*Dr / R5 + 6.0*Dr*D2r / R5 - 15.0*Dr*Dr*Dr / R7 - D3r));
362 D3Ndir.Multiply(myOffset);
365 D2Ndir.Subtract(DNdir.Multiplied(2.0 * Dr / R3));
366 D2Ndir.Subtract(Ndir.Multiplied((3.0 * Dr * Dr / R5) - (D2r / R3)));
367 D2Ndir.Multiply(myOffset);
369 DNdir.Multiply(myOffset / R);
370 DNdir.Subtract(Ndir.Multiplied(myOffset * Dr / R3));
373 Ndir.Multiply(myOffset / R);
375 theValue.ChangeCoord().Add(Ndir);
377 theD1.Add(gp_Vec(DNdir));
379 theD2.Add(gp_Vec(D2Ndir));
383 theD3.Add(gp_Vec(D2Ndir));
387 Standard_Boolean GeomEvaluator_OffsetCurve::AdjustDerivative(
388 const Standard_Integer theMaxDerivative, const Standard_Real theU,
389 gp_Vec& theD1, gp_Vec& theD2, gp_Vec& theD3, gp_Vec& theD4) const
391 static const Standard_Real aTol = gp::Resolution();
392 static const Standard_Real aMinStep = 1e-7;
393 static const Standard_Integer aMaxDerivOrder = 3;
395 Standard_Boolean isDirectionChange = Standard_False;
396 Standard_Real anUinfium;
397 Standard_Real anUsupremum;
398 if (!myBaseAdaptor.IsNull())
400 anUinfium = myBaseAdaptor->FirstParameter();
401 anUsupremum = myBaseAdaptor->LastParameter();
405 anUinfium = myBaseCurve->FirstParameter();
406 anUsupremum = myBaseCurve->LastParameter();
409 static const Standard_Real DivisionFactor = 1.e-3;
411 if ((anUsupremum >= RealLast()) || (anUinfium <= RealFirst()))
414 du = anUsupremum - anUinfium;
416 const Standard_Real aDelta = Max(du * DivisionFactor, aMinStep);
418 //Derivative is approximated by Taylor-series
419 Standard_Integer anIndex = 1; //Derivative order
424 V = BaseDN(theU, ++anIndex);
425 } while ((V.SquareMagnitude() <= aTol) && anIndex < aMaxDerivOrder);
429 if (theU - anUinfium < aDelta)
435 BaseD0(Min(theU, u), P1);
436 BaseD0(Max(theU, u), P2);
439 isDirectionChange = V.Dot(V1) < 0.0;
440 Standard_Real aSign = isDirectionChange ? -1.0 : 1.0;
443 gp_Vec* aDeriv[3] = { &theD2, &theD3, &theD4 };
444 for (Standard_Integer i = 1; i < theMaxDerivative; i++)
445 *(aDeriv[i - 1]) = BaseDN(theU, anIndex + i) * aSign;
447 return isDirectionChange;