3333096a875c180958a8e106bb7752c115734706
[occt.git] / src / BRepFeat / BRepFeat_MakeLinearForm.cxx
1 // File:        BRepFeat_MakeLinearForm.cxx
2 // Created:     Mon Apr 14 13:34:46 1997
3 // Author:      Olga KOULECHOVA
4 //              <opt@langdox.paris1.matra-dtv.fr>
5
6
7 #include <BRepFeat_MakeLinearForm.ixx>
8
9 #include <BRepFeat.hxx>
10
11 #include <LocOpe.hxx>
12 #include <LocOpe_Builder.hxx>
13 #include <LocOpe_LinearForm.hxx>
14 #include <LocOpe_Gluer.hxx>
15 #include <LocOpe_FindEdges.hxx>
16
17 #include <gp_Vec.hxx>
18 #include <gp_Pln.hxx>
19 #include <gp_Lin.hxx>
20 #include <gp_Dir.hxx>
21 #include <gp_Pnt.hxx>
22
23 #include <gp_Vec2d.hxx>
24 #include <gp_Pnt2d.hxx>
25
26 #include <Geom_Curve.hxx>
27
28 #include <Geom2d_Curve.hxx>
29 #include <Geom2d_Line.hxx>
30
31 #include <Geom_Plane.hxx>
32 #include <Geom_Surface.hxx>
33 #include <Geom_CylindricalSurface.hxx>
34 #include <Geom_RectangularTrimmedSurface.hxx>
35
36 #include <Geom_TrimmedCurve.hxx>
37 #include <GeomProjLib.hxx>
38
39 #include <Geom2d_Curve.hxx>
40 #include <Geom2d_Curve.hxx>
41
42 #include <TColgp_SequenceOfPnt.hxx>
43
44 #include <TColStd_Array1OfReal.hxx>
45 #include <IntRes2d_IntersectionPoint.hxx>
46
47 #include <BRepTools_WireExplorer.hxx>
48
49 #include <BRep_Tool.hxx>
50 #include <BRep_Builder.hxx>
51
52 #include <TopExp_Explorer.hxx>
53 #include <TopExp.hxx>
54
55 #include <TopTools_MapOfShape.hxx>
56 #include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
57 #include <TopTools_ListOfShape.hxx>
58 #include <TopTools_ListIteratorOfListOfShape.hxx>
59 #include <TopTools_DataMapIteratorOfDataMapOfShapeListOfShape.hxx>
60 #include <TopTools_MapIteratorOfMapOfShape.hxx>
61 #include <TopOpeBRepBuild_HBuilder.hxx>
62 #include <TopTools_DataMapIteratorOfDataMapOfShapeShape.hxx>
63
64 #include <TColgp_Array1OfPnt.hxx>
65
66 #include <TColGeom_Array1OfCurve.hxx>
67
68 #include <TopoDS.hxx>
69 #include <TopoDS_Shape.hxx>
70 #include <TopoDS_Face.hxx>
71
72 #include <BRepLib_MakeVertex.hxx>
73 #include <BRepLib_MakeEdge.hxx>
74 #include <BRepLib_MakeFace.hxx>
75
76 #include <BRepTools.hxx>
77
78 #include <BRepBuilderAPI.hxx>
79 #include <BRepPrimAPI_MakeBox.hxx>
80 #include <BRepBuilderAPI_MakeFace.hxx>
81
82 //modified by NIZNHY-PKV Fri Mar 22 16:51:33 2002 f
83 //#include <BRepAlgo_Section.hxx>
84 //#include <BRepAlgo_Common.hxx>
85 #include <BRepAlgoAPI_Section.hxx>
86 #include <BRepAlgoAPI_Common.hxx>
87 //modified by NIZNHY-PKV Fri Mar 22 16:51:35 2002 t
88
89 #include <BRepExtrema_ExtPC.hxx>
90 #include <BRepExtrema_ExtPF.hxx>
91 #include <BRepExtrema_ExtCF.hxx>
92
93 #include <BRepTools_Modifier.hxx>
94 #include <BRepTools_TrsfModification.hxx>
95
96 #include <Standard_ConstructionError.hxx>
97
98 #include <Precision.hxx>
99
100 #include <GeomLProp_CLProps.hxx>
101
102 #include <BRepBuilderAPI_Transform.hxx>
103 #include <BRepLib_MakeWire.hxx>
104 //#include <DbgTools.hxx>
105
106 #ifdef DEB
107 Standard_IMPORT Standard_Boolean BRepFeat_GettraceFEAT();
108 Standard_IMPORT Standard_Boolean BRepFeat_GettraceFEATRIB();
109 #endif
110
111 static void MajMap(const TopoDS_Shape&, // base
112                    const LocOpe_LinearForm&,
113                    TopTools_DataMapOfShapeListOfShape&, // myMap
114                    TopoDS_Shape&,  // myFShape
115                    TopoDS_Shape&); // myLShape
116
117 static void SetGluedFaces(const TopTools_DataMapOfShapeListOfShape& theSlmap,
118                           LocOpe_LinearForm&,
119                           TopTools_DataMapOfShapeShape&);
120
121 //=======================================================================
122 //function : Init
123 //purpose  : 
124 //=======================================================================
125
126 void BRepFeat_MakeLinearForm::Init(const TopoDS_Shape& Sbase,
127                                    const TopoDS_Wire& W,
128                                    const Handle(Geom_Plane)& Plane,
129                                    const gp_Vec& Direc,
130                                    const gp_Vec& Direc1,
131                                    const Standard_Integer Mode,
132                                    const Standard_Boolean Modify)
133 {
134 #ifdef DEB
135   Standard_Boolean trc = BRepFeat_GettraceFEAT();
136   if (trc) cout << "BRepFeat_MakeLinearForm::Init" << endl;
137 #endif
138   Standard_Boolean RevolRib = Standard_False;
139   Done();
140   myGenerated.Clear();
141   
142 // modify = 0 if there is no intention to make sliding
143 //        = 1 if one tries to make sliding
144   Standard_Boolean Sliding = Modify;
145   myLFMap.Clear();
146
147   myShape.Nullify();
148   myMap.Clear();
149   myFShape.Nullify();
150   myLShape.Nullify();
151   mySbase  = Sbase;
152   mySkface.Nullify();
153   myPbase.Nullify();
154
155   myGShape.Nullify();
156   mySUntil.Nullify();
157   myListOfEdges.Clear();
158   mySlface.Clear();
159
160   TopoDS_Shape aLocalShape = W.Oriented(TopAbs_FORWARD);
161   myWire = TopoDS::Wire(aLocalShape);
162 //  myWire = TopoDS::Wire(W.Oriented(TopAbs_FORWARD));
163   myDir  = Direc;
164   myDir1 = Direc1;
165   myPln  = Plane;
166
167   if(Mode == 0) 
168     myFuse   = Standard_False;
169   else // if(Mode == 1) 
170     myFuse   = Standard_True;
171 #ifdef DEB
172   if (trc) {
173     if (myFuse)  cout << " Fuse" << endl;
174     if (!myFuse)  cout << " Cut" << endl;
175   }
176 #endif
177
178   
179 // ---Determine Tolerance : max tolerance on parameters
180   myTol = Precision::Confusion();
181
182   TopExp_Explorer exx;  
183   exx.Init(myWire, TopAbs_VERTEX);
184   for(; exx.More(); exx.Next()) {
185     const Standard_Real& tol = BRep_Tool::
186       Tolerance(TopoDS::Vertex(exx.Current()));
187     if(tol > myTol) myTol = tol;
188   }
189
190   exx.Init(Sbase, TopAbs_VERTEX);
191   for(; exx.More(); exx.Next()) {
192     const Standard_Real& tol = BRep_Tool::
193       Tolerance(TopoDS::Vertex(exx.Current()));
194     if(tol > myTol) myTol = tol;
195   }
196
197 // ---Control of directions
198 //    the wire should be in the rib
199   gp_Vec nulldir(0, 0, 0);
200   if(!myDir1.IsEqual(nulldir, myTol, myTol)) {
201     Standard_Real ang = myDir1.Angle(myDir);
202     if(ang != PI) {
203 #ifdef DEB
204       if (trc) cout << " Directions must be opposite" << endl;
205 #endif
206       myStatusError = BRepFeat_BadDirect;
207       NotDone();
208       return;
209     }
210   }
211   else {
212
213 // Rib is centre in the middle of translation
214 #ifdef DEB
215     if (trc)  cout << " Rib is centre" << endl;
216 #endif
217     const gp_Vec& DirTranslation = (Direc + Direc1) * 0.5;
218     gp_Trsf T;
219     T.SetTranslation(DirTranslation);
220     BRepBuilderAPI_Transform trf(T);
221     trf.Perform(myWire);
222     myWire = TopoDS::Wire(trf.Shape());
223     myDir  = Direc  - DirTranslation;
224     myDir1 = Direc1 - DirTranslation;
225     myPln->Transform(T);
226   }
227
228 // ---Calculate bounding box
229   BRep_Builder BB;
230
231   TopTools_ListOfShape theList;  
232   
233   TopoDS_Shape U;
234   U.Nullify();
235   gp_Pnt FirstCorner, LastCorner;
236   Standard_Real bnd = HeightMax(mySbase, U, FirstCorner, LastCorner);
237   myBnd = bnd;
238
239   BRepPrimAPI_MakeBox Bndbox(FirstCorner, LastCorner);
240   TopoDS_Solid BndBox = Bndbox.Solid();
241
242
243 // ---Construction of the face workplane (section bounding box)
244   BRepLib_MakeFace PlaneF(myPln->Pln(), -6.*myBnd, 
245                           6.*myBnd, -6.*myBnd, 6.*myBnd);
246   TopoDS_Face PlaneFace = TopoDS::Face(PlaneF.Shape());
247
248   //modified by NIZNHY-PKV Fri Mar 22 16:49:28 2002 f
249   //BRepAlgo_Common PlaneS(BndBox, PlaneFace);
250   BRepAlgoAPI_Common PlaneS(BndBox, PlaneFace);
251   //modified by NIZNHY-PKV Fri Mar 22 16:49:39 2002 t  
252   TopExp_Explorer EXP;
253   TopoDS_Shape PlaneSect = PlaneS.Shape();
254   EXP.Init(PlaneSect, TopAbs_WIRE);
255   TopoDS_Wire www = TopoDS::Wire(EXP.Current());
256   BRepLib_MakeFace Bndface(myPln->Pln(), www, Standard_True);
257   TopoDS_Face BndFace = TopoDS::Face(Bndface.Shape());
258
259
260 // ---Find support faces of the rib
261   TopoDS_Edge FirstEdge, LastEdge;
262   TopoDS_Face FirstFace, LastFace;
263   TopoDS_Vertex FirstVertex, LastVertex;
264
265   Standard_Boolean OnFirstFace = Standard_False;
266   Standard_Boolean OnLastFace = Standard_False;
267   Standard_Boolean PtOnFirstEdge = Standard_False;
268   Standard_Boolean PtOnLastEdge = Standard_False;
269   TopoDS_Edge OnFirstEdge, OnLastEdge;
270   OnFirstEdge.Nullify();
271   OnLastEdge.Nullify();
272
273   Standard_Boolean Data = ExtremeFaces(RevolRib, myBnd, myPln, FirstEdge, LastEdge, 
274                                        FirstFace, LastFace, FirstVertex, 
275                                        LastVertex, OnFirstFace, OnLastFace,
276                                        PtOnFirstEdge, PtOnLastEdge,
277                                        OnFirstEdge, OnLastEdge);
278  
279   if(!Data) {
280 #ifdef DEB
281     if (trc) cout << " No Extreme faces" << endl;
282 #endif
283     myStatusError = BRepFeat_NoExtFace;
284     NotDone();
285     return;
286   }
287
288
289 // ---Proofing Point for the side of the wire to be filled - side material
290   gp_Pnt CheckPnt = CheckPoint(FirstEdge, bnd/10., myPln);
291
292 //  Standard_Real f, l;
293
294 // ---Control sliding valuable
295 // Many cases when the sliding is abandoned
296   Standard_Integer Concavite = 3;  // a priori the profile is not concave
297
298   myFirstPnt = BRep_Tool::Pnt(FirstVertex);
299   myLastPnt  = BRep_Tool::Pnt(LastVertex);
300
301 // SliList : list of faces concerned by the rib
302   TopTools_ListOfShape SliList;
303   SliList.Append(FirstFace);
304
305   if(Sliding) {    // sliding
306 #ifdef DEB
307     if (trc) cout << " Sliding" << endl;
308 #endif
309     Standard_Boolean Sliding = Standard_False;
310     Handle(Geom_Surface) s = BRep_Tool::Surface(FirstFace);
311     if (s->DynamicType() == 
312         STANDARD_TYPE(Geom_RectangularTrimmedSurface)) {
313       s = Handle(Geom_RectangularTrimmedSurface)::
314         DownCast(s)->BasisSurface();
315     }
316     if(s->DynamicType() == STANDARD_TYPE(Geom_Plane) ||
317        s->DynamicType() == STANDARD_TYPE(Geom_CylindricalSurface)) {
318 // if plane or cylinder : sliding is possible
319       Sliding = Standard_True;
320     }
321   }
322
323 // Control only start and end points
324 // -> no control at the middle - improve
325 // Controle between Surface and segment between 2 limit points
326 // is too expensive - improve
327   if(Sliding) {
328     gp_Pnt p1(myFirstPnt.X()+myDir.X(),myFirstPnt.Y()+myDir.Y(),
329               myFirstPnt.Z()+myDir.Z());
330     BRepLib_MakeEdge ee1(myFirstPnt, p1);
331     BRepExtrema_ExtCF ext1(ee1, FirstFace);
332     if(ext1.NbExt() == 1 && ext1.SquareDistance(1)<=BRep_Tool::Tolerance(FirstFace) * BRep_Tool::Tolerance(FirstFace)) {
333       gp_Pnt p2(myLastPnt.X()+myDir.X(),myLastPnt.Y()+myDir.Y(),
334                 myLastPnt.Z()+myDir.Z());
335       BRepLib_MakeEdge ee2(myLastPnt, p2);
336       BRepExtrema_ExtCF ext2(ee2, LastFace); // ExtCF : curves and surfaces
337       if(ext2.NbExt() == 1 && ext2.SquareDistance(1)<=BRep_Tool::Tolerance(LastFace) * BRep_Tool::Tolerance(LastFace)) {
338         Sliding = Standard_True;
339       }
340       else {
341         Sliding = Standard_False;
342       }      
343     }
344     else {
345       Sliding = Standard_False;
346     }   
347   }
348
349   if(!myDir1.IsEqual(nulldir, Precision::Confusion(), Precision::Confusion())) {
350     if(Sliding) {
351       gp_Pnt p1(myFirstPnt.X()+myDir1.X(),myFirstPnt.Y()+myDir1.Y(),
352                 myFirstPnt.Z()+myDir1.Z());
353       BRepLib_MakeEdge ee1(myFirstPnt, p1);
354       BRepExtrema_ExtCF ext1(ee1, FirstFace);
355       if(ext1.NbExt() == 1 && ext1.SquareDistance(1)<=BRep_Tool::Tolerance(FirstFace) * BRep_Tool::Tolerance(FirstFace)) {
356         gp_Pnt p2(myLastPnt.X()+myDir1.X(),myLastPnt.Y()+myDir1.Y(),
357                   myLastPnt.Z()+myDir1.Z());
358         BRepLib_MakeEdge ee2(myLastPnt, p2);
359         BRepExtrema_ExtCF ext2(ee2, LastFace);
360         if(ext2.NbExt() == 1 && ext2.SquareDistance(1)<=BRep_Tool::Tolerance(LastFace) * BRep_Tool::Tolerance(LastFace)) {
361           Sliding = Standard_True;
362         }
363         else {
364           Sliding = Standard_False;
365         }      
366       }
367       else {
368         Sliding = Standard_False;
369       }   
370     }
371   }
372
373
374 // Construct a great profile that goes till the bounding box
375 // -> by tangency with the first and the last edge of the Wire
376 // -> by normals to the support faces : statistically better
377 // Intersect everything to find the final profile
378
379
380 // ---case of sliding : construction of the profile face 
381   if(Sliding) {
382 #ifdef DEB
383     if (trc) cout << " still Sliding" << endl;
384 #endif
385     TopoDS_Face Prof;
386     Standard_Boolean ProfileOK;
387     ProfileOK = SlidingProfile(Prof,RevolRib,myTol,Concavite,myPln,BndFace,CheckPnt,
388                                FirstFace,LastFace,FirstVertex,LastVertex,
389                                FirstEdge,LastEdge);
390     
391     if (!ProfileOK) {
392 #ifdef DEB
393       cout << "Not computable" << endl;
394       if (trc) cout << "Face profile not computable" << endl;
395 #endif
396       myStatusError = BRepFeat_NoFaceProf;
397       NotDone();
398       return;
399     }
400
401
402 // ---Propagation on faces of the initial shape 
403 // to find the faces concerned by the rib
404     Standard_Boolean falseside = Standard_True;
405     Sliding = Propagate(SliList, Prof, myFirstPnt, myLastPnt, falseside);
406 // Control if there is everything required to have the material at the proper side
407     if(falseside == Standard_False) {
408 #ifdef DEB
409       cout << "Verify plane and wire orientation" << endl;
410 #endif
411       myStatusError = BRepFeat_FalseSide;
412       NotDone();
413       return;
414     }
415   }
416
417
418 // ---Generation of the base of the rib profile 
419
420   TopoDS_Wire w;
421   BB.MakeWire(w);
422   TopoDS_Edge thePreviousEdge;
423   TopoDS_Vertex theFV;
424   thePreviousEdge.Nullify();
425
426 // calculate the number of edges to fill the map
427   Standard_Integer counter = 1;
428
429 // ---case of sliding
430   if(Sliding && !myListOfEdges.IsEmpty()) {
431     BRepTools_WireExplorer EX1(myWire);
432     for(; EX1.More(); EX1.Next()) {
433       const TopoDS_Edge& E = EX1.Current();
434       if(!myLFMap.IsBound(E)) {
435         TopTools_ListOfShape theTmpList;
436         myLFMap.Bind(E, theTmpList );
437       }      
438       if(E.IsSame(FirstEdge)) {
439         Standard_Real f, l;
440         Handle(Geom_Curve) cc = BRep_Tool::Curve(E, f, l);
441         cc = new Geom_TrimmedCurve(cc, f, l);
442         gp_Pnt pt;
443         if(!FirstEdge.IsSame(LastEdge)) {
444           pt = BRep_Tool::Pnt(TopExp::LastVertex(E,Standard_True)); 
445         }
446         else {
447           pt = myLastPnt;
448           Standard_Real fpar = IntPar(cc, myFirstPnt);
449           Standard_Real lpar = IntPar(cc, pt);
450           Handle(Geom_Curve) ccc;
451           if(fpar > lpar) {
452             ccc = Handle(Geom_Curve)::DownCast(cc->Reversed());
453             cc = ccc;
454           }
455         }
456         TopoDS_Edge ee1;
457         if(thePreviousEdge.IsNull()) {
458           BRepLib_MakeVertex v1(myFirstPnt);
459           BRepLib_MakeVertex v2(pt);      
460           BRepLib_MakeEdge e(cc, v1, v2);
461           ee1 = TopoDS::Edge(e.Shape());
462         } 
463         else {
464           const TopoDS_Vertex& v1 = TopExp::LastVertex(thePreviousEdge,Standard_True);
465           BRepLib_MakeVertex v2(pt);
466           
467           BRepLib_MakeEdge e(cc, v1, v2);
468           ee1 = TopoDS::Edge(e.Shape());
469         }
470         TopoDS_Shape aLocalShape = ee1.Oriented(E.Orientation());
471         ee1 = TopoDS::Edge(aLocalShape);
472 //      ee1 = TopoDS::Edge(ee1.Oriented(E.Orientation()));
473         if(counter == 1) theFV = TopExp::FirstVertex(ee1,Standard_True);
474         myLFMap(E).Append(ee1);
475         BB.Add(w, ee1);
476         thePreviousEdge = ee1;
477         counter++;
478         EX1.Next();
479         break;
480       }
481     }
482  
483 // Case of several edges
484     if(!FirstEdge.IsSame(LastEdge)) {
485       for(; EX1.More(); EX1.Next()) {
486         const TopoDS_Edge& E = EX1.Current();
487         if(!myLFMap.IsBound(E)) {
488           TopTools_ListOfShape thelist1;
489           myLFMap.Bind(E, thelist1);
490         }      
491         theList.Append(E);
492         Standard_Real f, l;
493         if(!E.IsSame(LastEdge)) {
494           Handle(Geom_Curve) ccc = BRep_Tool::Curve(E, f, l);
495           TopoDS_Vertex v1, v2;
496           if(!thePreviousEdge.IsNull()) {
497             v1 = TopExp::LastVertex(thePreviousEdge,Standard_True);
498             v2 = TopExp::LastVertex(E,Standard_True);
499           }
500           else {
501 //          v1 = TopExp::LastVertex(E,Standard_True);
502             v1 = TopExp::FirstVertex(E,Standard_True);
503             v2 = TopExp::LastVertex(E,Standard_True);
504           }
505           BRepLib_MakeEdge E1(ccc, v1, v2);
506           TopoDS_Edge E11 = TopoDS::Edge(E1.Shape());
507           TopoDS_Shape aLocalShape = E11.Oriented(E.Orientation());
508           E11 = TopoDS::Edge(aLocalShape);
509 //        E11 = TopoDS::Edge(E11.Oriented(E.Orientation()));
510           thePreviousEdge = E11;
511           myLFMap(E).Append(E11);
512           BB.Add(w, E11);
513           if(counter == 1) theFV = TopExp::FirstVertex(E11,Standard_True);
514           counter++;
515         }
516         else {
517           Standard_Real f, l;
518           Handle(Geom_Curve) cc = BRep_Tool::Curve(E, f, l);
519           gp_Pnt pf = BRep_Tool::Pnt(TopExp::FirstVertex(E,Standard_True));
520           gp_Pnt pl = myLastPnt;
521           TopoDS_Edge ee;
522           if(thePreviousEdge.IsNull()) {
523             BRepLib_MakeEdge e(cc, pf , pl); 
524             ee = TopoDS::Edge(e.Shape());
525           }
526           else {
527             const TopoDS_Vertex& v1 = TopExp::LastVertex(thePreviousEdge,Standard_True);
528             BRepLib_MakeVertex v2(pl);
529             BRepLib_MakeEdge e(cc, v1, v2);
530             ee = TopoDS::Edge(e.Shape());
531           }
532           TopoDS_Shape aLocalShape = ee.Oriented(E.Orientation());
533           ee = TopoDS::Edge(aLocalShape);
534 //        ee = TopoDS::Edge(ee.Oriented(E.Orientation()));
535           BB.Add(w, ee);
536           myLFMap(E).Append(ee);
537           if(counter == 1) theFV = TopExp::FirstVertex(ee,Standard_True);
538           thePreviousEdge = ee;
539           counter++;
540           break;
541         }
542       }
543     }
544     
545     TopTools_ListIteratorOfListOfShape it(myListOfEdges);
546     Standard_Boolean FirstOK = Standard_False;
547     Standard_Boolean LastOK = Standard_False;
548     
549     gp_Pnt theLastPnt = myLastPnt;
550     Standard_Integer sens = 0;
551     TopoDS_Edge theEdge, theLEdge, theFEdge;
552     Standard_Integer counter1 = counter;
553     TopTools_ListOfShape NewListOfEdges;
554     NewListOfEdges.Clear();
555     while (!FirstOK) {
556       const TopoDS_Edge& edg = TopoDS::Edge(it.Value());
557       gp_Pnt fp, lp;
558       Standard_Real f, l;
559       Handle(Geom_Curve) ccc = BRep_Tool::Curve(edg, f, l);
560       Handle(Geom_TrimmedCurve) cc = new Geom_TrimmedCurve(ccc, f, l);
561       if ( edg.Orientation() == TopAbs_REVERSED) cc->Reverse();
562
563       fp = cc->Value(cc->FirstParameter());
564       lp = cc->Value(cc->LastParameter());
565       Standard_Real dist = fp.Distance(theLastPnt);
566       if(dist <= myTol) {
567         sens = 1;
568         LastOK = Standard_True;
569       }
570       else {
571         dist = lp.Distance(theLastPnt);
572         if(dist <= myTol) {
573           sens = 2;
574           LastOK = Standard_True;
575           cc->Reverse();
576         }
577       }
578       Standard_Integer FirstFlag = 0;
579       if(sens==1 && lp.Distance(myFirstPnt) <= myTol) {
580         FirstOK = Standard_True;
581         FirstFlag = 1;
582       }
583       else if(sens==2 && fp.Distance(myFirstPnt) <= myTol) {
584         FirstOK = Standard_True;
585         FirstFlag = 2;
586       }
587       
588       if (LastOK) {
589         TopoDS_Edge eeee;
590         Standard_Real fpar = cc->FirstParameter();
591         Standard_Real lpar = cc->LastParameter();
592         if(!FirstOK) {
593           if(thePreviousEdge.IsNull()) {
594             BRepLib_MakeEdge e(cc, fpar, lpar);
595             eeee = TopoDS::Edge(e.Shape());
596           }
597           else {
598             const TopoDS_Vertex& v1 = TopExp::LastVertex(thePreviousEdge,Standard_True);
599             BB.UpdateVertex(v1, dist);
600             BRepLib_MakeVertex v2(cc->Value(lpar));
601             TopoDS_Vertex nv=v2.Vertex();
602             BRepLib_MakeEdge e(cc, v1, nv);
603             eeee = TopoDS::Edge(e.Shape());
604           }
605         }
606         else {
607           if(thePreviousEdge.IsNull()) {
608             BRepLib_MakeVertex v1(cc->Value(fpar)); 
609             BRepLib_MakeEdge e(cc, v1, theFV);
610             eeee = TopoDS::Edge(e.Shape());
611           }
612           else {
613             const TopoDS_Vertex& v1 = TopExp::LastVertex(thePreviousEdge,Standard_True);
614             BRepLib_MakeEdge e(cc, v1, theFV);
615             eeee = TopoDS::Edge(e.Shape());
616           }
617         }
618
619         thePreviousEdge = eeee;
620         BB.Add(w, eeee);
621         if(counter == 1) theFV = TopExp::FirstVertex(eeee,Standard_True);
622         counter1++;
623         NewListOfEdges.Append(edg);
624         theEdge = eeee;
625 #ifdef DEB
626         Standard_Real dist1 = 
627 #endif
628           theLastPnt.Distance(myLastPnt);
629
630         if(dist <= myTol) 
631           theFEdge = edg;
632         theLastPnt = BRep_Tool::Pnt(TopExp::LastVertex(theEdge,Standard_True));
633       }
634
635       if(FirstFlag == 1) {
636         theLEdge = edg;
637       }
638       else if(FirstFlag == 2) {
639         theLEdge = theEdge;
640       }
641
642       if(LastOK) {
643         myListOfEdges.Remove(it);
644         it.Initialize(myListOfEdges);
645         LastOK = Standard_False;
646       }
647       else if(it.More()) it.Next();
648       else {
649         Sliding = Standard_False;
650         break;
651       } 
652       sens = 0;
653     }
654
655
656     TopTools_DataMapOfShapeListOfShape SlidMap;
657     SlidMap.Clear();
658     
659     if(Sliding && counter1 > counter) {
660       TopTools_ListIteratorOfListOfShape it;
661       TopTools_DataMapIteratorOfDataMapOfShapeListOfShape itm;
662       TopExp_Explorer EX2(w, TopAbs_EDGE);
663       Standard_Integer ii = 0;
664       for(; EX2.More(); EX2.Next()) {
665         const TopoDS_Edge& E = TopoDS::Edge(EX2.Current());
666         ii++;   
667         if(ii >= counter && ii <= counter1) {
668           it.Initialize(NewListOfEdges);
669           Standard_Integer jj = 0;
670           for(; it.More(); it.Next()) {
671             const TopoDS_Edge& e2 = TopoDS::Edge(it.Value());
672             jj++;
673             if(jj== (ii - counter +1)) {          
674               itm.Initialize(mySlface);
675               for(; itm.More(); itm.Next()) {
676                 const TopoDS_Face& fac = TopoDS::Face(itm.Key());
677                 const TopTools_ListOfShape& ledg = itm.Value();
678                 TopTools_ListIteratorOfListOfShape itedg(ledg);
679                 //Standard_Integer iiii = 0;
680                 for(; itedg.More(); itedg.Next()) {
681                   const TopoDS_Edge& e1 = TopoDS::Edge(itedg.Value());
682                   if(e1.IsSame(e2)){
683                     if(!SlidMap.IsBound(fac)) {
684                       TopTools_ListOfShape thelist2;
685                       SlidMap.Bind(fac, thelist2);
686                     }
687                     SlidMap(fac).Append(E);
688                   }
689                 }               
690               }
691             }
692           } 
693         }
694       }
695     }
696
697     mySlface.Clear();
698     mySlface = SlidMap;
699   }
700
701 // ---Arguments of LocOpe_LinearForm : arguments of the prism sliding
702   if(Sliding) {
703     TopoDS_Face F;
704     BB.MakeFace(F, myPln, myTol);
705     BB.Add(F, w);
706 //    BRepLib_MakeFace F(myPln->Pln(),w, Standard_True);
707     mySkface = F;
708     myPbase  = mySkface;
709     mySUntil.Nullify();
710   }
711   
712
713 // ---Case without sliding : construction of the profile face   
714   if(!Sliding) {
715 #ifdef DEB
716     if (trc) {
717       if (Modify) cout << " Sliding failure" << endl;
718       cout << " no Sliding" << endl;
719     }
720 #endif
721     TopoDS_Face Prof;
722     Standard_Boolean ProfileOK;
723     ProfileOK = NoSlidingProfile(Prof,RevolRib,myTol,Concavite,myPln,
724                                  bnd,BndFace,CheckPnt,
725                                  FirstFace,LastFace,FirstVertex,LastVertex,
726                                  FirstEdge,LastEdge,OnFirstFace,OnLastFace);
727     
728     if (!ProfileOK) {
729 #ifdef DEB
730       cout << "Not computable" << endl;
731       if (trc) cout << " Face profile not computable" << endl;
732 #endif
733       myStatusError = BRepFeat_NoFaceProf;
734       NotDone();
735       return;
736     }
737
738
739 // ---Propagation on faces of the initial shape
740 // to find the faces concerned by the rib
741     Standard_Boolean falseside = Standard_True;
742     Propagate(SliList, Prof, myFirstPnt, myLastPnt, falseside);
743 // Control if there is everything required to have the material at the proper side
744     if(falseside == Standard_False) {
745 #ifdef DEB
746       cout << "Verify plane and wire orientation" << endl;
747 #endif
748       myStatusError = BRepFeat_FalseSide;
749       NotDone();
750       return;
751     }
752
753     mySlface.Clear();
754
755     TopTools_ListIteratorOfListOfShape it;
756     it.Initialize(SliList);
757     
758     TopoDS_Shape comp;
759     
760     BRep_Builder BB;
761     BB.MakeShell(TopoDS::Shell(comp));
762     
763     for(; it.More(); it.Next()) {
764       BB.Add(comp, it.Value());
765     }
766     
767     mySUntil = comp;
768
769     mySkface = Prof;
770     myPbase  = Prof;
771   }
772
773   mySliding = Sliding;
774
775   TopExp_Explorer exp;
776   for (exp.Init(mySbase,TopAbs_FACE);exp.More();exp.Next()) {
777     TopTools_ListOfShape thelist3;
778     myMap.Bind(exp.Current(), thelist3);
779     myMap(exp.Current()).Append(exp.Current());
780   }
781 }
782
783
784 //=======================================================================
785 //function : Add
786 //purpose  : add des element de collage
787 //=======================================================================
788
789 void BRepFeat_MakeLinearForm::Add(const TopoDS_Edge& E,
790                                   const TopoDS_Face& F)
791 {
792 #ifdef DEB
793   Standard_Boolean trc = BRepFeat_GettraceFEAT();
794   if (trc) cout << "BRepFeat_MakeLinearForm::Add" << endl;
795 #endif
796   if(mySlface.IsEmpty()) {
797     TopExp_Explorer exp;
798     for (exp.Init(mySbase,TopAbs_FACE);exp.More();exp.Next()) {
799       if (exp.Current().IsSame(F)) {
800         break;
801       }
802     }
803     if (!exp.More()) {
804       Standard_ConstructionError::Raise();
805     }
806   
807     if (!mySlface.IsBound(F)) {
808       TopTools_ListOfShape thelist;
809       mySlface.Bind(F, thelist);
810     }
811     TopTools_ListIteratorOfListOfShape itl(mySlface(F));
812     for (; itl.More();itl.Next()) {
813       if (itl.Value().IsSame(E)) {
814         break;
815       }
816     }
817     if (!itl.More()) {
818       mySlface(F).Append(E);
819     }
820   } 
821 }
822
823   
824
825 //=======================================================================
826 //function : Perform
827 //purpose  : construction of rib from a profile and the initial shape
828 //=======================================================================
829
830 void BRepFeat_MakeLinearForm::Perform()
831 {
832 #ifdef DEB
833   Standard_Boolean trc = BRepFeat_GettraceFEAT();
834   if (trc) cout << "BRepFeat_MakeLinearForm::Perform()" << endl;
835 #endif
836   if(mySbase.IsNull() || mySkface.IsNull() || myPbase.IsNull()) {
837 #ifdef DEB
838     if (trc) cout << " Fields not initialized" << endl;
839 #endif
840     myStatusError = BRepFeat_NotInitialized;
841     NotDone();
842     return;
843   }
844
845   gp_Vec nulldir(0, 0, 0);
846
847   Standard_Real Length = myDir.Magnitude() +  myDir1.Magnitude();
848
849   myGluedF.Clear();
850  
851   if(!mySUntil.IsNull()) 
852     myPerfSelection = BRepFeat_SelectionU;
853   else 
854     myPerfSelection = BRepFeat_NoSelection;
855
856   gp_Dir dir(myDir);
857   gp_Vec V = Length*dir;
858
859   LocOpe_LinearForm theForm;
860
861   if(myDir1.IsEqual(nulldir, Precision::Confusion(), Precision::Confusion())) 
862     theForm.Perform(myPbase, V, myFirstPnt, myLastPnt);
863   else 
864     theForm.Perform(myPbase, V, myDir1, myFirstPnt, myLastPnt);
865
866   TopoDS_Shape VraiForm = theForm.Shape();   // primitive of the rib
867
868   myFacesForDraft.Append(theForm.FirstShape());
869   myFacesForDraft.Append(theForm.LastShape());
870   MajMap(myPbase,theForm,myMap,myFShape,myLShape);   // management of descendants
871
872   TopExp_Explorer exx(myPbase, TopAbs_EDGE);
873   for(; exx.More(); exx.Next()) {
874     const TopoDS_Edge& e = TopoDS::Edge(exx.Current());
875     if(!myMap.IsBound(e)) {
876 #ifdef DEB
877       if (trc) cout << " Sliding face not in Base shape" << endl;
878 #endif
879       myStatusError = BRepFeat_IncSlidFace;
880       NotDone();
881       return;
882     }
883   }
884
885   myGShape = VraiForm;
886   SetGluedFaces(mySlface, theForm, myGluedF);  // management of sliding faces  
887
888   if(!myGluedF.IsEmpty() && !mySUntil.IsNull()) {
889 #ifdef DEB
890     cout << "The case is not computable" << endl;
891     if (trc) cout << " Glued faces not empty and Until shape not null" << endl;
892 #endif
893     myStatusError = BRepFeat_InvShape;
894     NotDone();
895     return;
896   }
897
898   LFPerform();
899
900 /*
901
902   TopExp_Explorer expr(mySbase, TopAbs_FACE);
903   char nom1[20], nom2[20];
904   Standard_Integer ii = 0;
905   for(; expr.More(); expr.Next()) {
906     ii++;
907     sprintf(nom1, "faceinitial_%d", ii);
908     DBRep::Set(nom1, expr.Current());
909     Standard_Integer jj = 0;
910     const TopTools_ListOfShape& list = Modified(expr.Current());
911     TopTools_ListIteratorOfListOfShape ite(list);
912     for(; ite.More(); ite.Next()) {
913       jj++;
914       sprintf(nom2, "facemodifie_%d_%d", ii, jj);
915       DBRep::Set(nom2, ite.Value());
916     }
917   }
918
919   expr.Init(myWire, TopAbs_EDGE);
920   ii=0;
921   for(; expr.More(); expr.Next()) {
922     ii++;
923     sprintf(nom1, "edgeinitial_%d", ii);
924     DBRep::Set(nom1, expr.Current());
925     Standard_Integer jj = 0;
926     const TopTools_ListOfShape& genf = Generated(expr.Current());
927     TopTools_ListIteratorOfListOfShape ite(genf);
928     for(; ite.More(); ite.Next()) {
929       jj++;
930       sprintf(nom2, "egdegeneree_%d_%d", ii, jj);
931       DBRep::Set(nom2, ite.Value());
932     }
933   }
934 */
935 }
936
937 //=======================================================================
938 //function : Propagate
939 //purpose  : propagation on faces of the initial shape, find 
940 // faces concerned by the rib
941 //=======================================================================
942   Standard_Boolean BRepFeat_MakeLinearForm::Propagate(TopTools_ListOfShape& SliList,
943                                                       const TopoDS_Face& fac,
944                                                       const gp_Pnt& Firstpnt, 
945                                                       const gp_Pnt& Lastpnt, 
946                                                       Standard_Boolean& falseside)
947 {
948 #ifdef DEB
949   Standard_Boolean trc = BRepFeat_GettraceFEATRIB();
950   if (trc) cout << "BRepFeat_MakeLinearForm::Propagate" << endl;
951 #endif
952   gp_Pnt Firstpoint = Firstpnt;
953   gp_Pnt Lastpoint = Lastpnt;
954
955   Standard_Boolean result = Standard_True;
956   TopoDS_Face CurrentFace, saveFace;
957   CurrentFace = TopoDS::Face(SliList.First());
958   saveFace = CurrentFace;
959
960   Standard_Boolean LastOK = Standard_False, FirstOK= Standard_False;
961   Standard_Boolean v1OK = Standard_False, v2OK= Standard_False;
962   TopoDS_Vertex v1, v2, v3, v4, ve1, ve2;
963
964   //modified by NIZNHY-PKV Fri Mar 22 16:50:24 2002 f
965   //BRepAlgo_Section sect (fac, CurrentFace, Standard_False);
966   BRepAlgoAPI_Section sect (fac, CurrentFace, Standard_False);
967   //modified by NIZNHY-PKV Fri Mar 22 16:50:32 2002 t
968
969   sect.Approximation(Standard_True);
970   sect.Build();
971
972   TopExp_Explorer Ex;
973   TopoDS_Edge eb, ec;
974   gp_Pnt p1, p2;
975 #ifndef DEB
976   Standard_Real t1 = 0., t2 = 0.;
977 #else
978   Standard_Real t1, t2;
979 #endif
980   Standard_Boolean c1f, c2f, c1l, c2l;
981
982   for (Ex.Init(sect.Shape(), TopAbs_EDGE); Ex.More(); Ex.Next()) {
983     ec = TopoDS::Edge(Ex.Current());
984     v1 = TopExp::FirstVertex(ec,Standard_True); 
985     v2 = TopExp::LastVertex(ec,Standard_True); 
986     p1 = BRep_Tool::Pnt(v1);
987     p2 = BRep_Tool::Pnt(v2);
988     t1 = BRep_Tool::Tolerance(v1);
989     t2 = BRep_Tool::Tolerance(v2);
990     c1f = p1.Distance(Firstpoint)<=t1;
991     c2f = p2.Distance(Firstpoint)<=t2;
992     c1l = p1.Distance(Lastpoint)<=t1;
993     c2l = p2.Distance(Lastpoint)<=t2;
994     if (c1f || c2f || c1l|| c2l) {
995       eb = ec;
996       if (c1f || c1l) v1OK=Standard_True;
997       if (c2f || c2l) v2OK=Standard_True;
998       if (c1f || c2f) FirstOK=Standard_True;
999       if (c1l || c2l) LastOK=Standard_True;
1000       break;
1001     }
1002   }
1003
1004   if(eb.IsNull()) {
1005     falseside = Standard_False;
1006     return Standard_False;
1007   }
1008   TopTools_ListOfShape thelist;
1009   mySlface.Bind(CurrentFace, thelist);
1010   mySlface(CurrentFace).Append(eb);
1011     
1012   myListOfEdges.Clear();
1013   myListOfEdges.Append(eb);
1014     
1015   // two points are on the same face.
1016   if(LastOK && FirstOK) {
1017     return result;
1018   }
1019   
1020   TopTools_IndexedDataMapOfShapeListOfShape mapedges;
1021   TopExp::MapShapesAndAncestors(mySbase, TopAbs_EDGE, TopAbs_FACE, mapedges);
1022   TopExp_Explorer ex;
1023   TopoDS_Edge FirstEdge;
1024   BRep_Builder BB;
1025
1026   TopoDS_Vertex Vprevious;
1027   gp_Pnt ptprev;
1028   Standard_Real tvp, dp;
1029
1030   while (!(LastOK && FirstOK)) {
1031     if (v1OK) {
1032       Vprevious=v2;
1033       ptprev=p2;
1034       tvp=t2;
1035     }
1036     else {
1037       Vprevious=v1;
1038       ptprev=p1;
1039       tvp=t1;
1040     }
1041     
1042     // find edge connected to v1 or v2:
1043     for (ex.Init(CurrentFace, TopAbs_EDGE); ex.More(); ex.Next()) {
1044       const TopoDS_Edge& rfe = TopoDS::Edge(ex.Current());
1045
1046       BRepExtrema_ExtPC projF(Vprevious, rfe);
1047
1048       if(projF.IsDone() && projF.NbExt() >=1) {
1049         Standard_Real dist2min = RealLast();
1050         Standard_Integer index = 0;
1051         for (Standard_Integer sol =1 ; sol <= projF.NbExt(); sol++) {
1052           if (projF.SquareDistance(sol) <= dist2min) {
1053             index = sol;
1054             dist2min = projF.SquareDistance(sol);
1055           }
1056         }
1057         if (index != 0) {
1058           if (dist2min <= BRep_Tool::Tolerance(rfe) * BRep_Tool::Tolerance(rfe)) {
1059             FirstEdge = rfe;
1060             // If the edge is not perpendicular to the plane of the rib
1061             // it is required to set Sliding(result) to false.
1062             if (result) {
1063               result=Standard_False;
1064               ve1 = TopExp::FirstVertex(rfe,Standard_True);
1065               ve2 = TopExp::LastVertex(rfe,Standard_True);
1066               BRepExtrema_ExtPF perp(ve1, fac);
1067               if (perp.IsDone()) {
1068                 gp_Pnt pe1=perp.Point(1);
1069                 perp.Perform(ve2, fac);
1070                 if (perp.IsDone()) {
1071                   gp_Pnt pe2=perp.Point(1);
1072                   if (pe1.Distance(pe2)<=BRep_Tool::Tolerance(rfe)) 
1073                     result=Standard_True;
1074                 }
1075               }
1076             }
1077             break;
1078           }
1079         }
1080       }
1081     }
1082     
1083     const TopTools_ListOfShape& L = mapedges.FindFromKey(FirstEdge);
1084     TopTools_ListIteratorOfListOfShape It(L);
1085
1086     for (; It.More(); It.Next()) {
1087       const TopoDS_Face& FF = TopoDS::Face(It.Value());
1088       if (!FF.IsSame(CurrentFace)) {
1089         CurrentFace = FF;
1090         break;
1091       }
1092     }
1093
1094     //modified by NIZNHY-PKV Fri Mar 22 16:50:53 2002 f
1095     //BRepAlgo_Section sectf (fac, CurrentFace, Standard_False);
1096     BRepAlgoAPI_Section sectf (fac, CurrentFace, Standard_False);
1097     //modified by NIZNHY-PKV Fri Mar 22 16:51:03 2002 t
1098     sectf.Approximation(Standard_True);
1099     sectf.Build();
1100
1101     TopoDS_Edge edg1;
1102     for (Ex.Init(sectf.Shape(), TopAbs_EDGE); Ex.More(); Ex.Next()) {
1103       edg1 = TopoDS::Edge(Ex.Current());
1104       v1=TopExp::FirstVertex(edg1,Standard_True);
1105       v2=TopExp::LastVertex(edg1,Standard_True);
1106       t1 = BRep_Tool::Tolerance(v1);
1107       t2 = BRep_Tool::Tolerance(v2);
1108       p1 = BRep_Tool::Pnt(v1);
1109       p2 = BRep_Tool::Pnt(v2);
1110       v1OK = p1.Distance(ptprev)<=t1;
1111       v2OK = p2.Distance(ptprev)<=t2;
1112       if (v1OK || v2OK) break;
1113     }    
1114     
1115     if (v1OK) {
1116       if (!FirstOK) {
1117         dp = p2.Distance(Firstpoint);
1118         if(dp <= 2*t2) {
1119           FirstOK = Standard_True;
1120           BB.UpdateVertex(v2, dp);
1121         }
1122       }
1123       if (!LastOK) {
1124         dp = p2.Distance(Lastpoint);
1125         if(dp <= 2*t2) {
1126           LastOK = Standard_True;
1127           BB.UpdateVertex(v2, dp);
1128         }
1129       }
1130     }
1131     else if (v2OK) {
1132       if (!FirstOK) {
1133         dp = p1.Distance(Firstpoint);
1134         if(dp <= 2*t1) {
1135           FirstOK = Standard_True;
1136           BB.UpdateVertex(v1, dp);
1137         }
1138       }
1139       if (!LastOK) {
1140         dp = p1.Distance(Lastpoint);
1141         if(dp <= 2*t1) {
1142           LastOK = Standard_True;
1143           BB.UpdateVertex(v1, dp);
1144         }
1145       }
1146     }
1147     else {
1148       // end by chaining the section
1149       return Standard_False;
1150     }
1151 // #ifdef DEB
1152 //     Standard_Boolean isnb=
1153 // #endif
1154     TopTools_ListOfShape thelist1;
1155 #ifdef DEB
1156      Standard_Boolean isnb=
1157 #endif
1158     mySlface.Bind(CurrentFace, thelist1);
1159     mySlface(CurrentFace).Append(edg1);
1160     myListOfEdges.Append(edg1);
1161   }
1162
1163   return result;
1164   
1165 }
1166
1167 //=======================================================================
1168 //function : MajMap
1169 //purpose  : management of descendants
1170 //=======================================================================
1171
1172 static void MajMap(const TopoDS_Shape& theB,
1173                    const LocOpe_LinearForm& theP,
1174                    TopTools_DataMapOfShapeListOfShape& theMap, // myMap
1175                    TopoDS_Shape& theFShape,  // myFShape
1176                    TopoDS_Shape& theLShape) // myLShape
1177 {
1178   TopExp_Explorer exp(theP.FirstShape(),TopAbs_WIRE);
1179   if (exp.More()) {
1180     theFShape = exp.Current();
1181     TopTools_ListOfShape thelist;
1182     theMap.Bind(theFShape, thelist);
1183     for (exp.Init(theP.FirstShape(),TopAbs_FACE);exp.More();exp.Next()) {
1184       theMap(theFShape).Append(exp.Current());
1185     }
1186   }
1187   
1188   exp.Init(theP.LastShape(),TopAbs_WIRE);
1189   if (exp.More()) {
1190     theLShape = exp.Current();
1191     TopTools_ListOfShape thelist1;
1192     theMap.Bind(theLShape, thelist1);
1193     for (exp.Init(theP.LastShape(),TopAbs_FACE);exp.More();exp.Next()) {
1194       theMap(theLShape).Append(exp.Current());
1195     }
1196   }
1197   
1198   for (exp.Init(theB,TopAbs_EDGE); exp.More(); exp.Next()) {
1199     if (!theMap.IsBound(exp.Current())) {
1200 #ifdef DEB
1201       const TopoDS_Edge& e = 
1202 #endif
1203       TopoDS::Edge(exp.Current());
1204       TopTools_ListOfShape thelist2; 
1205       theMap.Bind(exp.Current(), thelist2);
1206       theMap(exp.Current()) = theP.Shapes(exp.Current());
1207     }
1208   }
1209 }
1210
1211 //=======================================================================
1212 //function : SetGluedFaces
1213 //purpose  : management of faces of gluing
1214 //=======================================================================
1215
1216 static void SetGluedFaces(const TopTools_DataMapOfShapeListOfShape& theSlmap,
1217                           LocOpe_LinearForm& thePrism,
1218                           TopTools_DataMapOfShapeShape& theMap)
1219 {
1220   // Slidings
1221   TopTools_DataMapIteratorOfDataMapOfShapeListOfShape itm(theSlmap);
1222   if(!theSlmap.IsEmpty()) {
1223     for (; itm.More(); itm.Next()) {
1224       const TopoDS_Face& fac = TopoDS::Face(itm.Key());
1225       const TopTools_ListOfShape& ledg = itm.Value();
1226       TopTools_ListIteratorOfListOfShape it;
1227       for (it.Initialize(ledg); it.More(); it.Next()) {
1228         const TopTools_ListOfShape& gfac = thePrism.Shapes(it.Value());
1229         if (gfac.Extent() != 1) {
1230 #ifdef DEB
1231           cout << "Pb SetGluedFace" << endl;
1232 #endif
1233         }
1234         theMap.Bind(gfac.First(),fac);
1235       }
1236     }
1237   }
1238 }
1239