Commit | Line | Data |
---|---|---|
b311480e | 1 | // Created on: 1995-07-18 |
2 | // Created by: Joelle CHAUVET | |
3 | // Copyright (c) 1995-1999 Matra Datavision | |
973c2be1 | 4 | // Copyright (c) 1999-2014 OPEN CASCADE SAS |
b311480e | 5 | // |
973c2be1 | 6 | // This file is part of Open CASCADE Technology software library. |
b311480e | 7 | // |
d5f74e42 | 8 | // This library is free software; you can redistribute it and/or modify it under |
9 | // the terms of the GNU Lesser General Public License version 2.1 as published | |
973c2be1 | 10 | // by the Free Software Foundation, with special exception defined in the file |
11 | // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT | |
12 | // distribution for complete text of the license and disclaimer of any warranty. | |
b311480e | 13 | // |
973c2be1 | 14 | // Alternatively, this file may be used under the terms of Open CASCADE |
15 | // commercial license or contractual agreement. | |
b311480e | 16 | |
7fd59977 | 17 | // Modified: Mon Jan 12 10:50:10 1998 |
7fd59977 | 18 | // gestion automatique de l'origine et de l'orientation |
19 | // avec la methode ArrangeWires | |
20 | // Modified: Mon Jan 19 10:11:56 1998 | |
7fd59977 | 21 | // traitement des cas particuliers cylindre, cone, plan |
22 | // (methodes DetectKPart et CreateKPart) | |
23 | // Modified: Mon Feb 23 09:28:46 1998 | |
7fd59977 | 24 | // traitement des sections avec nombre d'elements different |
25 | // + quelques ameliorations pour les cas particuliers | |
26 | // + cas de la derniere section ponctuelle | |
27 | // Modified: Mon Apr 6 15:47:44 1998 | |
7fd59977 | 28 | // traitement des cas particuliers deplace dans BRepFill |
29 | // Modified: Thu Apr 30 15:24:17 1998 | |
7fd59977 | 30 | // separation sections fermees / sections ouvertes + debug |
31 | // Modified: Fri Jul 10 11:23:35 1998 | |
7fd59977 | 32 | // surface de CreateSmoothed par concatenation,approximation |
33 | // et segmentation (PRO13924, CTS21295) | |
34 | // Modified: Tue Jul 21 16:48:35 1998 | |
7fd59977 | 35 | // pb de ratio (BUC60281) |
36 | // Modified: Thu Jul 23 11:38:36 1998 | |
7fd59977 | 37 | // sections bouclantes |
38 | // Modified: Fri Aug 28 10:13:44 1998 | |
7fd59977 | 39 | // traitement des sections ponctuelles |
40 | // dans l'historique (cf. loft06 et loft09) | |
41 | // et dans le cas des solides | |
42 | // Modified: Tue Nov 3 10:06:15 1998 | |
7fd59977 | 43 | // utilisation de BRepFill_CompatibleWires |
b311480e | 44 | |
42cf5bc1 | 45 | #include <BRep_Builder.hxx> |
46 | #include <BRep_Tool.hxx> | |
47 | #include <BRepBuilderAPI_FindPlane.hxx> | |
48 | #include <BRepBuilderAPI_MakeFace.hxx> | |
49 | #include <BRepClass3d_SolidClassifier.hxx> | |
50 | #include <BRepFill_CompatibleWires.hxx> | |
51 | #include <BRepFill_Generator.hxx> | |
52 | #include <BRepLib.hxx> | |
53 | #include <BRepOffsetAPI_ThruSections.hxx> | |
54 | #include <BRepTools_WireExplorer.hxx> | |
55 | #include <BSplCLib.hxx> | |
56 | #include <Geom2d_Line.hxx> | |
57 | #include <Geom_BezierCurve.hxx> | |
58 | #include <Geom_BSplineCurve.hxx> | |
59 | #include <Geom_BSplineSurface.hxx> | |
60 | #include <Geom_Conic.hxx> | |
7fd59977 | 61 | #include <Geom_Curve.hxx> |
c04c30b3 | 62 | #include <Geom_Plane.hxx> |
7fd59977 | 63 | #include <Geom_TrimmedCurve.hxx> |
42cf5bc1 | 64 | #include <GeomAbs_Shape.hxx> |
7fd59977 | 65 | #include <GeomConvert.hxx> |
66 | #include <GeomConvert_ApproxCurve.hxx> | |
42cf5bc1 | 67 | #include <GeomConvert_CompCurveToBSplineCurve.hxx> |
68 | #include <GeomFill_AppSurf.hxx> | |
69 | #include <GeomFill_Line.hxx> | |
70 | #include <GeomFill_SectionGenerator.hxx> | |
71 | #include <gp_Dir2d.hxx> | |
72 | #include <gp_Pnt.hxx> | |
73 | #include <gp_Pnt2d.hxx> | |
74 | #include <Precision.hxx> | |
75 | #include <Standard_DomainError.hxx> | |
76 | #include <Standard_NullObject.hxx> | |
77 | #include <TColgp_Array1OfPnt.hxx> | |
7fd59977 | 78 | #include <TopAbs.hxx> |
42cf5bc1 | 79 | #include <TopExp.hxx> |
80 | #include <TopLoc_Location.hxx> | |
7fd59977 | 81 | #include <TopoDS.hxx> |
7fd59977 | 82 | #include <TopoDS_Edge.hxx> |
42cf5bc1 | 83 | #include <TopoDS_Face.hxx> |
84 | #include <TopoDS_Iterator.hxx> | |
85 | #include <TopoDS_Shape.hxx> | |
86 | #include <TopoDS_Solid.hxx> | |
7fd59977 | 87 | #include <TopoDS_Vertex.hxx> |
88 | #include <TopoDS_Wire.hxx> | |
7fd59977 | 89 | #include <TopTools_Array1OfShape.hxx> |
7fd59977 | 90 | #include <TopTools_IndexedDataMapOfShapeListOfShape.hxx> |
42cf5bc1 | 91 | #include <TopTools_ListIteratorOfListOfShape.hxx> |
50258e77 | 92 | #include <BRepAdaptor_Surface.hxx> |
7fd59977 | 93 | |
aadab519 | 94 | //======================================================================= |
95 | //function : PreciseUpar | |
96 | //purpose : pins the u-parameter of surface close to U-knot | |
97 | // to this U-knot | |
98 | //======================================================================= | |
aadab519 | 99 | static Standard_Real PreciseUpar(const Standard_Real anUpar, |
e01907f1 | 100 | const Handle(Geom_BSplineSurface)& aSurface) |
aadab519 | 101 | { |
102 | Standard_Real Tol = Precision::PConfusion(); | |
103 | Standard_Integer i1, i2; | |
7fd59977 | 104 | |
aadab519 | 105 | aSurface->LocateU(anUpar, Tol, i1, i2); |
106 | Standard_Real U1 = aSurface->UKnot(i1); | |
107 | Standard_Real U2 = aSurface->UKnot(i2); | |
108 | ||
109 | Standard_Real NewU = anUpar; | |
110 | ||
111 | NewU = (anUpar - U1 < U2 - anUpar)? U1 : U2; | |
112 | return NewU; | |
113 | } | |
7fd59977 | 114 | |
115 | //======================================================================= | |
116 | //function : PerformPlan | |
0d969553 | 117 | //purpose : Construct a plane of filling if exists |
7fd59977 | 118 | //======================================================================= |
119 | ||
120 | static Standard_Boolean PerformPlan(const TopoDS_Wire& W, | |
e01907f1 | 121 | const Standard_Real presPln, |
122 | TopoDS_Face& theFace) | |
7fd59977 | 123 | { |
124 | Standard_Boolean isDegen = Standard_True; | |
125 | TopoDS_Iterator iter(W); | |
126 | for (; iter.More(); iter.Next()) | |
e01907f1 | 127 | { |
128 | const TopoDS_Edge& anEdge = TopoDS::Edge(iter.Value()); | |
129 | if (!BRep_Tool::Degenerated(anEdge)) | |
130 | isDegen = Standard_False; | |
131 | } | |
7fd59977 | 132 | if (isDegen) |
133 | return Standard_True; | |
134 | ||
135 | Standard_Boolean Ok = Standard_False; | |
136 | if (!W.IsNull()) { | |
137 | BRepBuilderAPI_FindPlane Searcher( W, presPln ); | |
138 | if (Searcher.Found()) | |
e01907f1 | 139 | { |
140 | theFace = BRepBuilderAPI_MakeFace(Searcher.Plane(), W); | |
141 | Ok = Standard_True; | |
142 | } | |
7fd59977 | 143 | else // try to find another surface |
e01907f1 | 144 | { |
145 | BRepBuilderAPI_MakeFace MF( W ); | |
146 | if (MF.IsDone()) | |
7fd59977 | 147 | { |
e01907f1 | 148 | theFace = MF.Face(); |
149 | Ok = Standard_True; | |
7fd59977 | 150 | } |
e01907f1 | 151 | } |
7fd59977 | 152 | } |
153 | ||
e01907f1 | 154 | return Ok; |
7fd59977 | 155 | } |
156 | ||
157 | //============================================================================= | |
158 | //function : IsSameOriented | |
159 | //purpose : Checks whether aFace is oriented to the same side as aShell or not | |
160 | //============================================================================= | |
161 | ||
162 | static Standard_Boolean IsSameOriented(const TopoDS_Shape& aFace, | |
e01907f1 | 163 | const TopoDS_Shape& aShell) |
7fd59977 | 164 | { |
165 | TopExp_Explorer Explo(aFace, TopAbs_EDGE); | |
166 | TopoDS_Shape anEdge = Explo.Current(); | |
167 | TopAbs_Orientation Or1 = anEdge.Orientation(); | |
168 | ||
169 | TopTools_IndexedDataMapOfShapeListOfShape EFmap; | |
170 | TopExp::MapShapesAndAncestors( aShell, TopAbs_EDGE, TopAbs_FACE, EFmap ); | |
171 | ||
172 | const TopoDS_Shape& AdjacentFace = EFmap.FindFromKey(anEdge).First(); | |
173 | TopoDS_Shape theEdge; | |
174 | for (Explo.Init(AdjacentFace, TopAbs_EDGE); Explo.More(); Explo.Next()) | |
e01907f1 | 175 | { |
176 | theEdge = Explo.Current(); | |
177 | if (theEdge.IsSame(anEdge)) | |
178 | break; | |
179 | } | |
7fd59977 | 180 | |
181 | TopAbs_Orientation Or2 = theEdge.Orientation(); | |
182 | if (Or1 == Or2) | |
183 | return Standard_False; | |
184 | return Standard_True; | |
185 | } | |
186 | ||
187 | //======================================================================= | |
188 | //function : MakeSolid | |
189 | //purpose : | |
190 | //======================================================================= | |
191 | ||
192 | static TopoDS_Solid MakeSolid(TopoDS_Shell& shell, const TopoDS_Wire& wire1, | |
e01907f1 | 193 | const TopoDS_Wire& wire2, const Standard_Real presPln, |
194 | TopoDS_Face& face1, TopoDS_Face& face2) | |
7fd59977 | 195 | { |
196 | if (shell.IsNull()) | |
9775fa61 | 197 | throw StdFail_NotDone("Thrusections is not build"); |
7fd59977 | 198 | Standard_Boolean B = shell.Closed(); |
199 | BRep_Builder BB; | |
200 | ||
201 | if (!B) | |
e01907f1 | 202 | { |
203 | // It is necessary to close the extremities | |
204 | B = PerformPlan(wire1, presPln, face1); | |
205 | if (B) { | |
206 | B = PerformPlan(wire2, presPln, face2); | |
7fd59977 | 207 | if (B) { |
e01907f1 | 208 | if (!face1.IsNull() && !IsSameOriented( face1, shell )) |
209 | face1.Reverse(); | |
210 | if (!face2.IsNull() && !IsSameOriented( face2, shell )) | |
211 | face2.Reverse(); | |
212 | ||
213 | if (!face1.IsNull()) | |
214 | BB.Add(shell, face1); | |
215 | if (!face2.IsNull()) | |
216 | BB.Add(shell, face2); | |
217 | ||
218 | shell.Closed(Standard_True); | |
7fd59977 | 219 | } |
220 | } | |
e01907f1 | 221 | } |
7fd59977 | 222 | |
223 | TopoDS_Solid solid; | |
224 | BB.MakeSolid(solid); | |
225 | BB.Add(solid, shell); | |
e01907f1 | 226 | |
7fd59977 | 227 | // verify the orientation the solid |
228 | BRepClass3d_SolidClassifier clas3d(solid); | |
229 | clas3d.PerformInfinitePoint(Precision::Confusion()); | |
230 | if (clas3d.State() == TopAbs_IN) { | |
231 | BB.MakeSolid(solid); | |
232 | TopoDS_Shape aLocalShape = shell.Reversed(); | |
233 | BB.Add(solid, TopoDS::Shell(aLocalShape)); | |
e01907f1 | 234 | // B.Add(solid, TopoDS::Shell(newShell.Reversed())); |
7fd59977 | 235 | } |
236 | ||
237 | solid.Closed(Standard_True); | |
238 | return solid; | |
239 | } | |
240 | ||
241 | ||
242 | //======================================================================= | |
243 | //function : BRepOffsetAPI_ThruSections | |
244 | //purpose : | |
245 | //======================================================================= | |
246 | ||
50258e77 | 247 | BRepOffsetAPI_ThruSections::BRepOffsetAPI_ThruSections(const Standard_Boolean isSolid, |
248 | const Standard_Boolean ruled, | |
249 | const Standard_Real pres3d): | |
250 | myNbEdgesInSection(0), | |
251 | myIsSolid(isSolid), myIsRuled(ruled), | |
252 | myPres3d(pres3d), | |
253 | myDegen1(Standard_False), myDegen2(Standard_False) | |
7fd59977 | 254 | { |
255 | myWCheck = Standard_True; | |
e01907f1 | 256 | //---------------------------- |
7fd59977 | 257 | myParamType = Approx_ChordLength; |
258 | myDegMax = 8; | |
259 | myContinuity = GeomAbs_C2; | |
260 | myCritWeights[0] = .4; | |
261 | myCritWeights[1] = .2; | |
262 | myCritWeights[2] = .4; | |
263 | myUseSmoothing = Standard_False; | |
264 | } | |
265 | ||
266 | ||
267 | //======================================================================= | |
268 | //function : Init | |
269 | //purpose : | |
270 | //======================================================================= | |
271 | ||
272 | void BRepOffsetAPI_ThruSections::Init(const Standard_Boolean isSolid, const Standard_Boolean ruled, | |
e01907f1 | 273 | const Standard_Real pres3d) |
7fd59977 | 274 | { |
275 | myIsSolid = isSolid; | |
276 | myIsRuled = ruled; | |
277 | myPres3d = pres3d; | |
278 | myWCheck = Standard_True; | |
e01907f1 | 279 | //---------------------------- |
7fd59977 | 280 | myParamType = Approx_ChordLength; |
281 | myDegMax = 6; | |
282 | myContinuity = GeomAbs_C2; | |
283 | myCritWeights[0] = .4; | |
284 | myCritWeights[1] = .2; | |
285 | myCritWeights[2] = .4; | |
286 | myUseSmoothing = Standard_False; | |
287 | ||
288 | } | |
289 | ||
290 | ||
291 | //======================================================================= | |
292 | //function : AddWire | |
293 | //purpose : | |
294 | //======================================================================= | |
295 | ||
296 | void BRepOffsetAPI_ThruSections::AddWire(const TopoDS_Wire& wire) | |
297 | { | |
298 | myWires.Append(wire); | |
299 | } | |
300 | ||
301 | //======================================================================= | |
302 | //function : AddVertex | |
303 | //purpose : | |
304 | //======================================================================= | |
305 | ||
306 | void BRepOffsetAPI_ThruSections::AddVertex(const TopoDS_Vertex& aVertex) | |
307 | { | |
308 | BRep_Builder BB; | |
309 | ||
310 | TopoDS_Edge DegEdge; | |
311 | BB.MakeEdge( DegEdge ); | |
312 | BB.Add( DegEdge, aVertex.Oriented(TopAbs_FORWARD) ); | |
313 | BB.Add( DegEdge, aVertex.Oriented(TopAbs_REVERSED) ); | |
314 | BB.Degenerated( DegEdge, Standard_True ); | |
7fd59977 | 315 | |
316 | TopoDS_Wire DegWire; | |
317 | BB.MakeWire( DegWire ); | |
318 | BB.Add( DegWire, DegEdge ); | |
319 | DegWire.Closed( Standard_True ); | |
320 | ||
321 | myWires.Append( DegWire ); | |
322 | } | |
323 | ||
324 | //======================================================================= | |
325 | //function : CheckCompatibility | |
326 | //purpose : | |
327 | //======================================================================= | |
328 | ||
329 | void BRepOffsetAPI_ThruSections::CheckCompatibility(const Standard_Boolean check) | |
330 | { | |
331 | myWCheck = check; | |
332 | } | |
333 | ||
334 | ||
335 | //======================================================================= | |
336 | //function : Build | |
337 | //purpose : | |
338 | //======================================================================= | |
339 | ||
340 | void BRepOffsetAPI_ThruSections::Build() | |
341 | { | |
342 | //Check set of section for right configuration of punctual sections | |
343 | Standard_Integer i; | |
344 | TopExp_Explorer explo; | |
345 | for (i = 2; i <= myWires.Length()-1; i++) | |
e01907f1 | 346 | { |
347 | Standard_Boolean wdeg = Standard_True; | |
348 | for (explo.Init(myWires(i), TopAbs_EDGE); explo.More(); explo.Next()) | |
7fd59977 | 349 | { |
e01907f1 | 350 | const TopoDS_Edge& anEdge = TopoDS::Edge(explo.Current()); |
351 | wdeg = wdeg && (BRep_Tool::Degenerated(anEdge)); | |
7fd59977 | 352 | } |
e01907f1 | 353 | if (wdeg) |
9775fa61 | 354 | throw Standard_Failure("Wrong usage of punctual sections"); |
e01907f1 | 355 | } |
7fd59977 | 356 | if (myWires.Length() <= 2) |
e01907f1 | 357 | { |
358 | Standard_Boolean wdeg = Standard_True; | |
359 | for (i = 1; i <= myWires.Length(); i++) | |
360 | for (explo.Init(myWires(i), TopAbs_EDGE); explo.More(); explo.Next()) | |
361 | { | |
362 | const TopoDS_Edge& anEdge = TopoDS::Edge(explo.Current()); | |
363 | wdeg = wdeg && (BRep_Tool::Degenerated(anEdge)); | |
364 | } | |
7fd59977 | 365 | if (wdeg) |
9775fa61 | 366 | throw Standard_Failure("Wrong usage of punctual sections"); |
e01907f1 | 367 | } |
7fd59977 | 368 | |
50258e77 | 369 | myNbEdgesInSection = 0; |
370 | ||
7fd59977 | 371 | if (myWCheck) { |
372 | // compute origin and orientation on wires to avoid twisted results | |
373 | // and update wires to have same number of edges | |
e01907f1 | 374 | |
0d969553 | 375 | // use BRepFill_CompatibleWires |
7fd59977 | 376 | TopTools_SequenceOfShape WorkingSections; |
377 | WorkingSections.Clear(); | |
378 | TopTools_DataMapOfShapeListOfShape WorkingMap; | |
379 | WorkingMap.Clear(); | |
e01907f1 | 380 | |
0d969553 | 381 | // Calculate the working sections |
7fd59977 | 382 | BRepFill_CompatibleWires Georges(myWires); |
383 | Georges.Perform(); | |
384 | if (Georges.IsDone()) { | |
385 | WorkingSections = Georges.Shape(); | |
386 | WorkingMap = Georges.Generated(); | |
50258e77 | 387 | myDegen1 = Georges.IsDegeneratedFirstSection(); |
388 | myDegen2 = Georges.IsDegeneratedLastSection(); | |
389 | //For each sub-edge of each section | |
390 | //we save its splits | |
391 | Standard_Integer IndFirstSec = 1; | |
392 | if (Georges.IsDegeneratedFirstSection()) | |
393 | IndFirstSec = 2; | |
394 | TopoDS_Shape aWorkingSection = WorkingSections(IndFirstSec); | |
395 | TopoDS_Iterator itw(aWorkingSection); | |
396 | for (; itw.More(); itw.Next()) | |
397 | myNbEdgesInSection++; | |
398 | for (Standard_Integer ii = 1; ii <= myWires.Length(); ii++) | |
399 | { | |
400 | TopExp_Explorer Explo(myWires(ii), TopAbs_EDGE); | |
401 | for (; Explo.More(); Explo.Next()) | |
402 | { | |
403 | const TopoDS_Edge& anEdge = TopoDS::Edge(Explo.Current()); | |
404 | Standard_Integer aSign = 1; | |
405 | TopoDS_Vertex Vfirst, Vlast; | |
406 | TopExp::Vertices(anEdge, Vfirst, Vlast); | |
407 | TopTools_ListOfShape aNewEdges = Georges.GeneratedShapes(anEdge); | |
408 | TColStd_ListOfInteger IList; | |
409 | aWorkingSection = WorkingSections(ii); | |
410 | Standard_Integer NbNewEdges = aNewEdges.Extent(); | |
411 | TopTools_ListIteratorOfListOfShape itl(aNewEdges); | |
412 | for (Standard_Integer kk = 1; itl.More(); itl.Next(),kk++) | |
413 | { | |
414 | const TopoDS_Edge& aNewEdge = TopoDS::Edge(itl.Value()); | |
415 | Standard_Integer inde = 1; | |
416 | for (itw.Initialize(aWorkingSection); itw.More(); itw.Next(),inde++) | |
417 | { | |
418 | const TopoDS_Shape& aWorkingEdge = itw.Value(); | |
419 | if (aWorkingEdge.IsSame(aNewEdge)) | |
420 | { | |
421 | aSign = (aWorkingEdge.Orientation() == TopAbs_FORWARD)? 1 : -1; | |
422 | break; | |
423 | } | |
424 | } | |
425 | IList.Append(inde); | |
426 | if (kk == 1 || kk == NbNewEdges) | |
427 | { | |
428 | //For each sub-vertex of each section | |
429 | //we save its index of new edge | |
430 | TopoDS_Vertex NewVfirst, NewVlast; | |
431 | TopExp::Vertices(aNewEdge, NewVfirst, NewVlast); | |
432 | if (NewVfirst.IsSame(Vfirst) && !myVertexIndex.IsBound(Vfirst)) | |
433 | myVertexIndex.Bind(Vfirst, aSign*inde); | |
434 | if (NewVlast.IsSame(Vlast) && !myVertexIndex.IsBound(Vlast)) | |
435 | myVertexIndex.Bind(Vlast, aSign*(-inde)); | |
436 | } | |
437 | } | |
438 | myEdgeNewIndices.Bind(anEdge, IList); | |
439 | } | |
440 | } | |
7fd59977 | 441 | } |
442 | myWires = WorkingSections; | |
50258e77 | 443 | } //if (myWCheck) |
444 | else //no check | |
445 | { | |
446 | TopoDS_Edge anEdge; | |
447 | for (Standard_Integer ii = 1; ii <= myWires.Length(); ii++) | |
448 | { | |
449 | TopExp_Explorer Explo(myWires(ii), TopAbs_EDGE); | |
450 | Standard_Integer inde = 1; | |
451 | for (; Explo.More(); Explo.Next(),inde++) | |
452 | { | |
453 | anEdge = TopoDS::Edge(Explo.Current()); | |
454 | TColStd_ListOfInteger IList; | |
455 | IList.Append(inde); | |
456 | myEdgeNewIndices.Bind(anEdge, IList); | |
457 | TopoDS_Vertex V1, V2; | |
458 | TopExp::Vertices(anEdge, V1, V2); | |
459 | if (!myVertexIndex.IsBound(V1)) | |
460 | myVertexIndex.Bind(V1, inde); | |
461 | if (!myVertexIndex.IsBound(V2)) | |
462 | myVertexIndex.Bind(V2, -inde); | |
463 | } | |
464 | inde--; | |
465 | if (inde > myNbEdgesInSection) | |
466 | myNbEdgesInSection = inde; | |
467 | if (inde == 1 && BRep_Tool::Degenerated(anEdge)) | |
468 | { | |
469 | if (ii == 1) | |
470 | myDegen1 = Standard_True; | |
471 | else | |
472 | myDegen2 = Standard_True; | |
473 | } | |
474 | } | |
7fd59977 | 475 | } |
476 | ||
a4bb1420 | 477 | try { |
478 | // Calculate the resulting shape | |
479 | if (myWires.Length() == 2 || myIsRuled) { | |
480 | // create a ruled shell | |
481 | CreateRuled(); | |
482 | } | |
483 | else { | |
484 | // create a smoothed shell | |
485 | CreateSmoothed(); | |
486 | } | |
7fd59977 | 487 | } |
a4bb1420 | 488 | catch (Standard_Failure) |
489 | { | |
490 | NotDone(); | |
491 | return; | |
7fd59977 | 492 | } |
493 | // Encode the Regularities | |
494 | BRepLib::EncodeRegularity(myShape); | |
7fd59977 | 495 | } |
496 | ||
497 | ||
498 | //======================================================================= | |
499 | //function : CreateRuled | |
500 | //purpose : | |
501 | //======================================================================= | |
502 | ||
503 | void BRepOffsetAPI_ThruSections::CreateRuled() | |
504 | { | |
505 | Standard_Integer nbSects = myWires.Length(); | |
506 | BRepFill_Generator aGene; | |
e01907f1 | 507 | // for (Standard_Integer i=1; i<=nbSects; i++) { |
7fd59977 | 508 | Standard_Integer i; |
509 | for (i=1; i<=nbSects; i++) { | |
510 | aGene.AddWire(TopoDS::Wire(myWires(i))); | |
511 | } | |
512 | aGene.Perform(); | |
513 | TopoDS_Shell shell = aGene.Shell(); | |
514 | ||
515 | if (myIsSolid) { | |
516 | ||
0d969553 | 517 | // check if the first wire is the same as the last |
7fd59977 | 518 | Standard_Boolean vClosed = (myWires(1).IsSame(myWires(nbSects))) ; |
519 | ||
520 | if (vClosed) { | |
521 | ||
522 | TopoDS_Solid solid; | |
523 | BRep_Builder B; | |
524 | B.MakeSolid(solid); | |
525 | B.Add(solid, shell); | |
e01907f1 | 526 | |
0d969553 | 527 | // verify the orientation of the solid |
7fd59977 | 528 | BRepClass3d_SolidClassifier clas3d(solid); |
529 | clas3d.PerformInfinitePoint(Precision::Confusion()); | |
530 | if (clas3d.State() == TopAbs_IN) { | |
e01907f1 | 531 | B.MakeSolid(solid); |
532 | TopoDS_Shape aLocalShape = shell.Reversed(); | |
533 | B.Add(solid, TopoDS::Shell(aLocalShape)); | |
534 | // B.Add(solid, TopoDS::Shell(shell.Reversed())); | |
7fd59977 | 535 | } |
536 | myShape = solid; | |
537 | ||
538 | } | |
539 | ||
540 | else { | |
541 | ||
542 | TopoDS_Wire wire1 = TopoDS::Wire(myWires.First()); | |
543 | TopoDS_Wire wire2 = TopoDS::Wire(myWires.Last()); | |
544 | myShape = MakeSolid(shell, wire1, wire2, myPres3d, myFirst, myLast); | |
545 | ||
546 | } | |
547 | ||
548 | Done(); | |
549 | } | |
550 | ||
551 | else { | |
552 | myShape = shell; | |
553 | Done(); | |
554 | } | |
555 | ||
556 | // history | |
557 | BRepTools_WireExplorer anExp1, anExp2; | |
558 | TopTools_IndexedDataMapOfShapeListOfShape M; | |
559 | TopExp::MapShapesAndAncestors(shell, TopAbs_EDGE, TopAbs_FACE, M); | |
560 | TopTools_ListIteratorOfListOfShape it; | |
561 | ||
562 | TopTools_IndexedDataMapOfShapeListOfShape MV; | |
563 | TopExp::MapShapesAndAncestors(shell, TopAbs_VERTEX, TopAbs_FACE, MV); | |
e01907f1 | 564 | |
7fd59977 | 565 | for (i=1; i<=nbSects-1; i++) { |
e01907f1 | 566 | |
7fd59977 | 567 | const TopoDS_Wire& wire1 = TopoDS::Wire(myWires(i)); |
568 | const TopoDS_Wire& wire2 = TopoDS::Wire(myWires(i+1)); | |
e01907f1 | 569 | |
7fd59977 | 570 | anExp1.Init(wire1); |
571 | anExp2.Init(wire2); | |
572 | ||
573 | Standard_Boolean tantque = anExp1.More() && anExp2.More(); | |
574 | ||
575 | while (tantque) { | |
576 | ||
577 | const TopoDS_Shape& edge1 = anExp1.Current(); | |
578 | const TopoDS_Shape& edge2 = anExp2.Current(); | |
579 | Standard_Boolean degen1 = BRep_Tool::Degenerated(anExp1.Current()); | |
580 | Standard_Boolean degen2 = BRep_Tool::Degenerated(anExp2.Current()); | |
e01907f1 | 581 | |
7fd59977 | 582 | TopTools_MapOfShape MapFaces; |
583 | if (degen2){ | |
e01907f1 | 584 | TopoDS_Vertex Vdegen = TopExp::FirstVertex(TopoDS::Edge(edge2)); |
585 | for (it.Initialize(MV.FindFromKey(Vdegen)); it.More(); it.Next()) { | |
586 | MapFaces.Add(it.Value()); | |
587 | } | |
7fd59977 | 588 | } |
589 | else { | |
e01907f1 | 590 | for (it.Initialize(M.FindFromKey(edge2)); it.More(); it.Next()) { |
591 | MapFaces.Add(it.Value()); | |
592 | } | |
7fd59977 | 593 | } |
e01907f1 | 594 | |
7fd59977 | 595 | if (degen1) { |
e01907f1 | 596 | TopoDS_Vertex Vdegen = TopExp::FirstVertex(TopoDS::Edge(edge1)); |
597 | for (it.Initialize(MV.FindFromKey(Vdegen)); it.More(); it.Next()) { | |
598 | const TopoDS_Shape& Face = it.Value(); | |
599 | if (MapFaces.Contains(Face)) { | |
50258e77 | 600 | myEdgeFace.Bind(edge1, Face); |
e01907f1 | 601 | break; |
602 | } | |
603 | } | |
7fd59977 | 604 | } |
605 | else { | |
e01907f1 | 606 | for (it.Initialize(M.FindFromKey(edge1)); it.More(); it.Next()) { |
607 | const TopoDS_Shape& Face = it.Value(); | |
608 | if (MapFaces.Contains(Face)) { | |
50258e77 | 609 | myEdgeFace.Bind(edge1, Face); |
e01907f1 | 610 | break; |
611 | } | |
612 | } | |
7fd59977 | 613 | } |
e01907f1 | 614 | |
7fd59977 | 615 | if (!degen1) anExp1.Next(); |
616 | if (!degen2) anExp2.Next(); | |
e01907f1 | 617 | |
7fd59977 | 618 | tantque = anExp1.More() && anExp2.More(); |
619 | if (degen1) tantque = anExp2.More(); | |
620 | if (degen2) tantque = anExp1.More(); | |
e01907f1 | 621 | |
7fd59977 | 622 | } |
e01907f1 | 623 | |
7fd59977 | 624 | } |
e01907f1 | 625 | |
7fd59977 | 626 | } |
627 | ||
628 | //======================================================================= | |
629 | //function : CreateSmoothed | |
630 | //purpose : | |
631 | //======================================================================= | |
632 | ||
633 | void BRepOffsetAPI_ThruSections::CreateSmoothed() | |
634 | { | |
635 | // initialisation | |
636 | Standard_Integer nbSects = myWires.Length(); | |
637 | BRepTools_WireExplorer anExp; | |
638 | ||
639 | Standard_Boolean w1Point = Standard_True; | |
0d969553 | 640 | // check if the first wire is punctual |
7fd59977 | 641 | for(anExp.Init(TopoDS::Wire(myWires(1))); anExp.More(); anExp.Next()) { |
642 | w1Point = w1Point && (BRep_Tool::Degenerated(anExp.Current())); | |
643 | } | |
644 | ||
645 | Standard_Boolean w2Point = Standard_True; | |
0d969553 | 646 | // check if the last wire is punctual |
7fd59977 | 647 | for(anExp.Init(TopoDS::Wire(myWires(nbSects))); anExp.More(); anExp.Next()) { |
648 | w2Point = w2Point && (BRep_Tool::Degenerated(anExp.Current())); | |
649 | } | |
650 | ||
651 | Standard_Boolean vClosed = Standard_False; | |
0d969553 | 652 | // check if the first wire is the same as last |
7fd59977 | 653 | if (myWires(1).IsSame(myWires(myWires.Length()))) vClosed = Standard_True; |
654 | ||
655 | // find the dimension | |
656 | Standard_Integer nbEdges=0; | |
657 | if (!w1Point) { | |
658 | for(anExp.Init(TopoDS::Wire(myWires(1))); anExp.More(); anExp.Next()) { | |
659 | nbEdges++; | |
660 | } | |
661 | } | |
662 | else { | |
663 | for(anExp.Init(TopoDS::Wire(myWires(2))); anExp.More(); anExp.Next()) { | |
664 | nbEdges++; | |
665 | } | |
666 | } | |
667 | ||
668 | // recover the shapes | |
669 | Standard_Boolean uClosed = Standard_True; | |
670 | TopTools_Array1OfShape shapes(1, nbSects*nbEdges); | |
671 | Standard_Integer nb=0, i, j; | |
672 | ||
673 | for (i=1; i<=nbSects; i++) { | |
674 | const TopoDS_Wire& wire = TopoDS::Wire(myWires(i)); | |
675 | if (!wire.Closed()) { | |
0d969553 | 676 | // check if the vertices are the same |
7fd59977 | 677 | TopoDS_Vertex V1, V2; |
678 | TopExp::Vertices(wire,V1,V2); | |
679 | if ( !V1.IsSame(V2)) uClosed = Standard_False; | |
680 | } | |
681 | if ( (i==1 && w1Point) || (i==nbSects && w2Point) ) { | |
0d969553 | 682 | // if the wire is punctual |
7fd59977 | 683 | anExp.Init(TopoDS::Wire(wire)); |
684 | for(j=1; j<=nbEdges; j++) { | |
e01907f1 | 685 | nb++; |
686 | shapes(nb) = anExp.Current(); | |
7fd59977 | 687 | } |
688 | } | |
689 | else { | |
0d969553 | 690 | // otherwise |
7fd59977 | 691 | for(anExp.Init(TopoDS::Wire(wire)); anExp.More(); anExp.Next()) { |
e01907f1 | 692 | nb++; |
693 | shapes(nb) = anExp.Current(); | |
7fd59977 | 694 | } |
695 | } | |
696 | } | |
697 | ||
698 | // create the new surface | |
699 | TopoDS_Shell shell; | |
700 | TopoDS_Face face; | |
701 | TopoDS_Wire W; | |
702 | TopoDS_Edge edge, edge1, edge2, edge3, edge4, couture; | |
703 | TopTools_Array1OfShape vcouture(1, nbEdges); | |
704 | ||
705 | BRep_Builder B; | |
706 | B.MakeShell(shell); | |
707 | ||
708 | TopoDS_Wire newW1, newW2; | |
709 | BRep_Builder BW1, BW2; | |
710 | BW1.MakeWire(newW1); | |
711 | BW2.MakeWire(newW2); | |
712 | ||
713 | TopLoc_Location loc; | |
714 | TopoDS_Vertex v1f,v1l,v2f,v2l; | |
715 | ||
7fd59977 | 716 | Standard_Integer nbPnts = 21; |
717 | TColgp_Array2OfPnt points(1, nbPnts, 1, nbSects); | |
718 | ||
0d969553 | 719 | // concatenate each section to get a total surface that will be segmented |
7fd59977 | 720 | Handle(Geom_BSplineSurface) TS; |
721 | TS = TotalSurf(shapes,nbSects,nbEdges,w1Point,w2Point,vClosed); | |
722 | ||
723 | if(TS.IsNull()) { | |
724 | return; | |
725 | } | |
726 | ||
727 | TopoDS_Shape firstEdge; | |
728 | for (i=1; i<=nbEdges; i++) { | |
729 | ||
0d969553 | 730 | // segmentation of TS |
7fd59977 | 731 | Handle(Geom_BSplineSurface) surface; |
732 | surface = Handle(Geom_BSplineSurface)::DownCast(TS->Copy()); | |
733 | Standard_Real Ui1,Ui2,V0,V1; | |
734 | Ui1 = i-1; | |
735 | Ui2 = i; | |
aadab519 | 736 | Ui1 = PreciseUpar(Ui1, surface); |
737 | Ui2 = PreciseUpar(Ui2, surface); | |
7fd59977 | 738 | V0 = surface->VKnot(surface->FirstVKnotIndex()); |
739 | V1 = surface->VKnot(surface->LastVKnotIndex()); | |
740 | surface->Segment(Ui1,Ui2,V0,V1); | |
741 | ||
0d969553 | 742 | // return vertices |
7fd59977 | 743 | edge = TopoDS::Edge(shapes(i)); |
744 | TopExp::Vertices(edge,v1f,v1l); | |
745 | if (edge.Orientation() == TopAbs_REVERSED) | |
746 | TopExp::Vertices(edge,v1l,v1f); | |
747 | firstEdge = edge; | |
748 | ||
749 | edge = TopoDS::Edge(shapes((nbSects-1)*nbEdges+i)); | |
750 | TopExp::Vertices(edge,v2f,v2l); | |
751 | if (edge.Orientation() == TopAbs_REVERSED) | |
752 | TopExp::Vertices(edge,v2l,v2f); | |
753 | ||
754 | // make the face | |
755 | B.MakeFace(face, surface, Precision::Confusion()); | |
756 | ||
757 | // make the wire | |
758 | B.MakeWire(W); | |
e01907f1 | 759 | |
7fd59977 | 760 | // make the missing edges |
761 | Standard_Real f1, f2, l1, l2; | |
762 | surface->Bounds(f1,l1,f2,l2); | |
e01907f1 | 763 | |
7fd59977 | 764 | // --- edge 1 |
765 | if ( w1Point ) { | |
0d969553 | 766 | // copy the degenerated edge |
7fd59977 | 767 | TopoDS_Shape aLocalShape = shapes(1).EmptyCopied(); |
768 | edge1 = TopoDS::Edge(aLocalShape); | |
e01907f1 | 769 | // edge1 = TopoDS::Edge(shapes(1).EmptyCopied()); |
7fd59977 | 770 | edge1.Orientation(TopAbs_FORWARD); |
771 | } | |
772 | else { | |
773 | B.MakeEdge(edge1, surface->VIso(f2), Precision::Confusion()); | |
774 | } | |
775 | v1f.Orientation(TopAbs_FORWARD); | |
776 | B.Add(edge1, v1f); | |
777 | v1l.Orientation(TopAbs_REVERSED); | |
778 | B.Add(edge1, v1l); | |
779 | B.Range(edge1, f1, l1); | |
0d969553 Y |
780 | // processing of looping sections |
781 | // store edges of the 1st section | |
7fd59977 | 782 | if (vClosed) |
783 | vcouture(i) = edge1; | |
e01907f1 | 784 | |
7fd59977 | 785 | |
786 | // --- edge 2 | |
787 | if (vClosed) | |
788 | edge2 = TopoDS::Edge(vcouture(i)); | |
789 | else { | |
790 | if ( w2Point ) { | |
e01907f1 | 791 | // copy of the degenerated edge |
792 | TopoDS_Shape aLocalShape = shapes(nbSects*nbEdges).EmptyCopied(); | |
793 | edge2 = TopoDS::Edge(aLocalShape); | |
794 | // edge2 = TopoDS::Edge(shapes(nbSects*nbEdges).EmptyCopied()); | |
795 | edge2.Orientation(TopAbs_FORWARD); | |
7fd59977 | 796 | } |
797 | else { | |
e01907f1 | 798 | B.MakeEdge(edge2, surface->VIso(l2), Precision::Confusion()); |
7fd59977 | 799 | } |
800 | v2f.Orientation(TopAbs_FORWARD); | |
801 | B.Add(edge2, v2f); | |
802 | v2l.Orientation(TopAbs_REVERSED); | |
803 | B.Add(edge2, v2l); | |
804 | B.Range(edge2, f1, l1); | |
805 | } | |
806 | edge2.Reverse(); | |
807 | ||
808 | ||
809 | // --- edge 3 | |
810 | if (i==1) { | |
811 | B.MakeEdge(edge3, surface->UIso(f1), Precision::Confusion()); | |
812 | v1f.Orientation(TopAbs_FORWARD); | |
813 | B.Add(edge3, v1f); | |
814 | v2f.Orientation(TopAbs_REVERSED); | |
815 | B.Add(edge3, v2f); | |
816 | B.Range(edge3, f2, l2); | |
817 | if (uClosed) { | |
e01907f1 | 818 | couture = edge3; |
7fd59977 | 819 | } |
820 | } | |
821 | else { | |
822 | edge3 = edge4; | |
823 | } | |
824 | edge3.Reverse(); | |
825 | ||
826 | // --- edge 4 | |
827 | if ( uClosed && i==nbEdges) { | |
828 | edge4 = couture; | |
829 | } | |
830 | else { | |
831 | B.MakeEdge(edge4, surface->UIso(l1), Precision::Confusion()); | |
832 | v1l.Orientation(TopAbs_FORWARD); | |
833 | B.Add(edge4, v1l); | |
834 | v2l.Orientation(TopAbs_REVERSED); | |
835 | B.Add(edge4, v2l); | |
836 | B.Range(edge4, f2, l2); | |
837 | } | |
838 | ||
839 | B.Add(W,edge1); | |
840 | B.Add(W,edge4); | |
841 | B.Add(W,edge2); | |
842 | B.Add(W,edge3); | |
843 | ||
844 | // set PCurve | |
845 | if (vClosed) { | |
846 | B.UpdateEdge(edge1, | |
e01907f1 | 847 | new Geom2d_Line(gp_Pnt2d(0,f2),gp_Dir2d(1,0)), |
848 | new Geom2d_Line(gp_Pnt2d(0,l2),gp_Dir2d(1,0)),face, | |
849 | Precision::Confusion()); | |
7fd59977 | 850 | B.Range(edge1,face,f1,l1); |
851 | } | |
852 | else { | |
853 | B.UpdateEdge(edge1,new Geom2d_Line(gp_Pnt2d(0,f2),gp_Dir2d(1,0)),face, | |
e01907f1 | 854 | Precision::Confusion()); |
7fd59977 | 855 | B.Range(edge1,face,f1,l1); |
856 | B.UpdateEdge(edge2,new Geom2d_Line(gp_Pnt2d(0,l2),gp_Dir2d(1,0)),face, | |
e01907f1 | 857 | Precision::Confusion()); |
7fd59977 | 858 | B.Range(edge2,face,f1,l1); |
859 | } | |
860 | ||
861 | if ( uClosed && nbEdges ==1 ) { | |
862 | B.UpdateEdge(edge3, | |
e01907f1 | 863 | new Geom2d_Line(gp_Pnt2d(l1,0),gp_Dir2d(0,1)), |
864 | new Geom2d_Line(gp_Pnt2d(f1,0),gp_Dir2d(0,1)),face, | |
865 | Precision::Confusion()); | |
7fd59977 | 866 | B.Range(edge3,face,f2,l2); |
867 | ||
868 | } | |
869 | else { | |
870 | B.UpdateEdge(edge3,new Geom2d_Line(gp_Pnt2d(f1,0),gp_Dir2d(0,1)),face, | |
e01907f1 | 871 | Precision::Confusion()); |
7fd59977 | 872 | B.Range(edge3,face,f2,l2); |
873 | B.UpdateEdge(edge4,new Geom2d_Line(gp_Pnt2d(l1,0),gp_Dir2d(0,1)),face, | |
e01907f1 | 874 | Precision::Confusion()); |
7fd59977 | 875 | B.Range(edge4,face,f2,l2); |
876 | } | |
877 | B.Add(face,W); | |
878 | B.Add(shell, face); | |
879 | ||
880 | // complete newW1 newW2 | |
881 | TopoDS_Edge edge12 = edge1; | |
882 | TopoDS_Edge edge22 = edge2; | |
883 | edge12.Reverse(); | |
884 | edge22.Reverse(); | |
885 | BW1.Add(newW1, edge12); | |
886 | BW2.Add(newW2, edge22); | |
887 | ||
888 | // history | |
50258e77 | 889 | myEdgeFace.Bind(firstEdge, face); |
7fd59977 | 890 | } |
891 | ||
892 | if (uClosed && w1Point && w2Point) | |
893 | shell.Closed(Standard_True); | |
894 | ||
895 | if (myIsSolid) { | |
896 | ||
897 | if (vClosed) { | |
898 | ||
899 | TopoDS_Solid solid; | |
7fd59977 | 900 | B.MakeSolid(solid); |
901 | B.Add(solid, shell); | |
e01907f1 | 902 | |
7fd59977 | 903 | // verify the orientation the solid |
904 | BRepClass3d_SolidClassifier clas3d(solid); | |
905 | clas3d.PerformInfinitePoint(Precision::Confusion()); | |
906 | if (clas3d.State() == TopAbs_IN) { | |
e01907f1 | 907 | B.MakeSolid(solid); |
908 | TopoDS_Shape aLocalShape = shell.Reversed(); | |
909 | B.Add(solid, TopoDS::Shell(aLocalShape)); | |
910 | // B.Add(solid, TopoDS::Shell(shell.Reversed())); | |
7fd59977 | 911 | } |
912 | myShape = solid; | |
913 | ||
914 | } | |
915 | ||
916 | else { | |
917 | myShape = MakeSolid(shell, newW1, newW2, myPres3d, myFirst, myLast); | |
918 | } | |
919 | ||
920 | Done(); | |
921 | } | |
922 | ||
923 | else { | |
924 | myShape = shell; | |
925 | Done(); | |
926 | } | |
e01907f1 | 927 | |
7fd59977 | 928 | TopExp_Explorer ex(myShape,TopAbs_EDGE); |
929 | while (ex.More()) { | |
930 | const TopoDS_Edge& CurE = TopoDS::Edge(ex.Current()); | |
931 | B.SameRange(CurE, Standard_False); | |
932 | B.SameParameter(CurE, Standard_False); | |
933 | Standard_Real tol = BRep_Tool::Tolerance(CurE); | |
934 | BRepLib::SameParameter(CurE,tol); | |
935 | ex.Next(); | |
936 | } | |
937 | } | |
938 | ||
5e5b6f81 | 939 | //======================================================================= |
940 | //function : EdgeToBSpline | |
941 | //purpose : auxiliary -- get curve from edge and convert it to bspline | |
942 | // parameterized from 0 to 1 | |
943 | //======================================================================= | |
944 | ||
945 | // NOTE: this code duplicates the same function in BRepFill_NSections.cxx | |
946 | static Handle(Geom_BSplineCurve) EdgeToBSpline (const TopoDS_Edge& theEdge) | |
947 | { | |
948 | Handle(Geom_BSplineCurve) aBSCurve; | |
949 | if (BRep_Tool::Degenerated(theEdge)) { | |
950 | // degenerated edge : construction of a point curve | |
951 | TColStd_Array1OfReal aKnots (1,2); | |
952 | aKnots(1) = 0.; | |
953 | aKnots(2) = 1.; | |
954 | ||
955 | TColStd_Array1OfInteger aMults (1,2); | |
956 | aMults(1) = 2; | |
957 | aMults(2) = 2; | |
958 | ||
959 | TColgp_Array1OfPnt aPoles(1,2); | |
960 | TopoDS_Vertex vf, vl; | |
961 | TopExp::Vertices(theEdge,vl,vf); | |
962 | aPoles(1) = BRep_Tool::Pnt(vf); | |
963 | aPoles(2) = BRep_Tool::Pnt(vl); | |
964 | ||
965 | aBSCurve = new Geom_BSplineCurve (aPoles, aKnots, aMults, 1); | |
966 | } | |
967 | else | |
968 | { | |
969 | // get the curve of the edge | |
970 | TopLoc_Location aLoc; | |
971 | Standard_Real aFirst, aLast; | |
972 | Handle(Geom_Curve) aCurve = BRep_Tool::Curve (theEdge, aLoc, aFirst, aLast); | |
a4bb1420 | 973 | if (aCurve.IsNull()) |
9775fa61 | 974 | throw Standard_NullObject("Null 3D curve in edge"); |
5e5b6f81 | 975 | |
976 | // convert its part used by edge to bspline; note that if edge curve is bspline, | |
977 | // conversion made via trimmed curve is still needed -- it will copy it, segment | |
978 | // as appropriate, and remove periodicity if it is periodic (deadly for approximator) | |
979 | Handle(Geom_TrimmedCurve) aTrimCurve = new Geom_TrimmedCurve (aCurve, aFirst, aLast); | |
980 | ||
981 | // special treatment of conic curve | |
982 | if (aTrimCurve->BasisCurve()->IsKind(STANDARD_TYPE(Geom_Conic))) | |
983 | { | |
51740958 | 984 | const Handle(Geom_Curve)& aCurveTrimmed = aTrimCurve; // to avoid ambiguity |
985 | GeomConvert_ApproxCurve anAppr (aCurveTrimmed, Precision::Confusion(), GeomAbs_C1, 16, 14); | |
5e5b6f81 | 986 | if (anAppr.HasResult()) |
987 | aBSCurve = anAppr.Curve(); | |
988 | } | |
989 | ||
990 | // general case | |
991 | if (aBSCurve.IsNull()) | |
992 | aBSCurve = GeomConvert::CurveToBSplineCurve (aTrimCurve); | |
993 | ||
994 | // apply transformation if needed | |
995 | if (! aLoc.IsIdentity()) | |
996 | aBSCurve->Transform (aLoc.Transformation()); | |
997 | ||
998 | // reparameterize to [0,1] | |
999 | TColStd_Array1OfReal aKnots (1, aBSCurve->NbKnots()); | |
1000 | aBSCurve->Knots (aKnots); | |
1001 | BSplCLib::Reparametrize (0., 1., aKnots); | |
1002 | aBSCurve->SetKnots (aKnots); | |
1003 | } | |
1004 | ||
1005 | // reverse curve if edge is reversed | |
1006 | if (theEdge.Orientation() == TopAbs_REVERSED) | |
1007 | aBSCurve->Reverse(); | |
1008 | ||
1009 | return aBSCurve; | |
1010 | } | |
1011 | ||
7fd59977 | 1012 | //======================================================================= |
1013 | //function : TotalSurf | |
1014 | //purpose : | |
1015 | //======================================================================= | |
1016 | ||
1017 | Handle(Geom_BSplineSurface) BRepOffsetAPI_ThruSections:: | |
e01907f1 | 1018 | TotalSurf(const TopTools_Array1OfShape& shapes, |
1019 | const Standard_Integer NbSects, | |
1020 | const Standard_Integer NbEdges, | |
1021 | const Standard_Boolean w1Point, | |
1022 | const Standard_Boolean w2Point, | |
1023 | const Standard_Boolean vClosed) const | |
7fd59977 | 1024 | { |
1025 | Standard_Integer i,j,jdeb=1,jfin=NbSects; | |
7fd59977 | 1026 | TopoDS_Vertex vf,vl; |
1027 | ||
1028 | GeomFill_SectionGenerator section; | |
1029 | Handle(Geom_BSplineSurface) surface; | |
1030 | Handle(Geom_BSplineCurve) BS, BS1; | |
1031 | Handle(Geom_TrimmedCurve) curvTrim; | |
7fd59977 | 1032 | |
1033 | if (w1Point) { | |
1034 | jdeb++; | |
e01907f1 | 1035 | TopoDS_Edge edge = TopoDS::Edge(shapes(1)); |
7fd59977 | 1036 | TopExp::Vertices(edge,vl,vf); |
1037 | TColgp_Array1OfPnt Extremities(1,2); | |
1038 | Extremities(1) = BRep_Tool::Pnt(vf); | |
1039 | Extremities(2) = BRep_Tool::Pnt(vl); | |
1040 | TColStd_Array1OfReal Bounds(1,2); | |
1041 | Bounds(1) = 0.; | |
1042 | Bounds(2) = 1.; | |
7fd59977 | 1043 | TColStd_Array1OfInteger Mult(1,2); |
5e5b6f81 | 1044 | Mult(1) = 2; |
1045 | Mult(2) = 2; | |
7fd59977 | 1046 | Handle(Geom_BSplineCurve) BSPoint |
5e5b6f81 | 1047 | = new Geom_BSplineCurve(Extremities,Bounds,Mult,1); |
7fd59977 | 1048 | section.AddCurve(BSPoint); |
1049 | } | |
1050 | ||
1051 | if (w2Point) { | |
1052 | jfin--; | |
1053 | } | |
1054 | ||
1055 | for (j=jdeb; j<=jfin; j++) { | |
1056 | ||
0d969553 | 1057 | // case of looping sections |
7fd59977 | 1058 | if (j==jfin && vClosed) { |
1059 | section.AddCurve(BS1); | |
1060 | } | |
1061 | ||
1062 | else { | |
1063 | // read the first edge to initialise CompBS; | |
5e5b6f81 | 1064 | TopoDS_Edge aPrevEdge = TopoDS::Edge (shapes((j-1)*NbEdges+1)); |
1065 | Handle(Geom_BSplineCurve) curvBS = EdgeToBSpline (aPrevEdge); | |
7fd59977 | 1066 | |
0d969553 | 1067 | // initialization |
7fd59977 | 1068 | GeomConvert_CompCurveToBSplineCurve CompBS(curvBS); |
1069 | ||
1070 | for (i=2; i<=NbEdges; i++) { | |
e01907f1 | 1071 | // read the edge |
5e5b6f81 | 1072 | TopoDS_Edge aNextEdge = TopoDS::Edge (shapes((j-1)*NbEdges+i)); |
e01907f1 | 1073 | Standard_Real aTolV = Precision::Confusion(); |
1074 | TopExp::Vertices(aNextEdge,vf,vl); | |
1075 | aTolV = Max(aTolV, BRep_Tool::Tolerance(vf)); | |
1076 | aTolV = Max(aTolV, BRep_Tool::Tolerance(vl)); | |
1077 | aTolV = Min(aTolV, 1.e-3); | |
5e5b6f81 | 1078 | curvBS = EdgeToBSpline (aNextEdge); |
7fd59977 | 1079 | |
e01907f1 | 1080 | // concatenation |
1081 | CompBS.Add(curvBS, aTolV, Standard_True, Standard_False, 1); | |
7fd59977 | 1082 | } |
1083 | ||
0d969553 | 1084 | // return the final section |
7fd59977 | 1085 | BS = CompBS.BSplineCurve(); |
1086 | section.AddCurve(BS); | |
1087 | ||
0d969553 | 1088 | // case of looping sections |
7fd59977 | 1089 | if (j==jdeb && vClosed) { |
e01907f1 | 1090 | BS1 = BS; |
7fd59977 | 1091 | } |
1092 | ||
1093 | } | |
1094 | } | |
1095 | ||
1096 | if (w2Point) { | |
e01907f1 | 1097 | TopoDS_Edge edge = TopoDS::Edge(shapes(NbSects*NbEdges)); |
7fd59977 | 1098 | TopExp::Vertices(edge,vl,vf); |
1099 | TColgp_Array1OfPnt Extremities(1,2); | |
1100 | Extremities(1) = BRep_Tool::Pnt(vf); | |
1101 | Extremities(2) = BRep_Tool::Pnt(vl); | |
1102 | TColStd_Array1OfReal Bounds(1,2); | |
1103 | Bounds(1) = 0.; | |
1104 | Bounds(2) = 1.; | |
7fd59977 | 1105 | TColStd_Array1OfInteger Mult(1,2); |
5e5b6f81 | 1106 | Mult(1) = 2; |
1107 | Mult(2) = 2; | |
7fd59977 | 1108 | Handle(Geom_BSplineCurve) BSPoint |
5e5b6f81 | 1109 | = new Geom_BSplineCurve(Extremities,Bounds,Mult,1); |
7fd59977 | 1110 | section.AddCurve(BSPoint); |
1111 | } | |
1112 | ||
1113 | section.Perform(Precision::PConfusion()); | |
1114 | Handle(GeomFill_Line) line = new GeomFill_Line(NbSects); | |
1115 | ||
1116 | Standard_Integer nbIt = 3; | |
1117 | if(myPres3d <= 1.e-3) nbIt = 0; | |
1118 | ||
1119 | Standard_Integer degmin = 2, degmax = Max(myDegMax, degmin); | |
1120 | Standard_Boolean SpApprox = Standard_True; | |
1121 | ||
1122 | GeomFill_AppSurf anApprox(degmin, degmax, myPres3d, myPres3d, nbIt); | |
1123 | anApprox.SetContinuity(myContinuity); | |
1124 | ||
1125 | if(myUseSmoothing) { | |
1126 | anApprox.SetCriteriumWeight(myCritWeights[0], myCritWeights[1], myCritWeights[2]); | |
1127 | anApprox.PerformSmoothing(line, section); | |
1128 | } | |
1129 | else { | |
1130 | anApprox.SetParType(myParamType); | |
1131 | anApprox.Perform(line, section, SpApprox); | |
1132 | } | |
1133 | ||
1134 | if(anApprox.IsDone()) { | |
1135 | surface = | |
1136 | new Geom_BSplineSurface(anApprox.SurfPoles(), anApprox.SurfWeights(), | |
e01907f1 | 1137 | anApprox.SurfUKnots(), anApprox.SurfVKnots(), |
1138 | anApprox.SurfUMults(), anApprox.SurfVMults(), | |
1139 | anApprox.UDegree(), anApprox.VDegree()); | |
7fd59977 | 1140 | } |
1141 | ||
1142 | return surface; | |
e01907f1 | 1143 | |
7fd59977 | 1144 | } |
1145 | ||
1146 | //======================================================================= | |
1147 | //function : FirstShape | |
1148 | //purpose : | |
1149 | //======================================================================= | |
1150 | ||
1151 | const TopoDS_Shape& BRepOffsetAPI_ThruSections::FirstShape() const | |
1152 | { | |
1153 | return myFirst; | |
1154 | } | |
1155 | ||
1156 | //======================================================================= | |
1157 | //function : LastShape | |
1158 | //purpose : | |
1159 | //======================================================================= | |
1160 | ||
1161 | const TopoDS_Shape& BRepOffsetAPI_ThruSections::LastShape() const | |
1162 | { | |
1163 | return myLast; | |
1164 | } | |
1165 | ||
50258e77 | 1166 | //======================================================================= |
1167 | //function : Generated | |
1168 | //purpose : | |
1169 | //======================================================================= | |
1170 | const TopTools_ListOfShape& | |
1171 | BRepOffsetAPI_ThruSections::Generated(const TopoDS_Shape& S) | |
1172 | { | |
1173 | myGenerated.Clear(); | |
1174 | ||
1175 | TopTools_SequenceOfShape AllFaces; | |
1176 | TopExp_Explorer Explo(myShape, TopAbs_FACE); | |
1177 | for (; Explo.More(); Explo.Next()) | |
1178 | AllFaces.Append(Explo.Current()); | |
1179 | ||
1180 | if (S.ShapeType() == TopAbs_EDGE) | |
1181 | { | |
1182 | if (!myEdgeNewIndices.IsBound(S)) | |
1183 | return myGenerated; | |
1184 | ||
1185 | const TColStd_ListOfInteger& Indices = myEdgeNewIndices(S); | |
1186 | //Append the faces corresponding to <Indices> | |
1187 | //These faces "grow" from the first section | |
1188 | TColStd_ListIteratorOfListOfInteger itl(Indices); | |
1189 | for (; itl.More(); itl.Next()) | |
1190 | { | |
1191 | Standard_Integer IndOfFace = itl.Value(); | |
1192 | myGenerated.Append(AllFaces(IndOfFace)); | |
1193 | } | |
1194 | ||
1195 | if (myIsRuled) | |
1196 | //Append the next faces corresponding to <Indices> | |
1197 | for (Standard_Integer i = 2; i < myWires.Length(); i++) | |
1198 | for (itl.Initialize(Indices); itl.More(); itl.Next()) | |
1199 | { | |
1200 | Standard_Integer IndOfFace = itl.Value(); | |
1201 | IndOfFace += (i-1)*myNbEdgesInSection; | |
1202 | myGenerated.Append(AllFaces(IndOfFace)); | |
1203 | } | |
1204 | } | |
1205 | else if (S.ShapeType() == TopAbs_VERTEX) | |
1206 | { | |
1207 | if (!myVertexIndex.IsBound(S)) | |
1208 | return myGenerated; | |
1209 | ||
1210 | TopTools_IndexedDataMapOfShapeListOfShape VEmap; | |
1211 | ||
1212 | Standard_Boolean IsDegen [2] = {Standard_False, Standard_False}; | |
1213 | if (myDegen1 || myDegen2) | |
1214 | { | |
1215 | TopoDS_Shape EndSections [2]; | |
1216 | EndSections[0] = myWires(1); | |
1217 | EndSections[1] = myWires(myWires.Length()); | |
1218 | for (Standard_Integer i = 0; i < 2; i++) | |
1219 | { | |
1220 | if (i == 0 && !myDegen1) | |
1221 | continue; | |
1222 | if (i == 1 && !myDegen2) | |
1223 | continue; | |
1224 | ||
1225 | Explo.Init(EndSections[i], TopAbs_VERTEX); | |
1226 | const TopoDS_Shape& aVertex = Explo.Current(); | |
1227 | if (S.IsSame(aVertex)) | |
1228 | { | |
1229 | IsDegen[i] = Standard_True; | |
1230 | break; | |
1231 | } | |
1232 | } | |
1233 | } | |
1234 | // Only one of <IsDegen> can be True: | |
1235 | // in case of one vertex for start and end degenerated sections | |
1236 | // IsDegen[0] is True; | |
1237 | if (IsDegen[0] || IsDegen[1]) | |
1238 | { | |
1239 | //For start or end degenerated section | |
1240 | //we return the whole bunch of longitudinal edges | |
1241 | TopExp::MapShapesAndAncestors(myShape, TopAbs_VERTEX, TopAbs_EDGE, VEmap); | |
1242 | TopTools_IndexedMapOfShape Emap; | |
1243 | const TopTools_ListOfShape& Elist = VEmap.FindFromKey(S); | |
1244 | TopTools_ListIteratorOfListOfShape itl(Elist); | |
1245 | for (; itl.More(); itl.Next()) | |
1246 | { | |
1247 | const TopoDS_Edge& anEdge = TopoDS::Edge(itl.Value()); | |
1248 | if (!BRep_Tool::Degenerated(anEdge)) | |
1249 | { | |
1250 | TopoDS_Vertex VV [2]; | |
1251 | TopExp::Vertices(anEdge, VV[0], VV[1]); | |
1252 | //Comprehensive check for possible case of | |
1253 | //one vertex for start and end degenerated sections: | |
1254 | //we must take only outgoing or only ingoing edges | |
1255 | if ((IsDegen[0] && S.IsSame(VV[0])) || | |
1256 | (IsDegen[1] && S.IsSame(VV[1]))) | |
1257 | Emap.Add(anEdge); | |
1258 | } | |
1259 | } | |
1260 | for (Standard_Integer j = 1; j <= Emap.Extent(); j++) | |
1261 | { | |
1262 | TopoDS_Edge anEdge = TopoDS::Edge(Emap(j)); | |
1263 | myGenerated.Append(anEdge); | |
1264 | if (myIsRuled) | |
1265 | { | |
1266 | Standard_Integer i,k; | |
1267 | for (i = 2,k = myWires.Length()-1; i < myWires.Length(); i++,k--) | |
1268 | { | |
1269 | Standard_Integer IndOfSec = (IsDegen[0])? i : k; | |
1270 | TopoDS_Vertex aVertex = (IsDegen[0])? | |
1271 | TopExp::LastVertex(anEdge) : TopExp::FirstVertex(anEdge); | |
1272 | const TopTools_ListOfShape& EElist = VEmap.FindFromKey(aVertex); | |
1273 | TopTools_IndexedMapOfShape EmapOfSection; | |
1274 | TopExp::MapShapes(myWires(IndOfSec), TopAbs_EDGE, EmapOfSection); | |
1275 | TopoDS_Edge NextEdge; | |
1276 | for (itl.Initialize(EElist); itl.More(); itl.Next()) | |
1277 | { | |
1278 | NextEdge = TopoDS::Edge(itl.Value()); | |
1279 | if (!NextEdge.IsSame(anEdge) && | |
1280 | !EmapOfSection.Contains(NextEdge)) | |
1281 | break; | |
1282 | } | |
1283 | myGenerated.Append(NextEdge); | |
1284 | anEdge = NextEdge; | |
1285 | } | |
1286 | } | |
1287 | } | |
1288 | return myGenerated; | |
1289 | } //end of if (IsDegen[0] || IsDegen[1]) | |
1290 | ||
1291 | Standard_Integer Eindex = myVertexIndex(S); | |
1292 | Standard_Integer Vindex = (Eindex > 0)? 0 : 1; | |
1293 | Eindex = Abs(Eindex); | |
1294 | const TopoDS_Shape& FirstSection = myWires(1); | |
1295 | TopoDS_Edge FirstEdge; | |
1296 | TopoDS_Iterator itw(FirstSection); | |
1297 | for (Standard_Integer inde = 1; itw.More(); itw.Next(),inde++) | |
1298 | { | |
1299 | FirstEdge = TopoDS::Edge(itw.Value()); | |
1300 | if (inde == Eindex) | |
1301 | break; | |
1302 | } | |
1303 | ||
1304 | //Find the first longitudinal edge | |
1305 | TopoDS_Face FirstFace = TopoDS::Face(AllFaces(Eindex)); | |
1306 | FirstFace.Orientation(TopAbs_FORWARD); | |
1307 | Explo.Init(FirstFace, TopAbs_EDGE); | |
1308 | TopoDS_Edge anEdge; | |
1309 | BRepAdaptor_Surface BAsurf(FirstFace, Standard_False); | |
1310 | TopoDS_Vertex FirstVertex; | |
1311 | TopExp::MapShapesAndAncestors(FirstFace, TopAbs_VERTEX, TopAbs_EDGE, VEmap); | |
1312 | if (myDegen1 && BAsurf.GetType() == GeomAbs_Plane) | |
1313 | { | |
1314 | //There are only 3 edges in the face in this case: | |
1315 | //we take 1-st or 3-rd edge | |
1316 | if (Vindex == 0) | |
1317 | { | |
1318 | Explo.Next(); | |
1319 | Explo.Next(); | |
1320 | } | |
1321 | anEdge = TopoDS::Edge(Explo.Current()); | |
1322 | } | |
1323 | else | |
1324 | { | |
1325 | TopoDS_Shape FirstEdgeInFace; | |
1326 | FirstEdgeInFace = Explo.Current(); | |
1327 | TopoDS_Vertex VV [2]; | |
1328 | TopExp::Vertices(FirstEdge, VV[0], VV[1]); | |
1329 | FirstVertex = VV[Vindex]; | |
1330 | const TopTools_ListOfShape& Elist = VEmap.FindFromKey(FirstVertex); | |
1331 | TopTools_ListIteratorOfListOfShape itl(Elist); | |
1332 | TopAbs_Orientation anEdgeOr = (Vindex == 0)? TopAbs_REVERSED : TopAbs_FORWARD; | |
1333 | for (; itl.More(); itl.Next()) | |
1334 | { | |
1335 | anEdge = TopoDS::Edge(itl.Value()); | |
1336 | if (!anEdge.IsSame(FirstEdgeInFace) && | |
1337 | !BRep_Tool::Degenerated(anEdge) && | |
1338 | anEdge.Orientation() == anEdgeOr) | |
1339 | break; | |
1340 | } | |
1341 | } | |
1342 | myGenerated.Append(anEdge); | |
1343 | if (myIsRuled) | |
1344 | //Find the chain of longitudinal edges from first to last | |
1345 | for (Standard_Integer i = 2; i < myWires.Length(); i++) | |
1346 | { | |
1347 | FirstVertex = TopExp::LastVertex(anEdge); | |
1348 | const TopTools_ListOfShape& Elist1 = VEmap.FindFromKey(FirstVertex); | |
1349 | FirstEdge = (anEdge.IsSame(Elist1.First()))? | |
1350 | TopoDS::Edge(Elist1.Last()) : TopoDS::Edge(Elist1.First()); | |
1351 | Eindex += myNbEdgesInSection; | |
1352 | FirstFace = TopoDS::Face(AllFaces(Eindex)); | |
1353 | FirstFace.Orientation(TopAbs_FORWARD); | |
1354 | VEmap.Clear(); | |
1355 | TopExp::MapShapesAndAncestors(FirstFace, TopAbs_VERTEX, TopAbs_EDGE, VEmap); | |
1356 | const TopTools_ListOfShape& Elist2 = VEmap.FindFromKey(FirstVertex); | |
1357 | anEdge = (FirstEdge.IsSame(Elist2.First()))? | |
1358 | TopoDS::Edge(Elist2.Last()) : TopoDS::Edge(Elist2.First()); | |
1359 | myGenerated.Append(anEdge); | |
1360 | } | |
1361 | } | |
1362 | ||
1363 | return myGenerated; | |
1364 | } | |
1365 | ||
7fd59977 | 1366 | //======================================================================= |
1367 | //function : GeneratedFace | |
1368 | //purpose : | |
1369 | //======================================================================= | |
1370 | ||
1371 | TopoDS_Shape BRepOffsetAPI_ThruSections::GeneratedFace(const TopoDS_Shape& edge) const | |
1372 | { | |
1373 | TopoDS_Shape bid; | |
50258e77 | 1374 | if (myEdgeFace.IsBound(edge)) { |
1375 | return myEdgeFace(edge); | |
7fd59977 | 1376 | } |
1377 | else { | |
1378 | return bid; | |
1379 | } | |
1380 | } | |
1381 | ||
1382 | ||
1383 | //======================================================================= | |
1384 | //function : CriteriumWeight | |
0d969553 | 1385 | //purpose : returns the Weights associated to the criterium used in |
7fd59977 | 1386 | // the optimization. |
1387 | //======================================================================= | |
1388 | // | |
1389 | void BRepOffsetAPI_ThruSections::CriteriumWeight(Standard_Real& W1, Standard_Real& W2, Standard_Real& W3) const | |
1390 | { | |
1391 | W1 = myCritWeights[0]; | |
1392 | W2 = myCritWeights[1]; | |
1393 | W3 = myCritWeights[2]; | |
1394 | } | |
1395 | //======================================================================= | |
1396 | //function : SetCriteriumWeight | |
1397 | //purpose : | |
1398 | //======================================================================= | |
1399 | ||
1400 | void BRepOffsetAPI_ThruSections::SetCriteriumWeight(const Standard_Real W1, const Standard_Real W2, const Standard_Real W3) | |
1401 | { | |
9775fa61 | 1402 | if (W1 < 0 || W2 < 0 || W3 < 0 ) throw Standard_DomainError(); |
7fd59977 | 1403 | myCritWeights[0] = W1; |
1404 | myCritWeights[1] = W2; | |
1405 | myCritWeights[2] = W3; | |
1406 | } | |
1407 | //======================================================================= | |
1408 | //function : SetContinuity | |
1409 | //purpose : | |
1410 | //======================================================================= | |
1411 | ||
1412 | void BRepOffsetAPI_ThruSections::SetContinuity (const GeomAbs_Shape TheCont) | |
1413 | { | |
1414 | myContinuity = TheCont; | |
1415 | } | |
1416 | ||
1417 | //======================================================================= | |
1418 | //function : Continuity | |
1419 | //purpose : | |
1420 | //======================================================================= | |
1421 | ||
1422 | GeomAbs_Shape BRepOffsetAPI_ThruSections::Continuity () const | |
1423 | { | |
1424 | return myContinuity; | |
1425 | } | |
1426 | ||
1427 | //======================================================================= | |
1428 | //function : SetParType | |
1429 | //purpose : | |
1430 | //======================================================================= | |
1431 | ||
1432 | void BRepOffsetAPI_ThruSections::SetParType (const Approx_ParametrizationType ParType) | |
1433 | { | |
1434 | myParamType = ParType; | |
1435 | } | |
1436 | ||
1437 | //======================================================================= | |
1438 | //function : ParType | |
1439 | //purpose : | |
1440 | //======================================================================= | |
1441 | ||
1442 | Approx_ParametrizationType BRepOffsetAPI_ThruSections::ParType () const | |
1443 | { | |
1444 | return myParamType; | |
1445 | } | |
1446 | ||
1447 | //======================================================================= | |
1448 | //function : SetMaxDegree | |
1449 | //purpose : | |
1450 | //======================================================================= | |
1451 | ||
1452 | void BRepOffsetAPI_ThruSections:: SetMaxDegree(const Standard_Integer MaxDeg) | |
1453 | { | |
1454 | myDegMax = MaxDeg; | |
1455 | } | |
1456 | ||
1457 | //======================================================================= | |
1458 | //function : MaxDegree | |
1459 | //purpose : | |
1460 | //======================================================================= | |
1461 | ||
1462 | Standard_Integer BRepOffsetAPI_ThruSections::MaxDegree () const | |
1463 | { | |
1464 | return myDegMax; | |
1465 | } | |
1466 | ||
1467 | //======================================================================= | |
1468 | //function : SetSmoothing | |
1469 | //purpose : | |
1470 | //======================================================================= | |
1471 | ||
1472 | void BRepOffsetAPI_ThruSections::SetSmoothing(const Standard_Boolean UseVar) | |
1473 | { | |
1474 | myUseSmoothing = UseVar; | |
1475 | } | |
1476 | ||
1477 | //======================================================================= | |
1478 | //function : UseSmoothing | |
1479 | //purpose : | |
1480 | //======================================================================= | |
1481 | ||
1482 | Standard_Boolean BRepOffsetAPI_ThruSections::UseSmoothing () const | |
1483 | { | |
1484 | return myUseSmoothing; | |
1485 | } | |
1486 | ||
1487 | ||
1488 | ||
1489 | ||
1490 |