1 // Copyright (C) 2005 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
2 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License.
9 // This library is distributed in the hope that it will be useful
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 // File: GEOMAlgo_WireSplitter.cxx
22 // Author: Peter KURNEV
26 #include <GEOMAlgo_WireSplitter.ixx>
28 #include <TColStd_SequenceOfReal.hxx>
29 #include <Precision.hxx>
31 #include <gp_Pnt2d.hxx>
32 #include <gp_Vec2d.hxx>
33 #include <TColgp_SequenceOfPnt2d.hxx>
35 #include <Geom_Curve.hxx>
36 #include <Geom2d_Curve.hxx>
37 #include <GeomAdaptor_Surface.hxx>
39 #include <TopAbs_Orientation.hxx>
42 #include <TopoDS_Vertex.hxx>
43 #include <TopoDS_Edge.hxx>
44 #include <TopoDS_Face.hxx>
45 #include <TopoDS_Iterator.hxx>
48 #include <BRep_Tool.hxx>
49 #include <BRepAdaptor_Surface.hxx>
50 #include <BRepAdaptor_Curve2d.hxx>
53 #include <TopExp_Explorer.hxx>
57 #include <TopTools_SequenceOfShape.hxx>
58 #include <TopTools_ListOfShape.hxx>
59 #include <TopTools_ListIteratorOfListOfShape.hxx>
60 #include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
61 #include <TopTools_IndexedMapOfShape.hxx>
63 #include <BOPTColStd_ListOfListOfShape.hxx>
64 #include <BOPTColStd_ListIteratorOfListOfListOfShape.hxx>
66 #include <BOPTools_Tools2D.hxx>
68 #include <BOP_EdgeInfo.hxx>
69 #include <BOP_ListOfEdgeInfo.hxx>
70 #include <BOP_ListIteratorOfListOfEdgeInfo.hxx>
71 #include <BOP_IndexedDataMapOfVertexListEdgeInfo.hxx>
74 void Path (const GeomAdaptor_Surface& aGAS,
75 const TopoDS_Face& myFace,
76 const TopoDS_Vertex& aVa,
77 const TopoDS_Edge& aEOuta,
78 BOP_EdgeInfo& anEdgeInfo,
79 TopTools_SequenceOfShape& aLS,
80 TopTools_SequenceOfShape& aVertVa,
81 TColgp_SequenceOfPnt2d& aCoordVa,
82 BOPTColStd_ListOfListOfShape& myShapes,
83 BOP_IndexedDataMapOfVertexListEdgeInfo& mySmartMap);
87 Standard_Real Angle (const gp_Dir2d& aDir2D);
91 void GetNextVertex(const TopoDS_Vertex& aV,
92 const TopoDS_Edge& aE,
95 Standard_Real ClockWiseAngle(const Standard_Real aAngleIn,
96 const Standard_Real aAngleOut);
99 Standard_Real AngleIn(const TopoDS_Edge& aEIn,
100 const BOP_ListOfEdgeInfo& aLEInfo);
103 Standard_Real Angle2D (const TopoDS_Vertex& aV,
104 const TopoDS_Edge& anEdge,
105 const TopoDS_Face& myFace,
106 const GeomAdaptor_Surface& aGAS,
107 const Standard_Boolean aFlag);
109 gp_Pnt2d Coord2d (const TopoDS_Vertex& aV1,
110 const TopoDS_Edge& aE1,
111 const TopoDS_Face& aF);
113 gp_Pnt2d Coord2dVf (const TopoDS_Edge& aE,
114 const TopoDS_Face& aF);
116 Standard_Real Tolerance2D (const TopoDS_Vertex& aV,
117 const GeomAdaptor_Surface& aGAS);
120 Standard_Integer NbWaysOut(const BOP_ListOfEdgeInfo& );
123 //=======================================================================
126 //=======================================================================
127 GEOMAlgo_WireSplitter::GEOMAlgo_WireSplitter()
130 myNothingToDo(Standard_False)
133 //=======================================================================
136 //=======================================================================
137 GEOMAlgo_WireSplitter::~GEOMAlgo_WireSplitter()
140 //=======================================================================
143 //=======================================================================
144 void GEOMAlgo_WireSplitter::SetFace(const TopoDS_Face& aFace)
148 //=======================================================================
151 //=======================================================================
152 const TopoDS_Face& GEOMAlgo_WireSplitter::Face()const
156 //=======================================================================
157 // function: SetEdges
159 //=======================================================================
160 void GEOMAlgo_WireSplitter::SetEdges(const TopTools_ListOfShape& aLE)
162 TopTools_ListIteratorOfListOfShape anIt;
165 anIt.Initialize(aLE);
166 for (; anIt.More(); anIt.Next()) {
167 const TopoDS_Shape& aE =anIt.Value();
169 if (aE.Orientation()==TopAbs_INTERNAL){
176 //=======================================================================
179 //=======================================================================
180 const TopTools_ListOfShape& GEOMAlgo_WireSplitter::Edges()const
184 //=======================================================================
185 // function: IsNothingToDo
187 //=======================================================================
188 Standard_Boolean GEOMAlgo_WireSplitter::IsNothingToDo()const
190 return myNothingToDo;
192 //=======================================================================
195 //=======================================================================
196 const BOPTColStd_ListOfListOfShape& GEOMAlgo_WireSplitter::Shapes()const
200 //=======================================================================
203 //=======================================================================
204 void GEOMAlgo_WireSplitter::Perform()
207 myNothingToDo=Standard_True;
209 Standard_Integer index, i, aNb, aCntIn, aCntOut;
210 Standard_Boolean anIsIn;
211 Standard_Real anAngle;
213 BOP_ListOfEdgeInfo emptyInfo;
214 TopTools_ListIteratorOfListOfShape anItList;
216 // 1.Filling mySmartMap
219 anItList.Initialize(myEdges);
220 for (; anItList.More(); anItList.Next()) {
221 const TopoDS_Edge& anEdge = TopoDS::Edge(anItList.Value());
223 if (!BOPTools_Tools2D::HasCurveOnSurface (anEdge, myFace)) {
227 TopExp_Explorer anExpVerts (anEdge, TopAbs_VERTEX);
228 for (; anExpVerts.More(); anExpVerts.Next()) {
229 const TopoDS_Shape& aVertex= anExpVerts.Current();
231 index = mySmartMap.FindIndex(aVertex);
233 index=mySmartMap.Add(aVertex, emptyInfo);
236 BOP_ListOfEdgeInfo& aListOfEInfo=mySmartMap(index);
239 aEInfo.SetEdge(anEdge);
241 TopAbs_Orientation anOr=aVertex.Orientation();
243 if (anOr==TopAbs_FORWARD) {
244 aEInfo.SetInFlag(Standard_False);
247 else if (anOr==TopAbs_REVERSED) {
248 aEInfo.SetInFlag(Standard_True);
251 aListOfEInfo.Append(aEInfo);
255 aNb=mySmartMap.Extent();
258 myNothingToDo=Standard_True;
260 for (i=1; i<=aNb; i++) {
263 const BOP_ListOfEdgeInfo& aLEInfo= mySmartMap(i);
264 BOP_ListIteratorOfListOfEdgeInfo anIt(aLEInfo);
265 for (; anIt.More(); anIt.Next()) {
266 const BOP_EdgeInfo& anEdgeInfo=anIt.Value();
267 anIsIn=anEdgeInfo.IsIn();
275 if (aCntIn!=1 || aCntOut!=1) {
276 myNothingToDo=Standard_False;
281 // Each vertex has one edge In and one - Out. Good. But it is not enought
282 // to consider that nothing to do with this. We must check edges on TShape
283 // coinsidence. If there are such edges there is something to do with.
286 Standard_Integer aNbE, aNbMapEE;
287 TopTools_IndexedDataMapOfShapeListOfShape aMapEE;
288 aNbE=myEdges.Extent();
290 anItList.Initialize(myEdges);
291 for (; anItList.More(); anItList.Next()) {
292 const TopoDS_Shape& aE = anItList.Value();
294 if (!aMapEE.Contains(aE)) {
295 TopTools_ListOfShape aLEx;
297 aMapEE.Add(aE, aLEx);
300 TopTools_ListOfShape& aLEx=aMapEE.ChangeFromKey(aE);
305 Standard_Boolean bFlag;
307 aNbMapEE=aMapEE.Extent();
308 for (i=1; i<=aNbMapEE; i++) {
309 const TopTools_ListOfShape& aLEx=aMapEE(i);
316 const TopoDS_Shape& aE1=aLEx.First();
317 const TopoDS_Shape& aE2=aLEx.Last();
318 if (aE1.IsSame(aE2)) {
319 bFlag=Standard_False;
324 bFlag=Standard_False;
328 myNothingToDo=myNothingToDo && bFlag;
337 // 3. Angles in mySmartMap
338 BRepAdaptor_Surface aBAS(myFace);
339 const GeomAdaptor_Surface& aGAS=aBAS.Surface();
340 for (i=1; i<=aNb; i++) {
341 const TopoDS_Vertex& aV=TopoDS::Vertex (mySmartMap.FindKey(i));
342 const BOP_ListOfEdgeInfo& aLEInfo= mySmartMap(i);
344 BOP_ListIteratorOfListOfEdgeInfo anIt(aLEInfo);
345 for (; anIt.More(); anIt.Next()) {
346 BOP_EdgeInfo& anEdgeInfo=anIt.Value();
347 const TopoDS_Edge& aE=anEdgeInfo.Edge();
349 TopoDS_Vertex aVV=aV;
351 anIsIn=anEdgeInfo.IsIn();
354 aVV.Orientation(TopAbs_REVERSED);
355 anAngle=Angle2D (aVV, aE, myFace, aGAS, Standard_True);
360 aVV.Orientation(TopAbs_FORWARD);
361 anAngle=Angle2D (aVV, aE, myFace, aGAS, Standard_False);
363 anEdgeInfo.SetAngle(anAngle);
370 Standard_Boolean anIsOut, anIsNotPassed;
372 TopTools_SequenceOfShape aLS, aVertVa;
373 TColgp_SequenceOfPnt2d aCoordVa;
375 BOP_ListIteratorOfListOfEdgeInfo anIt;
377 for (i=1; i<=aNb; i++) {
378 const TopoDS_Vertex aVa=TopoDS::Vertex (mySmartMap.FindKey(i));
379 const BOP_ListOfEdgeInfo& aLEInfo=mySmartMap(i);
381 anIt.Initialize(aLEInfo);
382 for (; anIt.More(); anIt.Next()) {
383 BOP_EdgeInfo& anEdgeInfo=anIt.Value();
384 const TopoDS_Edge& aEOuta=anEdgeInfo.Edge();
386 anIsOut=!anEdgeInfo.IsIn();
387 anIsNotPassed=!anEdgeInfo.Passed();
389 if (anIsOut && anIsNotPassed) {
395 Path(aGAS, myFace, aVa, aEOuta, anEdgeInfo, aLS,
396 aVertVa, aCoordVa, myShapes, mySmartMap);
402 Standard_Integer aNbV, aNbE;
403 TopoDS_Vertex aV1, aV2;
404 BOPTColStd_ListOfListOfShape aShapes;
405 BOPTColStd_ListIteratorOfListOfListOfShape anItW(myShapes);
407 for (; anItW.More(); anItW.Next()) {
408 TopTools_IndexedMapOfShape aMV, aME;
409 const TopTools_ListOfShape& aLE=anItW.Value();
410 TopTools_ListIteratorOfListOfShape anItE(aLE);
411 for (; anItE.More(); anItE.Next()) {
412 const TopoDS_Edge& aE=TopoDS::Edge(anItE.Value());
414 TopExp::Vertices(aE, aV1, aV2);
426 anItW.Initialize(aShapes);
427 for (; anItW.More(); anItW.Next()) {
428 const TopTools_ListOfShape& aLE=anItW.Value();
429 myShapes.Append(aLE);
435 //=======================================================================
438 //=======================================================================
439 void Path (const GeomAdaptor_Surface& aGAS,
440 const TopoDS_Face& myFace,
441 const TopoDS_Vertex& aVa,
442 const TopoDS_Edge& aEOuta,
443 BOP_EdgeInfo& anEdgeInfo,
444 TopTools_SequenceOfShape& aLS,
445 TopTools_SequenceOfShape& aVertVa,
446 TColgp_SequenceOfPnt2d& aCoordVa,
447 BOPTColStd_ListOfListOfShape& myShapes,
448 BOP_IndexedDataMapOfVertexListEdgeInfo& mySmartMap)
451 Standard_Integer i,j, aNb, aNbj;
452 Standard_Real aTol, anAngleIn, anAngleOut, anAngle, aMinAngle;
453 Standard_Real aTol2D, aTol2D2;
454 Standard_Real aTol2, aD2;//, aTolUVb, aTolVVb;
455 Standard_Boolean anIsSameV2d, anIsSameV, anIsFound, anIsOut, anIsNotPassed;
456 BOP_ListIteratorOfListOfEdgeInfo anIt;
464 // Do not escape through edge from which you enter
467 const TopoDS_Shape& anEPrev=aLS(aNb);
468 if (anEPrev.IsSame(aEOuta)) {
474 anEdgeInfo.SetPassed(Standard_True);
478 TopoDS_Vertex pVa=aVa;
479 pVa.Orientation(TopAbs_FORWARD);
480 gp_Pnt2d aPa=Coord2d(pVa, aEOuta, myFace);
481 aCoordVa.Append(aPa);
483 GetNextVertex (pVa, aEOuta, aVb);
485 gp_Pnt2d aPb=Coord2d(aVb, aEOuta, myFace);
487 const BOP_ListOfEdgeInfo& aLEInfoVb=mySmartMap.FindFromKey(aVb);
489 aTol=2.*Tolerance2D(aVb, aGAS);
492 //modified by NIZNHY-PKV Tue Nov 14 16:56:19 2006f
494 UVTolerance2D(aVb, aGAS, aTolUVb, aTolVVb);
495 aTolUVb = 2.*aTolUVb;
496 aTolVVb = 2.*aTolVVb;
498 //modified by NIZNHY-PKV Tue Nov 14 16:56:26 2006t
503 TopTools_ListOfShape aBuf;
505 for (i=aNb; i>0; i--) {
506 const TopoDS_Shape& aVPrev=aVertVa(i);
507 const gp_Pnt2d& aPaPrev=aCoordVa(i);
508 const TopoDS_Shape& aEPrev=aLS(i);
512 anIsSameV=aVPrev.IsSame(aVb);
513 anIsSameV2d=Standard_False;
516 anIsSameV2d = Standard_True;
518 aD2=aPaPrev.SquareDistance(aPb);
519 anIsSameV2d =aD2<aTol2;
521 //modified by NIZNHY-PKV Tue Nov 14 16:56:49 2006f
524 Standard_Real udist, vdist;
526 udist=fabs(aPaPrev.X() - aPb.X());
527 vdist=fabs(aPaPrev.Y() - aPb.Y());
528 if((udist > aTolUVb) || (vdist > aTolVVb)) {
529 anIsSameV2d=!anIsSameV2d;
533 //modified by NIZNHY-PKV Tue Nov 14 16:56:54 2006t
536 if (anIsSameV && anIsSameV2d) {
537 myShapes.Append(aBuf);
539 TopTools_SequenceOfShape aLSt, aVertVat;
540 TColgp_SequenceOfPnt2d aCoordVat;
552 aVb=TopoDS::Vertex(aVertVa(i));
554 for (j=1; j<=aNbj; j++) {
556 aVertVat.Append(aVertVa(j));
557 aCoordVat.Append(aCoordVa(j));
573 aTol2D=2.*Tolerance2D(aVb, aGAS);
574 aTol2D2=aTol2D*aTol2D;
576 // anAngleIn in Vb from edge aEOuta
577 const BOP_ListOfEdgeInfo& aLEInfo=mySmartMap.FindFromKey(aVb);
579 anAngleIn=AngleIn(aEOuta, aLEInfo);
582 BOP_EdgeInfo *pEdgeInfo=NULL;
585 anIsFound=Standard_False;
587 Standard_Integer aCurIndexE = 0;
589 anIt.Initialize(aLEInfo);
590 for (; anIt.More(); anIt.Next()) {
591 BOP_EdgeInfo& anEI=anIt.Value();
592 const TopoDS_Edge& aE=anEI.Edge();
593 anIsOut=!anEI.IsIn();
594 anIsNotPassed=!anEI.Passed();
596 if (anIsOut && anIsNotPassed) {
599 // Is there one way to go out of the vertex
600 // we have to use it only.
601 Standard_Integer iCnt;
602 iCnt=NbWaysOut (aLEInfo);
605 // no way to go . (Error)
610 // the one and only way to go out .
612 anIsFound=Standard_True;
616 // Look for minimal angle and make the choice.
619 aP2Dx=Coord2dVf(aE, myFace);
621 aD2=aP2Dx.SquareDistance(aPb);
627 anAngleOut=anEI.Angle();
629 anAngle=ClockWiseAngle(anAngleIn, anAngleOut);
630 if (anAngle < aMinAngle) {
633 anIsFound=Standard_True;
636 } // for (; anIt.More(); anIt.Next())
639 // no way to go . (Error)
643 aEOutb=pEdgeInfo->Edge();
645 Path (aGAS, myFace, aVb, aEOutb, *pEdgeInfo, aLS,
646 aVertVa, aCoordVa, myShapes, mySmartMap);
648 //=======================================================================
649 // function: Coord2dVf
651 //=======================================================================
652 gp_Pnt2d Coord2dVf (const TopoDS_Edge& aE,
653 const TopoDS_Face& aF)
655 Standard_Real aCoord=99.;
656 gp_Pnt2d aP2D1(aCoord, aCoord);
660 for (; aIt.More(); aIt.Next()) {
661 const TopoDS_Shape& aVx=aIt.Value();
662 if (aVx.Orientation()==TopAbs_FORWARD) {
663 const TopoDS_Vertex& aVxx=TopoDS::Vertex(aVx);
664 aP2D1=Coord2d(aVxx, aE, aF);
670 //=======================================================================
671 // function: Tolerance2D
673 //=======================================================================
674 Standard_Real Tolerance2D (const TopoDS_Vertex& aV,
675 const GeomAdaptor_Surface& aGAS)
677 Standard_Real aTol2D, anUr, aVr, aTolV3D;
678 GeomAbs_SurfaceType aType;
680 aType=aGAS.GetType();
681 aTolV3D=BRep_Tool::Tolerance(aV);
683 anUr=aGAS.UResolution(aTolV3D);
684 aVr =aGAS.VResolution(aTolV3D);
685 aTol2D=(aVr>anUr) ? aVr : anUr;
687 if (aType==GeomAbs_BSplineSurface||
688 aType==GeomAbs_Sphere) {
689 if (aTol2D < aTolV3D) {
693 if (aType==GeomAbs_BSplineSurface) {
700 //=======================================================================
703 //=======================================================================
704 gp_Pnt2d Coord2d (const TopoDS_Vertex& aV1,
705 const TopoDS_Edge& aE1,
706 const TopoDS_Face& aF)
708 Standard_Real aT, aFirst, aLast;
709 Handle(Geom2d_Curve) aC2D;
712 aT=BRep_Tool::Parameter (aV1, aE1, aF);
713 aC2D=BRep_Tool::CurveOnSurface(aE1, aF, aFirst, aLast);
714 aC2D->D0 (aT, aP2D1);
718 //=======================================================================
721 //=======================================================================
722 Standard_Real AngleIn(const TopoDS_Edge& aEIn,
723 const BOP_ListOfEdgeInfo& aLEInfo)
725 Standard_Real anAngleIn;
726 Standard_Boolean anIsIn;
727 BOP_ListIteratorOfListOfEdgeInfo anIt;
729 anIt.Initialize(aLEInfo);
730 for (; anIt.More(); anIt.Next()) {
731 BOP_EdgeInfo& anEdgeInfo=anIt.Value();
732 const TopoDS_Edge& aE=anEdgeInfo.Edge();
733 anIsIn=anEdgeInfo.IsIn();
735 if (anIsIn && aE==aEIn) {
736 anAngleIn=anEdgeInfo.Angle();
743 //=======================================================================
744 // function: ClockWiseAngle
746 //=======================================================================
747 Standard_Real ClockWiseAngle(const Standard_Real aAngleIn,
748 const Standard_Real aAngleOut)
750 Standard_Real aTwoPi=Standard_PI+Standard_PI;
751 Standard_Real dA, A1, A2, AIn, AOut ;
759 if (AOut >= aTwoPi) {
776 //else if (dA <= 1.e-15) {
777 else if (dA <= 1.e-14) {
782 //=======================================================================
783 // function: GetNextVertex
785 //=======================================================================
786 void GetNextVertex(const TopoDS_Vertex& aV,
787 const TopoDS_Edge& aE,
793 for (; aIt.More(); aIt.Next()) {
794 const TopoDS_Shape& aVx=aIt.Value();
795 if (!aVx.IsEqual(aV)) {
796 aV1=TopoDS::Vertex(aVx);
802 //=======================================================================
805 //=======================================================================
806 Standard_Real Angle2D (const TopoDS_Vertex& aV,
807 const TopoDS_Edge& anEdge,
808 const TopoDS_Face& myFace,
809 const GeomAdaptor_Surface& aGAS,
810 const Standard_Boolean aFlag)
812 Standard_Real aFirst, aLast, aToler, dt, aTV, aTV1, anAngle;
814 Handle(Geom2d_Curve) aC2D;
816 BOPTools_Tools2D::CurveOnSurface (anEdge, myFace, aC2D,
817 aFirst, aLast, aToler, Standard_True);
819 aTV=BRep_Tool::Parameter (aV, anEdge, myFace);
820 if (Precision::IsInfinite(aTV))
824 dt=Tolerance2D(aV, aGAS);
826 if(dt > (aLast - aFirst) * 0.25) {
827 // to save direction of the curve as much as it possible
828 // in the case of big tolerances
829 dt = (aLast - aFirst) * 0.25;
832 if (fabs (aTV-aFirst) < fabs(aTV - aLast)) {
841 aC2D->D0 (aTV1, aPV1);
846 gp_Vec2d aV2DIn(aPV1, aPV);
852 gp_Vec2d aV2DOut(aPV, aPV1);
856 gp_Dir2d aDir2D(aV2D);
857 anAngle=Angle(aDir2D);
861 //=======================================================================
864 //=======================================================================
865 Standard_Real Angle (const gp_Dir2d& aDir2D)
867 gp_Dir2d aRefDir(1., 0.);
868 Standard_Real anAngle = aRefDir.Angle(aDir2D);
871 anAngle += Standard_PI + Standard_PI;
876 //=======================================================================
877 // function: NbWaysOut
879 //=======================================================================
880 Standard_Integer NbWaysOut(const BOP_ListOfEdgeInfo& aLEInfo)
882 Standard_Boolean bIsOut, bIsNotPassed;
883 Standard_Integer iCnt=0;
884 BOP_ListIteratorOfListOfEdgeInfo anIt;
886 anIt.Initialize(aLEInfo);
887 for (; anIt.More(); anIt.Next()) {
888 BOP_EdgeInfo& anEI=anIt.Value();
891 bIsNotPassed=!anEI.Passed();
892 if (bIsOut && bIsNotPassed) {
898 //modified by NIZNHY-PKV Tue Nov 14 16:55:31 2006f
901 void UVTolerance2D (const TopoDS_Vertex& aV,
902 const GeomAdaptor_Surface& aGAS,
903 Standard_Real& aTolU,
904 Standard_Real& aTolV);
907 //=======================================================================
908 //function : UVTolerance2D
910 //=======================================================================
911 void UVTolerance2D (const TopoDS_Vertex& aV,
912 const GeomAdaptor_Surface& aGAS,
913 Standard_Real& aTolU,
914 Standard_Real& aTolV)
916 Standard_Real aTolV3D;
918 aTolV3D = BRep_Tool::Tolerance(aV);
919 aTolU=aGAS.UResolution(aTolV3D);
920 aTolV=aGAS.VResolution(aTolV3D);
923 //modified by NIZNHY-PKV Tue Nov 14 16:55:39 2006