Renaming overview folder
[occt.git] / dox / overview / tutorial / tutorial.md
1  Tutorial {#overview__tutorial}
2 =======
3
4 @section sec1 Overview 
5
6
7 This tutorial will teach you how to use Open CASCADE Technology services to model a 3D object. The purpose of this tutorial is not to describe all Open CASCADE Technology classes but to help you start thinking in terms of Open CASCADE Technology as a tool. 
8
9
10 @subsection OCCT_TUTORIAL_SUB1_1 Prerequisites 
11
12 This tutorial assumes that you have experience in using and setting up C++.
13 From a programming standpoint, Open CASCADE Technology is designed to enhance your C++ tools with 3D modeling classes, methods and functions. The combination of all these resources will allow you to create substantial applications.
14
15 @subsection OCCT_TUTORIAL_SUB1_2 The Model
16
17 To illustrate the use of classes provided in the 3D geometric modeling toolkits, you will create a bottle as shown:
18
19 @image html /overview/tutorial/images/tutorial_image001.png
20 @image latex /overview/tutorial/images/tutorial_image001.png
21
22 In the tutorial we will create, step-by-step, a function that will model a bottle as shown above. You will find the complete source code of this tutorial, including the very function *MakeBottle* in the distribution of Open CASCADE Technology. The function body is provided in the file samples/qt/Tutorial/src/MakeBottle.cxx.
23
24 @subsection OCCT_TUTORIAL_SUB1_3 Model Specifications
25
26 We first define the bottle specifications as follows:
27
28 | Object Parameter | Parameter Name | Parameter Value |
29 | :--------------: | :------------: | :-------------: |
30 | Bottle height    | MyHeight       |         70mm    |
31 | Bottle width     | MyWidth        |         50mm    |
32 | Bottle thickness | MyThickness    |         30mm    |
33
34 In addition, we decide that the bottle's profile (base) will be centered on the origin of the global Cartesian coordinate system.
35
36 @image html /overview/tutorial/images/tutorial_image002.png
37 @image latex /overview/tutorial/images/tutorial_image002.png
38
39 This modeling requires four steps:
40
41   * build the bottle's Profile
42   * build the bottle's Body
43   * build the Threading on the bottle's neck
44   * build the result compound
45
46   
47 @section sec2 Building the Profile 
48
49 @subsection OCCT_TUTORIAL_SUB2_1 Defining Support Points
50
51 To create the bottle's profile, you first create characteristic points with their coordinates as shown below in the (XOY) plane. These points will be the supports that define the geometry of the profile.
52
53 @image html /overview/tutorial/images/tutorial_image003.png
54 @image latex /overview/tutorial/images/tutorial_image003.png
55
56 There are two classes to describe a 3D Cartesian point from its X, Y and Z coordinates in Open CASCADE Technology:
57
58   * the primitive geometric *gp_Pnt* class
59   * the transient *Geom_CartesianPoint* class manipulated by handle
60     
61 A handle is a type of smart pointer that provides automatic memory management.
62 To choose the best class for this application, consider the following:
63
64   * *gp_Pnt* is manipulated by value. Like all objects of its kind, it will have a limited lifetime.
65   * *Geom_CartesianPoint* is manipulated by handle and may have multiple references and a long lifetime.
66     
67 Since all the points you will define are only used to create the profile's curves, an object with a limited lifetime will do. Choose the *gp_Pnt* class.
68 To instantiate a *gp_Pnt* object, just specify the X, Y, and Z coordinates of the points in the global cartesian coordinate system:
69
70 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
71     gp_Pnt aPnt1(-myWidth / 2., 0, 0);
72     gp_Pnt aPnt2(-myWidth / 2., -myThickness / 4., 0);
73     gp_Pnt aPnt3(0, -myThickness / 2., 0);
74     gp_Pnt aPnt4(myWidth / 2., -myThickness / 4., 0);
75     gp_Pnt aPnt5(myWidth / 2., 0, 0);
76 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
77
78 Once your objects are instantiated, you can use methods provided by the class to access and modify its data. For example, to get the X coordinate of a point:
79
80 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
81 Standard_Real xValue1 = aPnt1.X();
82 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
83
84 @subsection OCCT_TUTORIAL_SUB2_2 Profile: Defining the Geometry
85 With the help of the previously defined points, you can compute a part of the bottle's profile geometry. As shown in the figure below, it will consist of two segments and one arc.
86
87 @image html /overview/tutorial/images/tutorial_image004.png
88 @image latex /overview/tutorial/images/tutorial_image004.png
89
90 To create such entities, you need a specific data structure, which implements 3D geometric objects. This can be found in the Geom package of Open CASCADE Technology.
91 In Open CASCADE Technology a package is a group of classes providing related functionality. The classes have names that start with the name of a package they belong to. For example, *Geom_Line* and *Geom_Circle* classes belong to the *Geom* package. The *Geom* package implements 3D geometric objects: elementary curves and surfaces are provided as well as more complex ones (such as *Bezier* and *BSpline*).
92 However, the *Geom* package provides only the data structure of geometric entities. You can directly instantiate classes belonging to *Geom*, but it is easier to compute elementary curves and surfaces by using the *GC* package. 
93 This is because the *GC* provides two algorithm classes which are exactly what is required for our profile:
94
95   * Class *GC_MakeSegment* to create a segment. One of its constructors allows you to define a segment by two end points P1 and P2
96   * Class *GC_MakeArcOfCircle* to create an arc of a circle. A useful constructor creates an arc from two end points P1 and P3 and going through P2.
97
98 Both of these classes return a *Geom_TrimmedCurve* manipulated by handle. This entity represents a base curve (line or circle, in our case), limited between two of its parameter values. For example, circle C is parameterized between 0 and 2PI. If you need to create a quarter of a circle, you create a *Geom_TrimmedCurve* on C limited between 0 and M_PI/2.
99
100 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
101     Handle(Geom_TrimmedCurve) aArcOfCircle = GC_MakeArcOfCircle(aPnt2,aPnt3,aPnt4);
102     Handle(Geom_TrimmedCurve) aSegment1    = GC_MakeSegment(aPnt1, aPnt2);
103     Handle(Geom_TrimmedCurve) aSegment2    = GC_MakeSegment(aPnt4, aPnt5);
104 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
105
106 All *GC* classes provide a casting method to obtain a result automatically with a function-like call. Note that this method will raise an exception if construction has failed. To handle possible errors more explicitly, you may use the *IsDone* and *Value* methods. For example:
107
108 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
109     GC_MakeSegment mkSeg (aPnt1, aPnt2);
110     Handle(Geom_TrimmedCurve) aSegment1;
111     if(mkSegment.IsDone()){
112         aSegment1 = mkSeg.Value();
113     }
114     else {
115     // handle error
116     }
117 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
118
119
120 @subsection OCCT_TUTORIAL_SUB2_3 Profile: Defining the Topology
121
122
123 You have created the support geometry of one part of the profile but these curves are independent with no relations between each other.
124 To simplify the modeling, it would be right to manipulate these three curves as a single entity.
125 This can be done by using the topological data structure of Open CASCADE Technology defined in the *TopoDS* package: it defines relationships between geometric entities which can be linked together to represent complex shapes.
126 Each object of the *TopoDS* package, inheriting from the *TopoDS_Shape* class, describes a topological shape as described below:
127
128 | Shape     | Open CASCADE Technology Class |                      Description                              |
129 | :-------- | :---------------------------- | :------------------------------------------------------------ |
130 | Vertex    | TopoDS_Vertex                 | Zero dimensional shape corresponding to a point in geometry.  |
131 | Edge      | TopoDS_Edge                   | One-dimensional shape corresponding to a curve and bounded by a vertex at each extremity.|
132 | Wire      | TopoDS_Wire                   | Sequence of edges connected by vertices.                      |
133 | Face      | TopoDS_Face                   | Part of a surface bounded by a closed wire(s).                |
134 | Shell     | TopoDS_Shell                  | Set of faces connected by edges.                              |
135 | Solid     | TopoDS_Solid                  | Part of 3D space bounded by Shells.                           |
136 | CompSolid | TopoDS_CompSolid              | Set of solids connected by their faces.                       |
137 | Compound  | TopoDS_Compound               | Set of any other shapes described above.                      |
138
139 Referring to the previous table, to build the profile, you will create:
140
141   * Three edges out of the previously computed curves.
142   * One wire with these edges.
143
144 @image html /overview/tutorial/images/tutorial_image005.png
145 @image latex /overview/tutorial/images/tutorial_image005.png
146     
147 However, the *TopoDS* package provides only the data structure of the topological entities. Algorithm classes available to compute standard topological objects can be found in the *BRepBuilderAPI* package.
148 To create an edge, you use the BRepBuilderAPI_MakeEdge class with the previously computed curves:
149
150 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
151     TopoDS_Edge aEdge1 = BRepBuilderAPI_MakeEdge(aSegment1);
152     TopoDS_Edge aEdge2 = BRepBuilderAPI_MakeEdge(aArcOfCircle);
153     TopoDS_Edge aEdge3 = BRepBuilderAPI_MakeEdge(aSegment2);
154 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
155
156 In Open CASCADE Technology, you can create edges in several ways. One possibility is to create an edge directly from two points, in which case the underlying geometry of this edge is a line, bounded by two vertices being automatically computed from the two input points. For example, aEdge1 and aEdge3 could have been computed in a simpler way:
157
158 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
159     TopoDS_Edge aEdge1 = BRepBuilderAPI_MakeEdge(aPnt1, aPnt3);
160     TopoDS_Edge aEdge2 = BRepBuilderAPI_MakeEdge(aPnt4, aPnt5);
161 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
162
163 To connect the edges, you need to create a wire with the *BRepBuilderAPI_MakeWire* class. There are two ways of building a wire with this class:
164
165   * directly from one to four edges
166   * by adding other wire(s) or edge(s) to an existing wire (this is explained later in this tutorial)
167
168 When building a wire from less than four edges, as in the present case, you can use the constructor directly as follows:
169
170 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
171     TopoDS_Wire aWire = BRepBuilderAPI_MakeWire(aEdge1, aEdge2, aEdge3);
172 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
173
174
175 @subsection OCCT_TUTORIAL_SUB2_4 Profile: Completing the Profile
176
177
178 Once the first part of your wire is created you need to compute the complete profile. A simple way to do this is to:
179
180   * compute a new wire by reflecting the existing one.
181   * add the reflected wire to the initial one.
182
183 @image html /overview/tutorial/images/tutorial_image006.png
184 @image latex /overview/tutorial/images/tutorial_image006.png
185
186 To apply a transformation on shapes (including wires), you first need to define the properties of a 3D geometric transformation by using the gp_Trsf class. This transformation can be a translation, a rotation, a scale, a reflection, or a combination of these.
187 In our case, we need to define a reflection with respect to the X axis of the global coordinate system. An axis, defined with the gp_Ax1 class, is built out of a point and has a direction (3D unitary vector). There are two ways to define this axis.
188 The first way is to define it from scratch, using its geometric definition:
189
190   * X axis is located at (0, 0, 0) - use the *gp_Pnt* class.
191   * X axis direction is (1, 0, 0) - use the *gp_Dir* class. A *gp_Dir* instance is created out of its X, Y and Z coordinates.
192
193 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
194     gp_Pnt aOrigin(0, 0, 0);
195     gp_Dir xDir(1, 0, 0);
196     gp_Ax1 xAxis(aOrigin, xDir);
197 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
198
199 The second and simplest way is to use the geometric constants defined in the gp package (origin, main directions and axis of the global coordinate system). To get the X axis, just call the *gp::OX* method:
200
201 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
202     gp_Ax1 xAxis = gp::OX();
203 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
204
205 As previously explained, the 3D geometric transformation is defined with the *gp_Trsf* class. There are two different ways to use this class:
206
207   * by defining a transformation matrix by all its values
208   * by using the appropriate methods corresponding to the required transformation (SetTranslation for a translation, SetMirror for a reflection, etc.): the matrix is automatically computed.
209     
210 Since the simplest approach is always the best one, you should use the SetMirror method with the axis as the center of symmetry.
211
212 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
213     gp_Trsf aTrsf;
214     aTrsf.SetMirror(xAxis);
215 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
216
217 You now have all necessary data to apply the transformation with the BRepBuilderAPI_Transform class by specifying:
218
219   * the shape on which the transformation must be applied.
220   * the geometric transformation
221
222 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
223     BRepBuilderAPI_Transform aBRepTrsf(aWire, aTrsf);
224 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
225
226 *BRepBuilderAPI_Transform* does not modify the nature of the shape: the result of the reflected wire remains a wire. But the function-like call or the *BRepBuilderAPI_Transform::Shape* method returns a *TopoDS_Shape* object:
227
228 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
229     TopoDS_Shape aMirroredShape = aBRepTrsf.Shape();
230 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
231
232 What you need is a method to consider the resulting reflected shape as a wire. The *TopoDS* global functions provide this kind of service by casting a shape into its real type. To cast the transformed wire, use the *TopoDS::Wire* method.
233
234 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
235     TopoDS_Wire aMirroredWire = TopoDS::Wire(aMirroredShape);
236 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
237
238 The bottle's profile is almost finished. You have created two wires: *aWire* and *aMirroredWire*. You need to concatenate them to compute a single shape. To do this, you use the *BRepBuilderAPI_MakeWire* class as follows:
239
240   * create an instance of *BRepBuilderAPI_MakeWire*.
241   * add all edges of the two wires by using the *Add* method on this object.
242
243 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
244     BRepBuilderAPI_MakeWire mkWire;
245     mkWire.Add(aWire);
246     mkWire.Add(aMirroredWire);
247     TopoDS_Wire myWireProfile = mkWire.Wire();
248 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
249
250
251 @section sec3 Building the Body
252
253
254 @subsection OCCT_TUTORIAL_SUB3_1 Prism the Profile
255
256
257 To compute the main body of the bottle, you need to create a solid shape. The simplest way is to use the previously created profile and to sweep it along a direction. The *Prism* functionality of Open CASCADE Technology is the most appropriate for that task. It accepts a shape and a direction as input and generates a new shape according to the following rules:
258
259 | Shape  | Generates          |
260 | :----- | :----------------- |
261 | Vertex | Edge               |
262 | Edge   | Face               |
263 | Wire   | Shell              |
264 | Face   | Solid              |
265 | Shell  | Compound of Solids |
266
267 @image html /overview/tutorial/images/tutorial_image007.png
268 @image latex /overview/tutorial/images/tutorial_image007.png
269
270 Your current profile is a wire. Referring to the Shape/Generates table, you need to compute a face out of its wire to generate a solid.
271 To create a face, use the *BRepBuilderAPI_MakeFace* class. As previously explained, a face is a part of a surface bounded by a closed wire. Generally, *BRepBuilderAPI_MakeFace* computes a face out of a surface and one or more wires.
272 When the wire lies on a plane, the surface is automatically computed.
273
274 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
275     TopoDS_Face myFaceProfile = BRepBuilderAPI_MakeFace(myWireProfile);
276 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
277
278 The *BRepPrimAPI* package provides all the classes to create topological primitive constructions: boxes, cones, cylinders, spheres, etc. Among them is the *BRepPrimAPI_MakePrism* class. As specified above, the prism is defined by:
279
280   * the basis shape to sweep;
281   * a vector for a finite prism or a direction for finite and infinite prisms.
282
283 You want the solid to be finite, swept along the Z axis and to be myHeight height. The vector, defined with the *gp_Vec* class on its X, Y and Z coordinates, is:
284
285 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
286     gp_Vec aPrismVec(0, 0, myHeight);
287 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
288
289 All the necessary data to create the main body of your bottle is now available. Just apply the *BRepPrimAPI_MakePrism* class to compute the solid:
290
291 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
292     TopoDS_Shape myBody = BRepPrimAPI_MakePrism(myFaceProfile, aPrismVec);
293 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
294
295
296 @subsection OCCT_TUTORIAL_SUB3_2 Applying Fillets
297
298
299 The edges of the bottle's body are very sharp. To replace them by rounded faces, you use the *Fillet* functionality of Open CASCADE Technology.
300 For our purposes, we will specify that fillets must be:
301
302   * applied on all edges of the shape
303   * have a radius of *myThickness* / 12
304
305 @image html /overview/tutorial/images/tutorial_image008.png
306 @image latex /overview/tutorial/images/tutorial_image008.png
307
308 To apply fillets on the edges of a shape, you use the *BRepFilletAPI_MakeFillet* class. This class is normally used as follows:
309
310   * Specify the shape to be filleted in the *BRepFilletAPI_MakeFillet* constructor.
311   * Add the fillet descriptions (an edge and a radius) using the *Add* method (you can add as many edges as you need).
312   * Ask for the resulting filleted shape with the *Shape* method.
313
314 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
315 BRepFilletAPI_MakeFillet mkFillet(myBody);
316 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
317
318 To add the fillet description, you need to know the edges belonging to your shape. The best solution is to explore your solid to retrieve its edges. This kind of functionality is provided with the *TopExp_Explorer* class, which explores the data structure described in a *TopoDS_Shape* and extracts the sub-shapes you specifically need. 
319 Generally, this explorer is created by providing the following information:
320
321   * the shape to explore
322   * the type of sub-shapes to be found. This information is given with the *TopAbs_ShapeEnum* enumeration.
323
324 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
325 TopExp_Explorer anEdgeExplorer(myBody, TopAbs_EDGE);
326 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
327
328 An explorer is usually applied in a loop by using its three main methods:
329
330   * *More()* to know if there are more sub-shapes to explore.
331   * *Current()* to know which is the currently explored sub-shape (used only if the *More()* method returns true).
332   * *Next()* to move onto the next sub-shape to explore.
333
334   
335 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
336     while(anEdgeExplorer.More()){
337         TopoDS_Edge anEdge = TopoDS::Edge(anEdgeExplorer.Current());
338         //Add edge to fillet algorithm
339         ...
340         anEdgeExplorer.Next();
341     }
342 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
343
344 In the explorer loop, you have found all the edges of the bottle shape. Each one must then be added in the *BRepFilletAPI_MakeFillet* instance with the *Add()* method. Do not forget to specify the radius of the fillet along with it.
345
346 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
347     mkFillet.Add(myThickness / 12., anEdge);
348 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
349
350 Once this is done, you perform the last step of the procedure by asking for the filleted shape.
351
352 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
353     myBody = mkFillet.Shape();
354 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
355
356
357 @subsection OCCT_TUTORIAL_SUB3_3 Adding the Neck
358
359
360 To add a neck to the bottle, you will create a cylinder and fuse it to the body. The cylinder is to be positioned on the top face of the body with a radius of *myThickness* / 4. and a height of *myHeight* / 10.
361
362 @image html /overview/tutorial/images/tutorial_image009.png
363 @image latex /overview/tutorial/images/tutorial_image009.png
364
365 To position the cylinder, you need to define a coordinate system with the *gp_Ax2* class defining a right-handed coordinate system from a point and two directions - the main (Z) axis direction and the X direction (the Y direction is computed from these two).
366 To align the neck with the center of the top face, being in the global coordinate system (0, 0, *myHeight*), with its normal on the global Z axis, your local coordinate system can be defined as follows:
367
368 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
369     gp_Pnt neckLocation(0, 0, myHeight);
370     gp_Dir neckAxis = gp::DZ();
371     gp_Ax2 neckAx2(neckLocation, neckAxis);
372 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
373
374 To create a cylinder, use another class from the primitives construction package: the *BRepPrimAPI_MakeCylinder* class. The information you must provide is:
375
376   * the coordinate system where the cylinder will be located;
377   * the radius and height.
378
379 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
380     Standard_Real myNeckRadius = myThickness / 4.;
381     Standard_Real myNeckHeight = myHeight / 10;
382     BRepPrimAPI_MakeCylinder MKCylinder(neckAx2, myNeckRadius, myNeckHeight);
383     TopoDS_Shape myNeck = MKCylinder.Shape();
384 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
385
386 You now have two separate parts: a main body and a neck that you need to fuse together.
387 The *BRepAlgoAPI* package provides services to perform Boolean operations between shapes, and especially: *common* (Boolean intersection), *cut* (Boolean subtraction) and *fuse* (Boolean union).
388 Use *BRepAlgoAPI_Fuse* to fuse the two shapes:
389
390 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
391     myBody = BRepAlgoAPI_Fuse(myBody, myNeck);
392 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
393
394
395 @subsection OCCT_TUTORIAL_SUB3_4 Creating a Hollowed Solid
396
397
398 Since a real bottle is used to contain liquid material, you should now create a hollowed solid from the bottle's top face.
399 In Open CASCADE Technology, a hollowed solid is called a *Thick* *Solid* and is internally computed as follows:
400
401   * Remove one or more faces from the initial solid to obtain the first wall W1 of the hollowed solid.
402   * Create a parallel wall W2 from W1 at a distance D. If D is positive, W2 will be outside the initial solid, otherwise it will be inside.
403   * Compute a solid from the two walls W1 and W2.
404
405 @image html /overview/tutorial/images/tutorial_image010.png
406 @image latex /overview/tutorial/images/tutorial_image010.png
407     
408 To compute a thick solid, you create an instance of the *BRepOffsetAPI_MakeThickSolid* class by giving the following information:
409     
410   * The shape, which must be hollowed.
411   * The tolerance used for the computation (tolerance criterion for coincidence in generated shapes).
412   * The thickness between the two walls W1 and W2 (distance D).
413   * The face(s) to be removed from the original solid to compute the first wall W1.
414     
415 The challenging part in this procedure is to find the face to remove from your shape - the top face of the neck, which:
416     
417   * has a plane (planar surface) as underlying geometry;
418   * is the highest face (in Z coordinates) of the bottle.
419     
420 To find the face with such characteristics, you will once again use an explorer to iterate on all the bottle's faces to find the appropriate one.
421
422 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
423     for(TopExp_Explorer aFaceExplorer(myBody, TopAbs_FACE) ; aFaceExplorer.More() ; aFaceExplorer.Next()){
424         TopoDS_Face aFace = TopoDS::Face(aFaceExplorer.Current());
425     }
426 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
427
428 For each detected face, you need to access the geometric properties of the shape: use the *BRep_Tool* class for that. The most commonly used methods of this class are:
429     
430   * *Surface* to access the surface of a face;
431   * *Curve* to access the 3D curve of an edge;
432   * *Point* to access the 3D point of a vertex.
433
434 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
435 Handle(Geom_Surface) aSurface = BRep_Tool::Surface(aFace);
436 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
437
438 As you can see, the *BRep_Tool::Surface* method returns an instance of the *Geom_Surface* class manipulated by handle. However, the *Geom_Surface* class does not provide information about the real type of the object *aSurface*, which could be an instance of *Geom_Plane*, *Geom_CylindricalSurface*, etc.
439 All objects manipulated by handle, like *Geom_Surface*, inherit from the *Standard_Transient* class which provides two very useful methods concerning types:
440
441   * *DynamicType* to know the real type of the object
442   * *IsKind* to know if the object inherits from one particular type
443
444 DynamicType returns the real type of the object, but you need to compare it with the existing known types to determine whether *aSurface* is a plane, a cylindrical surface or some other type.
445 To compare a given type with the type you seek, use the *STANDARD_TYPE* macro, which returns the type of a class:
446
447 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
448     if(aSurface->DynamicType() == STANDARD_TYPE(Geom_Plane)){
449     //
450     }
451 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
452
453 If this comparison is true, you know that the *aSurface* real type is *Geom_Plane*. You can then convert it from *Geom_Surface* to *Geom_Plane* by using the *DownCast()* method provided by each class inheriting *Standard_Transient*. As its name implies, this static method is used to downcast objects to a given type with the following syntax:
454
455 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
456     Handle(Geom_Plane) aPlane = Handle(Geom_Plane)::DownCast(aSurface);
457 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
458
459 Remember that the goal of all these conversions is to find the highest face of the bottle lying on a plane. Suppose that you have these two global variables:
460
461 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
462     TopoDS_Face faceToRemove;
463     Standard_Real zMax = -1;
464 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
465
466 You can easily find the plane whose origin is the biggest in Z knowing that the location of the plane is given with the *Geom_Plane::Location* method. For example:
467
468 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
469     gp_Pnt aPnt = aPlane->Location();
470     Standard_Real aZ = aPnt.Z();
471     if(aZ > zMax){
472         zMax = aZ;
473         faceToRemove = aFace;
474     }
475 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
476
477 You have now found the top face of the neck. Your final step before creating the hollowed solid is to put this face in a list. Since more than one face can be removed from the initial solid, the *BRepOffsetAPI_MakeThickSolid* constructor takes a list of faces as arguments.
478 Open CASCADE Technology provides many collections for different kinds of objects: see *TColGeom* package for collections of objects from *Geom* package, *TColgp* package for collections of objects from gp package, etc.
479 The collection for shapes can be found in the *TopTools* package. As *BRepOffsetAPI_MakeThickSolid* requires a list, use the *TopTools_ListOfShape* class.
480
481 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
482     TopTools_ListOfShape facesToRemove;
483     facesToRemove.Append(faceToRemove);
484 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
485
486 All the necessary data are now available so you can create your hollowed solid by calling the *BRepOffsetAPI_MakeThickSolid* constructor:
487
488 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
489     MyBody = BRepOffsetAPI_MakeThickSolid(myBody, facesToRemove, -myThickness / 50, 1.e-3);
490 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
491
492
493 @section sec4 Building the Threading
494
495
496 @subsection OCCT_TUTORIAL_SUB4_1 Creating Surfaces
497
498
499 Up to now, you have learned how to create edges out of 3D curves.
500 You will now learn how to create an edge out of a 2D curve and a surface.
501 To learn this aspect of Open CASCADE Technology, you will build helicoidal profiles out of 2D curves on cylindrical surfaces. The theory is more complex than in previous steps, but applying it is very simple.
502 As a first step, you compute these cylindrical surfaces. You are already familiar with curves of the *Geom* package. Now you can create a cylindrical surface (*Geom_CylindricalSurface*) using:
503
504   * a coordinate system;
505   * a radius.
506
507 Using the same coordinate system *neckAx2* used to position the neck, you create two cylindrical surfaces *Geom_CylindricalSurface* with the following radii:
508
509 @image html /overview/tutorial/images/tutorial_image011.png
510 @image latex /overview/tutorial/images/tutorial_image011.png
511
512 Notice that one of the cylindrical surfaces is smaller than the neck. There is a good reason for this: after the thread creation, you will fuse it with the neck. So, we must make sure that the two shapes remain in contact.
513
514 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
515     Handle(Geom_CylindricalSurface) aCyl1 = new Geom_CylindricalSurface(neckAx2, myNeckRadius * 0.99);
516
517     Handle(Geom_CylindricalSurface) aCyl2 = new Geom_CylindricalSurface(neckAx2, myNeckRadius * 1.05);
518 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
519
520
521 @subsection OCCT_TUTORIAL_SUB4_2 Defining 2D Curves
522
523
524 To create the neck of the bottle, you made a solid cylinder based on a cylindrical surface. You will create the profile of threading by creating 2D curves on such a surface.
525 All geometries defined in the *Geom* package are parameterized. This means that each curve or surface from Geom is computed with a parametric equation.
526 A *Geom_CylindricalSurface* surface is defined with the following parametric equation:
527
528 P(U, V) = O + R * (cos(U) * xDir + sin(U) * yDir) + V * zDir, where :
529
530   * P is the point defined by parameters (U, V).
531   * O, *Dir, yDir and zDir are respectively the origin, the X direction, Y direction and Z direction of the cylindrical surface local coordinate system.
532   * R is the radius of the cylindrical surface.
533   * U range is [0, 2PI] and V is infinite.
534
535 @image html /overview/tutorial/images/tutorial_image012.png
536 @image latex /overview/tutorial/images/tutorial_image012.png
537
538 The advantage of having such parameterized geometries is that you can compute, for any (U, V) parameters of the surface:
539
540   * the 3D point;
541   * the derivative vectors of order 1, 2 to N at this point.
542
543 There is another advantage of these parametric equations: you can consider a surface as a 2D parametric space defined with a (U, V) coordinate system. For example, consider the parametric ranges of the neck's surface:
544
545 @image html /overview/tutorial/images/tutorial_image013.png
546 @image latex /overview/tutorial/images/tutorial_image013.png
547
548 Suppose that you create a 2D line on this parametric (U, V) space and compute its 3D parametric curve. Depending on the line definition, results are as follows:
549
550 | Case          | Parametric Equation                                          | Parametric Curve                                                              |
551 | :------------ | :----------------------------------------------------------- | :---------------------------------------------------------------------------- |
552 | U = 0         | P(V) = O + V * zDir                                          | Line parallel to the Z direction                                              |
553 | V = 0         | P(U) = O + R * (cos(U) * xDir + sin(U) * yDir)               | Circle parallel to the (O, X, Y) plane                                        |
554 | U != 0 V != 0 | P(U, V) = O + R * (cos(U) * xDir + sin(U) * yDir) + V * zDir | Helicoidal curve describing the evolution of height and angle on the cylinder |
555
556 The helicoidal curve type is exactly what you need. On the neck's surface, the evolution laws of this curve will be:
557
558   * In V parameter: between 0 and myHeighNeck for the height description
559   * In U parameter: between 0 and 2PI for the angle description. But, since a cylindrical surface is U periodic, you can decide to extend this angle evolution to 4PI as shown in the following drawing:
560
561 @image html /overview/tutorial/images/tutorial_image014.png
562 @image latex /overview/tutorial/images/tutorial_image014.png
563
564 In this (U, V) parametric space, you will create a local (X, Y) coordinate system to position the curves to be created. This coordinate system will be defined with:
565
566   * A center located in the middle of the neck's cylinder parametric space at (2*PI, myNeckHeight / 2) in U, V coordinates.
567   * A X direction defined with the (2*PI, myNeckHeight/4) vector in U, V coordinates, so that the curves occupy half of the neck's surfaces.
568   
569 @image html /overview/tutorial/images/tutorial_image015.png
570 @image latex /overview/tutorial/images/tutorial_image015.png
571   
572 To use 2D primitive geometry types of Open CASCADE Technology for defining a point and a coordinate system, you will once again instantiate classes from gp:
573
574   * To define a 2D point from its X and Y coordinates, use the *gp_Pnt2d* class.
575   * To define a 2D direction (unit vector) from its X and Y coordinates, use the gp_Dir2d class. The coordinates will automatically be normalized.
576   * To define a 2D right-handed coordinate system, use the *gp_Ax2d* class, which is computed from a point (origin of the coordinate system) and a direction - the X direction of the coordinate system. The Y direction will be automatically computed.
577   
578 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
579     gp_Pnt2d aPnt(2. * M_PI, myNeckHeight / 2.);
580     gp_Dir2d aDir(2. * M_PI, myNeckHeight / 4.);
581     gp_Ax2d anAx2d(aPnt, aDir);
582 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
583
584 You will now define the curves. As previously mentioned, these thread profiles are computed on two cylindrical surfaces. In the following figure, curves on the left define the base (on *aCyl1* surface) and the curves on the right define the top of the thread's shape (on *aCyl2* surface).
585
586 @image html /overview/tutorial/images/tutorial_image016.png
587 @image latex /overview/tutorial/images/tutorial_image016.png
588
589 You have already used the *Geom* package to define 3D geometric entities. For 2D, you will use the *Geom2d* package. As for *Geom*, all geometries are parameterized. For example, a *Geom2d_Ellipse* ellipse is defined from:
590
591   * a coordinate system whose origin is the ellipse center;
592   * a major radius on the major axis defined by the X direction of the coordinate system;
593   * a minor radius on the minor axis defined by the Y direction of the coordinate system.
594
595 Supposing that:
596
597   * Both ellipses have the same major radius of 2*PI,
598   * Minor radius of the first ellipse is myNeckHeight / 10,
599   * And the minor radius value of the second ellipse is a fourth of the first one,
600
601 Your ellipses are defined as follows:
602
603 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
604     Standard_Real aMajor = 2. * M_PI;
605     Standard_Real aMinor = myNeckHeight / 10;
606     Handle(Geom2d_Ellipse) anEllipse1 = new Geom2d_Ellipse(anAx2d, aMajor, aMinor);
607     Handle(Geom2d_Ellipse) anEllipse2 = new Geom2d_Ellipse(anAx2d, aMajor, aMinor / 4);
608 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
609
610 To describe portions of curves for the arcs drawn above, you define *Geom2d_TrimmedCurve* trimmed curves out of the created ellipses and two parameters to limit them.
611 As the parametric equation of an ellipse is P(U) = O + (MajorRadius * cos(U) * XDirection) + (MinorRadius * sin(U) * YDirection), the ellipses need to be limited between 0 and M_PI.
612
613 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
614     Handle(Geom2d_TrimmedCurve) anArc1 = new Geom2d_TrimmedCurve(anEllipse1, 0, M_PI);
615     Handle(Geom2d_TrimmedCurve) anArc2 = new Geom2d_TrimmedCurve(anEllipse2, 0, M_PI);
616 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
617
618 The last step consists in defining the segment, which is the same for the two profiles: a line limited by the first and the last point of one of the arcs.
619 To access the point corresponding to the parameter of a curve or a surface, you use the Value or D0 method (meaning 0th derivative), D1 method is for first derivative, D2 for the second one.
620
621 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
622     gp_Pnt2d anEllipsePnt1 = anEllipse1->Value(0);
623     gp_Pnt2d anEllipsePnt2;
624     anEllipse1->D0(M_PI, anEllipsePnt2);
625 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
626
627 When creating the bottle's profile, you used classes from the *GC* package, providing algorithms to create elementary geometries.
628 In 2D geometry, this kind of algorithms is found in the *GCE2d* package. Class names and behaviors are similar to those in *GC*. For example, to create a 2D segment out of two points:
629
630 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
631     Handle(Geom2d_TrimmedCurve) aSegment = GCE2d_MakeSegment(anEllipsePnt1, anEllipsePnt2);
632 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
633
634
635 @subsection OCCT_TUTORIAL_SUB4_3 Building Edges and Wires
636
637
638 As you did when creating the base profile of the bottle, you can now:
639
640   * compute the edges of the neck's threading.
641   * compute two wires out of these edges.
642
643 @image html /overview/tutorial/images/tutorial_image017.png
644 @image latex /overview/tutorial/images/tutorial_image017.png
645
646 Previously, you have built:
647
648   * two cylindrical surfaces of the threading
649   * three 2D curves defining the base geometry of the threading
650
651 To compute the edges out of these curves, once again use the *BRepBuilderAPI_MakeEdge* class. One of its constructors allows you to build an edge out of a curve described in the 2D parametric space of a surface.
652
653 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
654     TopoDS_Edge anEdge1OnSurf1 = BRepBuilderAPI_MakeEdge(anArc1, aCyl1);
655     TopoDS_Edge anEdge2OnSurf1 = BRepBuilderAPI_MakeEdge(aSegment, aCyl1);
656     TopoDS_Edge anEdge1OnSurf2 = BRepBuilderAPI_MakeEdge(anArc2, aCyl2);
657     TopoDS_Edge anEdge2OnSurf2 = BRepBuilderAPI_MakeEdge(aSegment, aCyl2);
658 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
659
660 Now, you can create the two profiles of the threading, lying on each surface.
661
662 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
663     TopoDS_Wire threadingWire1 = BRepBuilderAPI_MakeWire(anEdge1OnSurf1, anEdge2OnSurf1);
664     TopoDS_Wire threadingWire2 = BRepBuilderAPI_MakeWire(anEdge1OnSurf2, anEdge2OnSurf2);
665 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
666
667 Remember that these wires were built out of a surface and 2D curves.
668 One important data item is missing as far as these wires are concerned: there is no information on the 3D curves. Fortunately, you do not need to compute this yourself, which can be a difficult task since the mathematics can be quite complex.
669 When a shape contains all the necessary information except 3D curves, Open CASCADE Technology provides a tool to build them automatically. In the BRepLib tool package, you can use the *BuildCurves3d* method to compute 3D curves for all the edges of a shape.
670
671 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} 
672     BRepLib::BuildCurves3d(threadingWire1);
673     BRepLib::BuildCurves3d(threadingWire2);
674 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
675
676
677 @subsection OCCT_TUTORIAL_SUB4_4 Creating Threading
678
679
680 You have computed the wires of the threading. The threading will be a solid shape, so you must now compute the faces of the wires, the faces allowing you to join the wires, the shell out of these faces and then the solid itself. This can be a lengthy operation.
681 There are always faster ways to build a solid when the base topology is defined. You would like to create a solid out of two wires. Open CASCADE Technology provides a quick way to do this by building a loft: a shell or a solid passing through a set of wires in a given sequence.   
682 The loft function is implemented in the *BRepOffsetAPI_ThruSections* class, which you use as follows:
683   
684 @image html /overview/tutorial/images/tutorial_image018.png
685 @image latex /overview/tutorial/images/tutorial_image018.png
686   
687   * Initialize the algorithm by creating an instance of the class. The first parameter of this constructor must be specified if you want to create a solid. By default, *BRepOffsetAPI_ThruSections* builds a shell.
688   * Add the successive wires using the AddWire method.
689   * Use the *CheckCompatibility* method to activate (or deactivate) the option that checks whether the wires have the same number of edges. In this case, wires have two edges each, so you can deactivate this option.
690   * Ask for the resulting loft shape with the Shape method.
691
692 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} 
693     BRepOffsetAPI_ThruSections aTool(Standard_True);
694     aTool.AddWire(threadingWire1); aTool.AddWire(threadingWire2);
695     aTool.CheckCompatibility(Standard_False);
696     TopoDS_Shape myThreading = aTool.Shape();
697 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
698
699
700 @section sec5 Building the Resulting Compound
701
702
703 You are almost done building the bottle. Use the *TopoDS_Compound* and *BRep_Builder* classes to build single shape from *myBody* and *myThreading*:
704
705 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} 
706     TopoDS_Compound aRes;
707     BRep_Builder aBuilder;
708     aBuilder.MakeCompound (aRes);
709     aBuilder.Add (aRes, myBody);
710     aBuilder.Add (aRes, myThreading);
711 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
712
713 Congratulations! Your bottle is complete. Here is the result snapshot of the Tutorial application:
714
715 @image html /overview/tutorial/images/tutorial_image019.png
716 @image latex /overview/tutorial/images/tutorial_image019.png
717
718 We hope that this tutorial has provided you with a feel for the industrial strength power of Open CASCADE Technology.
719 If you want to know more and develop major projects using Open CASCADE Technology, we invite you to study our training, support, and consulting services on our site at http://www.opencascade.org/support. Our professional services can maximize the power of your Open CASCADE Technology applications.
720
721
722 @section sec6 Appendix
723
724
725 Complete definition of MakeBottle function (defined in the file src/MakeBottle.cxx of the Tutorial):
726
727 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp} 
728     TopoDS_Shape MakeBottle(const Standard_Real myWidth, const Standard_Real myHeight,
729                             const Standard_Real myThickness)
730     {
731         // Profile : Define Support Points
732         gp_Pnt aPnt1(-myWidth / 2., 0, 0);        
733         gp_Pnt aPnt2(-myWidth / 2., -myThickness / 4., 0);
734         gp_Pnt aPnt3(0, -myThickness / 2., 0);
735         gp_Pnt aPnt4(myWidth / 2., -myThickness / 4., 0);
736         gp_Pnt aPnt5(myWidth / 2., 0, 0);
737
738         // Profile : Define the Geometry
739         Handle(Geom_TrimmedCurve) anArcOfCircle = GC_MakeArcOfCircle(aPnt2,aPnt3,aPnt4);
740         Handle(Geom_TrimmedCurve) aSegment1 = GC_MakeSegment(aPnt1, aPnt2);
741         Handle(Geom_TrimmedCurve) aSegment2 = GC_MakeSegment(aPnt4, aPnt5);
742
743         // Profile : Define the Topology
744         TopoDS_Edge anEdge1 = BRepBuilderAPI_MakeEdge(aSegment1);
745         TopoDS_Edge anEdge2 = BRepBuilderAPI_MakeEdge(anArcOfCircle);
746         TopoDS_Edge anEdge3 = BRepBuilderAPI_MakeEdge(aSegment2);
747         TopoDS_Wire aWire  = BRepBuilderAPI_MakeWire(anEdge1, anEdge2, anEdge3);
748
749         // Complete Profile
750         gp_Ax1 xAxis = gp::OX();
751         gp_Trsf aTrsf;
752
753         aTrsf.SetMirror(xAxis);
754         BRepBuilderAPI_Transform aBRepTrsf(aWire, aTrsf);
755         TopoDS_Shape aMirroredShape = aBRepTrsf.Shape();
756         TopoDS_Wire aMirroredWire = TopoDS::Wire(aMirroredShape);
757
758         BRepBuilderAPI_MakeWire mkWire;
759         mkWire.Add(aWire);
760         mkWire.Add(aMirroredWire);
761         TopoDS_Wire myWireProfile = mkWire.Wire();
762
763         // Body : Prism the Profile
764         TopoDS_Face myFaceProfile = BRepBuilderAPI_MakeFace(myWireProfile);
765         gp_Vec aPrismVec(0, 0, myHeight);
766         TopoDS_Shape myBody = BRepPrimAPI_MakePrism(myFaceProfile, aPrismVec);
767
768         // Body : Apply Fillets
769         BRepFilletAPI_MakeFillet mkFillet(myBody);
770         TopExp_Explorer anEdgeExplorer(myBody, TopAbs_EDGE);
771         while(anEdgeExplorer.More()){
772             TopoDS_Edge anEdge = TopoDS::Edge(anEdgeExplorer.Current());
773             //Add edge to fillet algorithm
774             mkFillet.Add(myThickness / 12., anEdge);
775             anEdgeExplorer.Next();
776         }
777
778         myBody = mkFillet.Shape();
779
780         // Body : Add the Neck
781         gp_Pnt neckLocation(0, 0, myHeight);
782         gp_Dir neckAxis = gp::DZ();
783         gp_Ax2 neckAx2(neckLocation, neckAxis);
784
785         Standard_Real myNeckRadius = myThickness / 4.;
786         Standard_Real myNeckHeight = myHeight / 10.;
787
788         BRepPrimAPI_MakeCylinder MKCylinder(neckAx2, myNeckRadius, myNeckHeight);
789         TopoDS_Shape myNeck = MKCylinder.Shape();
790
791         myBody = BRepAlgoAPI_Fuse(myBody, myNeck);
792
793         // Body : Create a Hollowed Solid
794         TopoDS_Face   faceToRemove;
795         Standard_Real zMax = -1;
796
797         for(TopExp_Explorer aFaceExplorer(myBody, TopAbs_FACE); aFaceExplorer.More(); aFaceExplorer.Next()){
798             TopoDS_Face aFace = TopoDS::Face(aFaceExplorer.Current());
799             // Check if <aFace> is the top face of the bottle's neck 
800             Handle(Geom_Surface) aSurface = BRep_Tool::Surface(aFace);
801             if(aSurface->DynamicType() == STANDARD_TYPE(Geom_Plane)){
802                 Handle(Geom_Plane) aPlane = Handle(Geom_Plane)::DownCast(aSurface);
803                 gp_Pnt aPnt = aPlane->Location();
804                 Standard_Real aZ   = aPnt.Z();
805                 if(aZ > zMax){
806                     zMax = aZ;
807                     faceToRemove = aFace;
808                 }
809             }
810         }
811
812         TopTools_ListOfShape facesToRemove;
813         facesToRemove.Append(faceToRemove);
814         myBody = BRepOffsetAPI_MakeThickSolid(myBody, facesToRemove, -myThickness / 50, 1.e-3);
815         // Threading : Create Surfaces
816         Handle(Geom_CylindricalSurface) aCyl1 = new Geom_CylindricalSurface(neckAx2, myNeckRadius * 0.99);
817         Handle(Geom_CylindricalSurface) aCyl2 = new Geom_CylindricalSurface(neckAx2, myNeckRadius * 1.05);
818
819         // Threading : Define 2D Curves
820         gp_Pnt2d aPnt(2. * M_PI, myNeckHeight / 2.);
821         gp_Dir2d aDir(2. * M_PI, myNeckHeight / 4.);
822         gp_Ax2d anAx2d(aPnt, aDir);
823
824         Standard_Real aMajor = 2. * M_PI;
825         Standard_Real aMinor = myNeckHeight / 10;
826
827         Handle(Geom2d_Ellipse) anEllipse1 = new Geom2d_Ellipse(anAx2d, aMajor, aMinor);
828         Handle(Geom2d_Ellipse) anEllipse2 = new Geom2d_Ellipse(anAx2d, aMajor, aMinor / 4);
829         Handle(Geom2d_TrimmedCurve) anArc1 = new Geom2d_TrimmedCurve(anEllipse1, 0, M_PI);
830         Handle(Geom2d_TrimmedCurve) anArc2 = new Geom2d_TrimmedCurve(anEllipse2, 0, M_PI);
831         gp_Pnt2d anEllipsePnt1 = anEllipse1->Value(0);
832         gp_Pnt2d anEllipsePnt2 = anEllipse1->Value(M_PI);
833
834         Handle(Geom2d_TrimmedCurve) aSegment = GCE2d_MakeSegment(anEllipsePnt1, anEllipsePnt2);
835         // Threading : Build Edges and Wires
836         TopoDS_Edge anEdge1OnSurf1 = BRepBuilderAPI_MakeEdge(anArc1, aCyl1);
837         TopoDS_Edge anEdge2OnSurf1 = BRepBuilderAPI_MakeEdge(aSegment, aCyl1);
838         TopoDS_Edge anEdge1OnSurf2 = BRepBuilderAPI_MakeEdge(anArc2, aCyl2);
839         TopoDS_Edge anEdge2OnSurf2 = BRepBuilderAPI_MakeEdge(aSegment, aCyl2);
840         TopoDS_Wire threadingWire1 = BRepBuilderAPI_MakeWire(anEdge1OnSurf1, anEdge2OnSurf1);
841         TopoDS_Wire threadingWire2 = BRepBuilderAPI_MakeWire(anEdge1OnSurf2, anEdge2OnSurf2);
842         BRepLib::BuildCurves3d(threadingWire1);
843         BRepLib::BuildCurves3d(threadingWire2);
844
845         // Create Threading 
846         BRepOffsetAPI_ThruSections aTool(Standard_True);
847         aTool.AddWire(threadingWire1);
848         aTool.AddWire(threadingWire2);
849         aTool.CheckCompatibility(Standard_False);
850
851         TopoDS_Shape myThreading = aTool.Shape();
852
853         // Building the Resulting Compound 
854         TopoDS_Compound aRes;
855         BRep_Builder aBuilder;
856         aBuilder.MakeCompound (aRes);
857         aBuilder.Add (aRes, myBody);
858         aBuilder.Add (aRes, myThreading);
859
860         return aRes;
861     }
862 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~