1 // Copyright (C) 2007-2016 CEA/DEN, EDF R&D
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 // Author : Lioka RAZAFINDRAZAKA (CEA)
22 #include "SMESH_ControlPnt.hxx"
24 #include <BRepBndLib.hxx>
25 #include <BRepMesh_IncrementalMesh.hxx>
26 #include <BRep_Tool.hxx>
27 #include <Bnd_Box.hxx>
28 #include <GCPnts_UniformAbscissa.hxx>
29 #include <GeomAdaptor_Curve.hxx>
30 #include <Geom_Curve.hxx>
31 #include <IntCurvesFace_Intersector.hxx>
32 #include <Poly_Array1OfTriangle.hxx>
33 #include <Poly_Triangle.hxx>
34 #include <Poly_Triangulation.hxx>
35 #include <Precision.hxx>
36 #include <TColgp_Array1OfPnt.hxx>
37 #include <TopExp_Explorer.hxx>
38 #include <TopLoc_Location.hxx>
40 #include <TopoDS_Edge.hxx>
41 #include <TopoDS_Face.hxx>
42 #include <TopoDS_Iterator.hxx>
43 #include <TopoDS_Solid.hxx>
47 #include <gp_Trsf.hxx>
54 // Some functions for surface sampling
55 void subdivideTriangle( const gp_Pnt& p1,
58 const double& theSize,
59 std::vector<ControlPnt>& thePoints );
61 std::vector<gp_Pnt> computePointsForSplitting( const gp_Pnt& p1,
64 gp_Pnt tangencyPoint(const gp_Pnt& p1,
66 const gp_Pnt& Center);
70 //================================================================================
72 * \brief Fills a vector of points from which a size map input file can be written
74 //================================================================================
76 void SMESHUtils::createControlPoints( const TopoDS_Shape& theShape,
77 const double& theSize,
78 std::vector<ControlPnt>& thePoints )
80 if ( theShape.ShapeType() == TopAbs_VERTEX )
82 gp_Pnt aPnt = BRep_Tool::Pnt( TopoDS::Vertex(theShape) );
83 ControlPnt aControlPnt( aPnt, theSize );
84 thePoints.push_back( aControlPnt );
86 if ( theShape.ShapeType() == TopAbs_EDGE )
88 createPointsSampleFromEdge( TopoDS::Edge( theShape ), theSize, thePoints );
90 else if ( theShape.ShapeType() == TopAbs_WIRE )
93 for (Ex.Init(theShape,TopAbs_EDGE); Ex.More(); Ex.Next())
95 createPointsSampleFromEdge( TopoDS::Edge( Ex.Current() ), theSize, thePoints );
98 else if ( theShape.ShapeType() == TopAbs_FACE )
100 createPointsSampleFromFace( TopoDS::Face( theShape ), theSize, thePoints );
102 else if ( theShape.ShapeType() == TopAbs_SOLID )
104 createPointsSampleFromSolid( TopoDS::Solid( theShape ), theSize, thePoints );
106 else if ( theShape.ShapeType() == TopAbs_COMPOUND )
108 TopoDS_Iterator it( theShape );
109 for(; it.More(); it.Next())
111 createControlPoints( it.Value(), theSize, thePoints );
116 //================================================================================
118 * \brief Fills a vector of points with point samples approximately
119 * \brief spaced with a given size
121 //================================================================================
123 void SMESHUtils::createPointsSampleFromEdge( const TopoDS_Edge& theEdge,
124 const double& theSize,
125 std::vector<ControlPnt>& thePoints )
127 double step = theSize;
129 Handle( Geom_Curve ) aCurve = BRep_Tool::Curve( theEdge, first, last );
130 GeomAdaptor_Curve C ( aCurve );
131 GCPnts_UniformAbscissa DiscretisationAlgo(C, step , first, last, Precision::Confusion());
132 int nbPoints = DiscretisationAlgo.NbPoints();
135 aPnt.SetSize(theSize);
137 for ( int i = 1; i <= nbPoints; i++ )
139 double param = DiscretisationAlgo.Parameter( i );
140 aCurve->D0( param, aPnt );
141 thePoints.push_back( aPnt );
145 //================================================================================
147 * \brief Fills a vector of points with point samples approximately
148 * \brief spaced with a given size
150 //================================================================================
152 void SMESHUtils::createPointsSampleFromFace( const TopoDS_Face& theFace,
153 const double& theSize,
154 std::vector<ControlPnt>& thePoints )
156 BRepMesh_IncrementalMesh M(theFace, 0.01, Standard_True);
157 TopLoc_Location aLocation;
159 // Triangulate the face
160 Handle(Poly_Triangulation) aTri = BRep_Tool::Triangulation (theFace, aLocation);
162 // Get the transformation associated to the face location
163 gp_Trsf aTrsf = aLocation.Transformation();
166 int nbTriangles = aTri->NbTriangles();
167 Poly_Array1OfTriangle triangles(1,nbTriangles);
168 triangles=aTri->Triangles();
171 int nbNodes = aTri->NbNodes();
172 TColgp_Array1OfPnt nodes(1,nbNodes);
173 nodes = aTri->Nodes();
175 // Iterate on triangles and subdivide them
176 for(int i=1; i<=nbTriangles; i++)
178 Poly_Triangle aTriangle = triangles.Value(i);
179 gp_Pnt p1 = nodes.Value(aTriangle.Value(1));
180 gp_Pnt p2 = nodes.Value(aTriangle.Value(2));
181 gp_Pnt p3 = nodes.Value(aTriangle.Value(3));
187 subdivideTriangle(p1, p2, p3, theSize, thePoints);
191 //================================================================================
193 * \brief Fills a vector of points with point samples approximately
194 * \brief spaced with a given size
196 //================================================================================
198 void SMESHUtils::createPointsSampleFromSolid( const TopoDS_Solid& theSolid,
199 const double& theSize,
200 std::vector<ControlPnt>& thePoints )
202 // Compute the bounding box
203 double Xmin, Ymin, Zmin, Xmax, Ymax, Zmax;
205 BRepBndLib::Add(theSolid, B);
206 B.Get(Xmin, Ymin, Zmin, Xmax, Ymax, Zmax);
209 double step = theSize;
211 for ( double x=Xmin; x-Xmax<Precision::Confusion(); x=x+step )
213 for ( double y=Ymin; y-Ymax<Precision::Confusion(); y=y+step )
215 // Step1 : generate the Zmin -> Zmax line
216 gp_Pnt startPnt(x, y, Zmin);
217 gp_Pnt endPnt(x, y, Zmax);
218 gp_Vec aVec(startPnt, endPnt);
219 gp_Lin aLine(startPnt, aVec);
220 double endParam = Zmax - Zmin;
222 // Step2 : for each face of theSolid:
223 std::set<double> intersections;
224 std::set<double>::iterator it = intersections.begin();
227 for (Ex.Init(theSolid,TopAbs_FACE); Ex.More(); Ex.Next())
229 // check if there is an intersection
230 IntCurvesFace_Intersector anIntersector(TopoDS::Face(Ex.Current()), Precision::Confusion());
231 anIntersector.Perform(aLine, 0, endParam);
233 // get the intersection's parameter and store it
234 int nbPoints = anIntersector.NbPnt();
235 for(int i = 0 ; i < nbPoints ; i++ )
237 it = intersections.insert( it, anIntersector.WParameter(i+1) );
240 // Step3 : go through the line chunk by chunk
241 if ( intersections.begin() != intersections.end() )
243 std::set<double>::iterator intersectionsIterator=intersections.begin();
244 double first = *intersectionsIterator;
245 intersectionsIterator++;
246 bool innerPoints = true;
247 for ( ; intersectionsIterator!=intersections.end() ; intersectionsIterator++ )
249 double second = *intersectionsIterator;
252 // If the last chunk was outside of the shape or this is the first chunk
253 // add the points in the range [first, second] to the points vector
254 double localStep = (second -first) / ceil( (second - first) / step );
255 for ( double z = Zmin + first; z < Zmin + second; z = z + localStep )
257 thePoints.push_back(ControlPnt( x, y, z, theSize ));
259 thePoints.push_back(ControlPnt( x, y, Zmin + second, theSize ));
262 innerPoints = !innerPoints;
269 //================================================================================
271 * \brief Subdivides a triangle until it reaches a certain size (recursive function)
273 //================================================================================
275 void SMESHUtils::subdivideTriangle( const gp_Pnt& p1,
278 const double& theSize,
279 std::vector<ControlPnt>& thePoints)
281 // Size threshold to stop subdividing
282 // This value ensures that two control points are distant no more than 2*theSize
285 // The greater distance D of the mass center M to each Edge is 1/3 * Median
286 // and Median < sqrt(3/4) * a where a is the greater side (by using Apollonius' thorem).
287 // So D < 1/3 * sqrt(3/4) * a and if a < sqrt(3) * S then D < S/2
288 // and the distance between two mass centers of two neighbouring triangles
289 // sharing an edge is < 2 * 1/2 * S = S
290 // If the traingles share a Vertex and no Edge the distance of the mass centers
291 // to the Vertices is 2*D < S so the mass centers are distant of less than 2*S
293 double threshold = sqrt( 3. ) * theSize;
295 if ( (p1.Distance(p2) > threshold ||
296 p2.Distance(p3) > threshold ||
297 p3.Distance(p1) > threshold))
299 std::vector<gp_Pnt> midPoints = computePointsForSplitting(p1, p2, p3);
301 subdivideTriangle( midPoints[0], midPoints[1], midPoints[2], theSize, thePoints );
302 subdivideTriangle( midPoints[0], p2, midPoints[1], theSize, thePoints );
303 subdivideTriangle( midPoints[2], midPoints[1], p3, theSize, thePoints );
304 subdivideTriangle( p1, midPoints[0], midPoints[2], theSize, thePoints );
308 double x = (p1.X() + p2.X() + p3.X()) / 3 ;
309 double y = (p1.Y() + p2.Y() + p3.Y()) / 3 ;
310 double z = (p1.Z() + p2.Z() + p3.Z()) / 3 ;
312 ControlPnt massCenter( x ,y ,z, theSize );
313 thePoints.push_back( massCenter );
317 //================================================================================
319 * \brief Returns the appropriate points for splitting a triangle
320 * \brief the tangency points of the incircle are used in order to have mostly
321 * \brief well-shaped sub-triangles
323 //================================================================================
325 std::vector<gp_Pnt> SMESHUtils::computePointsForSplitting( const gp_Pnt& p1,
329 std::vector<gp_Pnt> midPoints;
331 gp_Trsf Trsf_1; // Identity transformation
332 gp_Ax3 reference_system(gp::Origin(), gp::DZ(), gp::DX()); // OXY
338 gp_Dir Dz = Dx.Crossed(Daux);
339 gp_Ax3 current_system(p1, Dz, Dx);
341 Trsf_1.SetTransformation( reference_system, current_system );
343 gp_Pnt A = p1.Transformed(Trsf_1);
344 gp_Pnt B = p2.Transformed(Trsf_1);
345 gp_Pnt C = p3.Transformed(Trsf_1);
347 double a = B.Distance(C) ;
348 double b = A.Distance(C) ;
349 double c = B.Distance(A) ;
351 // Incenter coordinates
352 // see http://mathworld.wolfram.com/Incenter.html
353 double Xi = ( b*B.X() + c*C.X() ) / ( a + b + c );
354 double Yi = ( b*B.Y() ) / ( a + b + c );
355 gp_Pnt Center(Xi, Yi, 0);
357 // Calculate the tangency points of the incircle
358 gp_Pnt T1 = tangencyPoint( A, B, Center);
359 gp_Pnt T2 = tangencyPoint( B, C, Center);
360 gp_Pnt T3 = tangencyPoint( C, A, Center);
362 gp_Pnt p1_2 = T1.Transformed(Trsf_1.Inverted());
363 gp_Pnt p2_3 = T2.Transformed(Trsf_1.Inverted());
364 gp_Pnt p3_1 = T3.Transformed(Trsf_1.Inverted());
366 midPoints.push_back(p1_2);
367 midPoints.push_back(p2_3);
368 midPoints.push_back(p3_1);
373 //================================================================================
375 * \brief Computes the tangency points of the circle of center Center with
376 * \brief the straight line (p1 p2)
378 //================================================================================
380 gp_Pnt SMESHUtils::tangencyPoint(const gp_Pnt& p1,
382 const gp_Pnt& Center)
387 // The tangency point is the intersection of the straight line (p1 p2)
388 // and the straight line (Center T) which is orthogonal to (p1 p2)
389 if ( fabs(p1.X() - p2.X()) <= Precision::Confusion() )
391 Xt=p1.X(); // T is on (p1 p2)
392 Yt=Center.Y(); // (Center T) is orthogonal to (p1 p2)
394 else if ( fabs(p1.Y() - p2.Y()) <= Precision::Confusion() )
396 Yt=p1.Y(); // T is on (p1 p2)
397 Xt=Center.X(); // (Center T) is orthogonal to (p1 p2)
401 // First straight line coefficients (equation y=a*x+b)
402 double a = (p2.Y() - p1.Y()) / (p2.X() - p1.X()) ;
403 double b = p1.Y() - a*p1.X(); // p1 is on this straight line
405 // Second straight line coefficients (equation y=c*x+d)
406 double c = -1 / a; // The 2 lines are orthogonal
407 double d = Center.Y() - c*Center.X(); // Center is on this straight line
409 Xt = (d - b) / (a - c);
413 return gp_Pnt( Xt, Yt, 0 );