1 // Created on: 2016-04-19
2 // Copyright (c) 2016 OPEN CASCADE SAS
3 // Created by: Oleg AGASHIN
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <BRepMesh_NURBSRangeSplitter.hxx>
17
18 #include <algorithm>
19 #include <BRepMesh_GeomTool.hxx>
20 #include <GeomAdaptor_Curve.hxx>
21 #include <GeomLib.hxx>
22 #include <IMeshData_Edge.hxx>
23 #include <IMeshData_Wire.hxx>
24 #include <NCollection_Handle.hxx>
25
26 namespace
27 {
28 class AnalyticalFilter
29 {
30 public:
31 //! Constructor.
AnalyticalFilter(const IMeshData::IFaceHandle & theDFace,const GeomAbs_IsoType theIsoType,const Handle (IMeshData::SequenceOfReal)& theParams,const Handle (IMeshData::SequenceOfReal)& theControlParams,const Handle (IMeshData::MapOfReal)& theParamsForbiddenToRemove,const Handle (IMeshData::MapOfReal)& theControlParamsForbiddenToRemove)32 AnalyticalFilter(
33 const IMeshData::IFaceHandle& theDFace,
34 const GeomAbs_IsoType theIsoType,
35 const Handle(IMeshData::SequenceOfReal)& theParams,
36 const Handle(IMeshData::SequenceOfReal)& theControlParams,
37 const Handle(IMeshData::MapOfReal)& theParamsForbiddenToRemove,
38 const Handle(IMeshData::MapOfReal)& theControlParamsForbiddenToRemove)
39 : myDFace(theDFace),
40 mySurface(myDFace->GetSurface()->Surface().Surface()),
41 myIsoU(theIsoType == GeomAbs_IsoU),
42 myParams(theParams),
43 myControlParams(theControlParams),
44 myParamsForbiddenToRemove(theParamsForbiddenToRemove),
45 myControlParamsForbiddenToRemove(theControlParamsForbiddenToRemove),
46 myAllocator(new NCollection_IncAllocator(IMeshData::MEMORY_BLOCK_SIZE_HUGE)),
47 myControlParamsToRemove(new IMeshData::MapOfReal(1, myAllocator)),
48 myCurrParam(0.0),
49 myCurrControlParam(0.0),
50 myPrevControlParam(0.0)
51 {
52 }
53
54 //! Returns map of parameters supposed to be removed.
Handle(IMeshData::MapOfReal)55 const Handle(IMeshData::MapOfReal)& GetControlParametersToRemove(
56 const IMeshTools_Parameters& theParameters)
57 {
58 myParameters = theParameters;
59
60 Standard_Integer aStartIndex, aEndIndex;
61 if (myIsoU)
62 {
63 aStartIndex = 1;
64 aEndIndex = myParams->Length();
65 }
66 else
67 {
68 aStartIndex = 2;
69 aEndIndex = myParams->Length() - 1;
70 }
71
72 for (Standard_Integer i = aStartIndex; i <= aEndIndex; ++i)
73 {
74 myCurrParam = myParams->Value(i);
75 myIso = new GeomAdaptor_Curve(myIsoU ? mySurface->UIso(myCurrParam) : mySurface->VIso(myCurrParam));
76
77 myPrevControlParam = myControlParams->Value(1);
78 myIso->D1(myPrevControlParam, myPrevControlPnt, myPrevControlVec);
79 for (Standard_Integer j = 2; j <= myControlParams->Length();)
80 {
81 j += checkControlPointAndMoveOn(j);
82 }
83 }
84
85 return myControlParamsToRemove;
86 }
87
88 private:
89
90 //! Checks the given control point for deviation.
91 //! Returns number of steps to be used to move point iterator.
checkControlPointAndMoveOn(const Standard_Integer theIndex)92 Standard_Integer checkControlPointAndMoveOn(const Standard_Integer theIndex)
93 {
94 Standard_Integer aMoveSteps = 0;
95 myCurrControlParam = myControlParams->Value(theIndex);
96 myIso->D1(myCurrControlParam, myCurrControlPnt, myCurrControlVec);
97
98 const Standard_Real aMidParam = 0.5 * (myPrevControlParam + myCurrControlParam);
99 const gp_Pnt aMidPnt = myIso->Value(aMidParam);
100
101 const Standard_Real aSqDist = BRepMesh_GeomTool::SquareDeflectionOfSegment(
102 myPrevControlPnt, myCurrControlPnt, aMidPnt);
103
104 Standard_Real anAngle = 0.0;
105
106 if ((myPrevControlVec.SquareMagnitude() > Precision::SquareConfusion()) &&
107 (myCurrControlVec.SquareMagnitude() > Precision::SquareConfusion()))
108 {
109 anAngle = myPrevControlVec.Angle(myCurrControlVec);
110 }
111
112 const Standard_Real aSqMaxDeflection = myDFace->GetDeflection() *
113 myDFace->GetDeflection();
114
115 if (((aSqDist > aSqMaxDeflection) || (anAngle > myParameters.AngleInterior)) &&
116 aSqDist > myParameters.MinSize * myParameters.MinSize)
117 {
118 // insertion
119 myControlParams->InsertBefore(theIndex, aMidParam);
120 }
121 else
122 {
123 // Here we should leave at least 3 parameters as far as
124 // we must have at least one parameter related to surface
125 // internals in order to prevent movement of triangle body
126 // outside the surface in case of highly curved ones, e.g.
127 // BSpline springs.
128 if (((aSqDist < aSqMaxDeflection) || (anAngle < myParameters.AngleInterior)) &&
129 myControlParams->Length() > 3 && theIndex < myControlParams->Length())
130 {
131 // Remove too dense points
132 const Standard_Real aTmpParam = myControlParams->Value(theIndex + 1);
133 if (checkParameterForDeflectionAndUpdateCache(aTmpParam))
134 {
135 ++aMoveSteps;
136 }
137 }
138
139 myPrevControlParam = myCurrControlParam;
140 myPrevControlPnt = myCurrControlPnt;
141 myPrevControlVec = myCurrControlVec;
142
143 ++aMoveSteps;
144 }
145
146 return aMoveSteps;
147 }
148
149 //! Checks whether the given param suits specified deflection. Updates cache.
checkParameterForDeflectionAndUpdateCache(const Standard_Real theParam)150 Standard_Boolean checkParameterForDeflectionAndUpdateCache(const Standard_Real theParam)
151 {
152 gp_Pnt aTmpPnt;
153 gp_Vec aTmpVec;
154 myIso->D1(theParam, aTmpPnt, aTmpVec);
155
156 const Standard_Real aTmpMidParam = 0.5 * (myPrevControlParam + theParam);
157 const gp_Pnt aTmpMidPnt = myIso->Value(aTmpMidParam);
158
159 // Lets check next parameter.
160 // If it also fits deflection, we can remove previous parameter.
161 const Standard_Real aSqDist = BRepMesh_GeomTool::SquareDeflectionOfSegment(
162 myPrevControlPnt, aTmpPnt, aTmpMidPnt);
163
164 if (aSqDist < myDFace->GetDeflection() * myDFace->GetDeflection())
165 {
166 // Lets check parameters for angular deflection.
167 if (myPrevControlVec.SquareMagnitude() < gp::Resolution() ||
168 aTmpVec.SquareMagnitude() < gp::Resolution() ||
169 myPrevControlVec.Angle(aTmpVec) < myParameters.AngleInterior)
170 {
171 // For current Iso line we can remove this parameter.
172 myControlParamsToRemove->Add(myCurrControlParam);
173 myCurrControlParam = theParam;
174 myCurrControlPnt = aTmpPnt;
175 myCurrControlVec = aTmpVec;
176 return Standard_True;
177 }
178 else
179 {
180 // We have found a place on the surface refusing
181 // removement of this parameter.
182 myParamsForbiddenToRemove ->Add(myCurrParam);
183 myControlParamsForbiddenToRemove->Add(myCurrControlParam);
184 }
185 }
186
187 return Standard_False;
188 }
189
190 private:
191
192 IMeshData::IFaceHandle myDFace;
193 Handle(Geom_Surface) mySurface;
194 Standard_Boolean myIsoU;
195 Handle(IMeshData::SequenceOfReal) myParams;
196 Handle(IMeshData::SequenceOfReal) myControlParams;
197
198 Handle(IMeshData::MapOfReal) myParamsForbiddenToRemove;
199 Handle(IMeshData::MapOfReal) myControlParamsForbiddenToRemove;
200
201 Handle(NCollection_IncAllocator) myAllocator;
202 Handle(IMeshData::MapOfReal) myControlParamsToRemove;
203
204
205 IMeshTools_Parameters myParameters;
206 NCollection_Handle<GeomAdaptor_Curve> myIso;
207
208 Standard_Real myCurrParam;
209
210 Standard_Real myCurrControlParam;
211 gp_Pnt myCurrControlPnt;
212 gp_Vec myCurrControlVec;
213
214 Standard_Real myPrevControlParam;
215 gp_Pnt myPrevControlPnt;
216 gp_Vec myPrevControlVec;
217 };
218
219 //! Adds param to map if it fits specified range.
addParam(const Standard_Real & theParam,const std::pair<Standard_Real,Standard_Real> & theRange,IMeshData::IMapOfReal & theParams)220 Standard_Boolean addParam(
221 const Standard_Real& theParam,
222 const std::pair<Standard_Real, Standard_Real>& theRange,
223 IMeshData::IMapOfReal& theParams)
224 {
225 if (theParam < theRange.first ||
226 theParam > theRange.second)
227 {
228 return Standard_False;
229 }
230
231 theParams.Add(theParam);
232 return Standard_True;
233 }
234
235 //! Initializes parameters map using CN intervals.
initParamsFromIntervals(const TColStd_Array1OfReal & theIntervals,const std::pair<Standard_Real,Standard_Real> & theRange,const Standard_Boolean isSplitIntervals,IMeshData::IMapOfReal & theParams)236 Standard_Boolean initParamsFromIntervals(
237 const TColStd_Array1OfReal& theIntervals,
238 const std::pair<Standard_Real, Standard_Real>& theRange,
239 const Standard_Boolean isSplitIntervals,
240 IMeshData::IMapOfReal& theParams)
241 {
242 Standard_Boolean isAdded = Standard_False;
243
244 for (Standard_Integer i = theIntervals.Lower(); i <= theIntervals.Upper(); ++i)
245 {
246 const Standard_Real aStartParam = theIntervals.Value(i);
247 if (addParam(aStartParam, theRange, theParams))
248 {
249 isAdded = Standard_True;
250 }
251
252 if (isSplitIntervals && i < theIntervals.Upper())
253 {
254 const Standard_Real aMidParam = (aStartParam + theIntervals.Value(i + 1)) / 2.;
255 if (addParam(aMidParam, theRange, theParams))
256 {
257 isAdded = Standard_True;
258 }
259 }
260 }
261
262 return isAdded;
263 }
264
265 //! Checks whether intervals should be split.
266 //! Returns true in case if it is impossible to compute normal
267 //! directly on intervals, false is returned elsewhere.
toSplitIntervals(const Handle (Geom_Surface)& theSurf,const TColStd_Array1OfReal (& theIntervals)[2])268 Standard_Boolean toSplitIntervals (const Handle (Geom_Surface)& theSurf,
269 const TColStd_Array1OfReal (&theIntervals)[2])
270 {
271 Standard_Integer aIntervalU = theIntervals[0].Lower ();
272 for (; aIntervalU <= theIntervals[0].Upper (); ++aIntervalU)
273 {
274 const Standard_Real aParamU = theIntervals[0].Value(aIntervalU);
275 if (Precision::IsInfinite (aParamU))
276 continue;
277
278 Standard_Integer aIntervalV = theIntervals[1].Lower ();
279 for (; aIntervalV <= theIntervals[1].Upper (); ++aIntervalV)
280 {
281 gp_Dir aNorm;
282 const Standard_Real aParamV = theIntervals[1].Value(aIntervalV);
283 if (Precision::IsInfinite (aParamV))
284 continue;
285
286 if (GeomLib::NormEstim (theSurf, gp_Pnt2d (aParamU, aParamV), Precision::Confusion (), aNorm) != 0)
287 {
288 return Standard_True;
289 }
290 // TODO: do not split intervals if there is no normal in the middle of interval.
291 }
292 }
293
294 return Standard_False;
295 }
296 }
297
298 //=======================================================================
299 // Function: AdjustRange
300 // Purpose :
301 //=======================================================================
AdjustRange()302 void BRepMesh_NURBSRangeSplitter::AdjustRange()
303 {
304 BRepMesh_DefaultRangeSplitter::AdjustRange();
305 mySurfaceType = GetSurface()->GetType();
306
307 if (mySurfaceType == GeomAbs_BezierSurface)
308 {
309 const std::pair<Standard_Real, Standard_Real>& aRangeU = GetRangeU();
310 const std::pair<Standard_Real, Standard_Real>& aRangeV = GetRangeV();
311
312 myIsValid = !(aRangeU.first < -0.5 ||
313 aRangeU.second > 1.5 ||
314 aRangeV.first < -0.5 ||
315 aRangeV.second > 1.5);
316 }
317 }
318
319 //=======================================================================
320 // Function: GenerateSurfaceNodes
321 // Purpose :
322 //=======================================================================
Handle(IMeshData::ListOfPnt2d)323 Handle(IMeshData::ListOfPnt2d) BRepMesh_NURBSRangeSplitter::GenerateSurfaceNodes(
324 const IMeshTools_Parameters& theParameters) const
325 {
326 if (!initParameters())
327 {
328 return Handle(IMeshData::ListOfPnt2d)();
329 }
330
331 const std::pair<Standard_Real, Standard_Real>& aRangeU = GetRangeU();
332 const std::pair<Standard_Real, Standard_Real>& aRangeV = GetRangeV();
333 const std::pair<Standard_Real, Standard_Real>& aDelta = GetDelta ();
334
335 const Standard_Real aDefFace = GetDFace()->GetDeflection();
336 const Handle(BRepAdaptor_Surface)& gFace = GetSurface();
337 Handle(Geom_Surface) aSurface = gFace->Surface().Surface();
338
339 const Handle(NCollection_IncAllocator) aTmpAlloc =
340 new NCollection_IncAllocator(IMeshData::MEMORY_BLOCK_SIZE_HUGE);
341
342 const Handle(IMeshData::SequenceOfReal) aParams[2] = {
343 computeGrainAndFilterParameters(GetParametersU(), gFace->UResolution(aDefFace),
344 (aRangeU.second - aRangeU.first), aDelta.first, theParameters, aTmpAlloc),
345
346 computeGrainAndFilterParameters(GetParametersV(), gFace->VResolution(aDefFace),
347 (aRangeV.second - aRangeV.first), aDelta.second, theParameters, aTmpAlloc)
348 };
349
350 // check intermediate isolines
351 Handle(IMeshData::MapOfReal) aFixedParams[2] = {
352 new IMeshData::MapOfReal(1, aTmpAlloc),
353 new IMeshData::MapOfReal(1, aTmpAlloc)
354 };
355
356 const Handle(IMeshData::MapOfReal) aParamsToRemove[2] = {
357 AnalyticalFilter(GetDFace(), GeomAbs_IsoV, aParams[1], aParams[0],
358 aFixedParams[1], aFixedParams[0]).GetControlParametersToRemove(theParameters),
359
360 AnalyticalFilter(GetDFace(), GeomAbs_IsoU, aParams[0], aParams[1],
361 aFixedParams[0], aFixedParams[1]).GetControlParametersToRemove(theParameters),
362 };
363
364 aParamsToRemove[0]->Subtract(*aFixedParams[0]);
365 aParamsToRemove[1]->Subtract(*aFixedParams[1]);
366
367 // insert nodes of the regular grid
368 Handle(IMeshData::ListOfPnt2d) aNodes = new IMeshData::ListOfPnt2d(
369 new NCollection_IncAllocator(IMeshData::MEMORY_BLOCK_SIZE_HUGE));
370
371 // insert nodes of the regular grid
372 for (Standard_Integer i = 1; i <= aParams[0]->Length(); ++i)
373 {
374 const Standard_Real aParam1 = aParams[0]->Value(i);
375 if (aParamsToRemove[0]->Contains(aParam1))
376 {
377 continue;
378 }
379
380 for (Standard_Integer j = 1; j <= aParams[1]->Length(); ++j)
381 {
382 const Standard_Real aParam2 = aParams[1]->Value(j);
383 if (aParamsToRemove[1]->Contains(aParam2))
384 {
385 continue;
386 }
387
388 aNodes->Append(gp_Pnt2d(aParam1, aParam2));
389 }
390 }
391
392 return aNodes;
393 }
394
395 //=======================================================================
396 // Function: initParameters
397 // Purpose :
398 //=======================================================================
initParameters() const399 Standard_Boolean BRepMesh_NURBSRangeSplitter::initParameters() const
400 {
401 const Handle(BRepAdaptor_Surface)& aSurface = GetSurface();
402
403 const GeomAbs_Shape aContinuity = GeomAbs_CN;
404 const std::pair<Standard_Integer, Standard_Integer> aIntervalsNb(
405 aSurface->NbUIntervals(aContinuity),
406 aSurface->NbVIntervals(aContinuity)
407 );
408
409 TColStd_Array1OfReal aIntervals[2] = {
410 TColStd_Array1OfReal(1, aIntervalsNb.first + 1),
411 TColStd_Array1OfReal(1, aIntervalsNb.second + 1)
412 };
413
414 aSurface->UIntervals(aIntervals[0], aContinuity);
415 aSurface->VIntervals(aIntervals[1], aContinuity);
416
417 const Standard_Boolean isSplitIntervals = toSplitIntervals (aSurface->Surface().Surface(), aIntervals);
418
419 if (!initParamsFromIntervals(aIntervals[0], GetRangeU(), isSplitIntervals,
420 const_cast<IMeshData::IMapOfReal&>(GetParametersU())))
421 {
422 //if (!grabParamsOfEdges (Edge_Frontier, Param_U))
423 {
424 return Standard_False;
425 }
426 }
427
428 if (!initParamsFromIntervals(aIntervals[1], GetRangeV(), isSplitIntervals,
429 const_cast<IMeshData::IMapOfReal&>(GetParametersV())))
430 {
431 //if (!grabParamsOfEdges (Edge_Frontier, Param_V))
432 {
433 return Standard_False;
434 }
435 }
436
437 return grabParamsOfEdges(Edge_Internal, Param_U | Param_V);
438 }
439
440 //=======================================================================
441 //function : grabParamsOfInternalEdges
442 //purpose :
443 //=======================================================================
grabParamsOfEdges(const EdgeType theEdgeType,const Standard_Integer theParamDimensionFlag) const444 Standard_Boolean BRepMesh_NURBSRangeSplitter::grabParamsOfEdges (
445 const EdgeType theEdgeType,
446 const Standard_Integer theParamDimensionFlag) const
447 {
448 if ((theParamDimensionFlag & (Param_U | Param_V)) == 0)
449 {
450 return Standard_False;
451 }
452
453 const IMeshData::IFaceHandle& aDFace = GetDFace ();
454 for (Standard_Integer aWireIt = 0; aWireIt < aDFace->WiresNb (); ++aWireIt)
455 {
456 const IMeshData::IWireHandle& aDWire = aDFace->GetWire (aWireIt);
457 for (Standard_Integer aEdgeIt = 0; aEdgeIt < aDWire->EdgesNb (); ++aEdgeIt)
458 {
459 const IMeshData::IEdgePtr& aDEdge = aDWire->GetEdge (aEdgeIt);
460 for (Standard_Integer aPCurveIt = 0; aPCurveIt < aDEdge->PCurvesNb (); ++aPCurveIt)
461 {
462 const IMeshData::IPCurveHandle& aDPCurve = aDEdge->GetPCurve (aPCurveIt);
463 if (aDPCurve->GetFace () == aDFace)
464 {
465 if (theEdgeType == Edge_Internal && !aDPCurve->IsInternal ())
466 {
467 continue;
468 }
469
470 for (Standard_Integer aPointIt = 0; aPointIt < aDPCurve->ParametersNb (); ++aPointIt)
471 {
472 const gp_Pnt2d& aPnt2d = aDPCurve->GetPoint (aPointIt);
473 if (theParamDimensionFlag & Param_U)
474 {
475 const_cast<IMeshData::IMapOfReal&>(GetParametersU ()).Add (aPnt2d.X ());
476 }
477
478 if (theParamDimensionFlag & Param_V)
479 {
480 const_cast<IMeshData::IMapOfReal&>(GetParametersV ()).Add (aPnt2d.Y ());
481 }
482 }
483 }
484 }
485 }
486 }
487
488 return Standard_True;
489 }
490
491 //=======================================================================
492 //function : computeGrainAndFilterParameters
493 //purpose :
494 //=======================================================================
Handle(IMeshData::SequenceOfReal)495 Handle(IMeshData::SequenceOfReal) BRepMesh_NURBSRangeSplitter::computeGrainAndFilterParameters(
496 const IMeshData::IMapOfReal& theSourceParams,
497 const Standard_Real theTol2d,
498 const Standard_Real theRangeDiff,
499 const Standard_Real theDelta,
500 const IMeshTools_Parameters& theParameters,
501 const Handle(NCollection_IncAllocator)& theAllocator) const
502 {
503 // Sort and filter sequence of parameters
504 Standard_Real aMinDiff = Precision::PConfusion();
505 if (theDelta < 1.)
506 {
507 aMinDiff /= theDelta;
508 }
509
510 aMinDiff = Max(theParameters.MinSize, aMinDiff);
511
512 const Standard_Real aDiffMaxLim = 0.1 * theRangeDiff;
513 const Standard_Real aDiffMinLim = Max(0.005 * theRangeDiff,
514 2. * theTol2d);
515 const Standard_Real aDiff = Max(theParameters.MinSize,
516 Min(aDiffMaxLim, aDiffMinLim));
517 return filterParameters(theSourceParams, aMinDiff, aDiff, theAllocator);
518 }
519
520 //=======================================================================
521 //function : filterParameters
522 //purpose :
523 //=======================================================================
Handle(IMeshData::SequenceOfReal)524 Handle(IMeshData::SequenceOfReal) BRepMesh_NURBSRangeSplitter::filterParameters(
525 const IMeshData::IMapOfReal& theParams,
526 const Standard_Real theMinDist,
527 const Standard_Real theFilterDist,
528 const Handle(NCollection_IncAllocator)& theAllocator) const
529 {
530 Handle(IMeshData::SequenceOfReal) aResult = new IMeshData::SequenceOfReal(theAllocator);
531
532 // Sort sequence of parameters
533 const Standard_Integer anInitLen = theParams.Extent();
534
535 if (anInitLen < 1)
536 {
537 return aResult;
538 }
539
540 TColStd_Array1OfReal aParamArray(1, anInitLen);
541 Standard_Integer j;
542 for (j = 1; j <= anInitLen; j++)
543 aParamArray(j) = theParams(j);
544
545 std::sort(aParamArray.begin(), aParamArray.end());
546
547 // mandatory pre-filtering using the first (minimal) filter value
548 Standard_Integer aParamLength = 1;
549 for (j = 2; j <= anInitLen; j++)
550 {
551 if ((aParamArray(j) - aParamArray(aParamLength)) > theMinDist)
552 {
553 if (++aParamLength < j)
554 aParamArray(aParamLength) = aParamArray(j);
555 }
556 }
557
558 //perform filtering on series
559 Standard_Real aLastAdded, aLastCandidate;
560 Standard_Boolean isCandidateDefined = Standard_False;
561 aLastAdded = aParamArray(1);
562 aLastCandidate = aLastAdded;
563 aResult->Append(aLastAdded);
564
565 for (j = 2; j < aParamLength; j++)
566 {
567 Standard_Real aVal = aParamArray(j);
568 if (aVal - aLastAdded > theFilterDist)
569 {
570 //adds the parameter
571 if (isCandidateDefined)
572 {
573 aLastAdded = aLastCandidate;
574 isCandidateDefined = Standard_False;
575 j--;
576 }
577 else
578 {
579 aLastAdded = aVal;
580 }
581 aResult->Append(aLastAdded);
582 continue;
583 }
584
585 aLastCandidate = aVal;
586 isCandidateDefined = Standard_True;
587 }
588 aResult->Append(aParamArray(aParamLength));
589
590 return aResult;
591 }
592