mirror of
https://git.salome-platform.org/gitpub/modules/geom.git
synced 2025-01-01 04:10:34 +05:00
539 lines
16 KiB
C++
539 lines
16 KiB
C++
// Copyright (C) 2005 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
|
|
// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
|
|
//
|
|
// This library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
// License as published by the Free Software Foundation; either
|
|
// version 2.1 of the License.
|
|
//
|
|
// This library is distributed in the hope that it will be useful
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
//
|
|
// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
|
|
//
|
|
// File: NMTTools_DEProcessor.cxx
|
|
// Created: Wed Sep 12 12:10:52 2001
|
|
// Author: Peter KURNEV
|
|
// <pkv@irinox>
|
|
|
|
#include <NMTTools_DEProcessor.ixx>
|
|
|
|
#include <Precision.hxx>
|
|
|
|
#include <TColStd_ListIteratorOfListOfInteger.hxx>
|
|
#include <TColStd_ListOfInteger.hxx>
|
|
|
|
#include <gp_Pnt2d.hxx>
|
|
#include <gp_Pnt.hxx>
|
|
#include <gp_Sphere.hxx>
|
|
|
|
#include <Geom2d_Curve.hxx>
|
|
#include <Geom2d_Line.hxx>
|
|
#include <Geom2dAdaptor_Curve.hxx>
|
|
#include <Geom2dInt_GInter.hxx>
|
|
|
|
#include <IntRes2d_IntersectionPoint.hxx>
|
|
|
|
#include <TopoDS_Shape.hxx>
|
|
#include <TopoDS_Edge.hxx>
|
|
#include <TopoDS.hxx>
|
|
#include <TopoDS_Face.hxx>
|
|
#include <TopoDS_Vertex.hxx>
|
|
#include <TopoDS_Solid.hxx>
|
|
|
|
#include <TopExp.hxx>
|
|
#include <TopTools_IndexedDataMapOfShapeListOfShape.hxx>
|
|
#include <TopTools_ListOfShape.hxx>
|
|
#include <TopTools_ListIteratorOfListOfShape.hxx>
|
|
|
|
#include <BRep_Tool.hxx>
|
|
#include <BRep_Builder.hxx>
|
|
|
|
#include <BRepAdaptor_Surface.hxx>
|
|
|
|
#include <BooleanOperations_AncestorsSeqAndSuccessorsSeq.hxx>
|
|
|
|
#include <IntTools_Tools.hxx>
|
|
#include <IntTools_Context.hxx>
|
|
|
|
#include <BOPTools_DEInfo.hxx>
|
|
#include <BOPTools_Pave.hxx>
|
|
#include <BOPTools_ListOfPave.hxx>
|
|
#include <BOPTools_ListIteratorOfListOfPave.hxx>
|
|
#include <BOPTools_PaveBlock.hxx>
|
|
#include <BOPTools_ListOfPaveBlock.hxx>
|
|
#include <BOPTools_ListIteratorOfListOfPaveBlock.hxx>
|
|
#include <BOPTools_PaveBlockIterator.hxx>
|
|
#include <BOPTools_SSInterference.hxx>
|
|
#include <BOPTools_PavePool.hxx>
|
|
#include <BOPTools_PaveSet.hxx>
|
|
#include <BOPTools_Tools3D.hxx>
|
|
#include <BOPTools_InterferencePool.hxx>
|
|
#include <BOPTools_CArray1OfSSInterference.hxx>
|
|
#include <BOPTools_SplitShapesPool.hxx>
|
|
|
|
#include <NMTDS_ShapesDataStructure.hxx>
|
|
|
|
#include <NMTTools_PaveFiller.hxx>
|
|
//
|
|
#include <BOPTools_SequenceOfCurves.hxx>
|
|
#include <BOPTools_Curve.hxx>
|
|
//
|
|
#include <Geom2d_TrimmedCurve.hxx>
|
|
|
|
#include <ElCLib.hxx>
|
|
#include <gp_Lin2d.hxx>
|
|
|
|
|
|
|
|
//=======================================================================
|
|
// function: NMTTools_DEProcessor::NMTTools_DEProcessor
|
|
// purpose:
|
|
//=======================================================================
|
|
NMTTools_DEProcessor::NMTTools_DEProcessor(NMTTools_PaveFiller& aPaveFiller)
|
|
:
|
|
myIsDone(Standard_False)
|
|
{
|
|
myFiller=(NMTTools_PaveFiller*) &aPaveFiller;
|
|
myDS=myFiller->DS();
|
|
}
|
|
//=======================================================================
|
|
// function: IsDone
|
|
// purpose:
|
|
//=======================================================================
|
|
Standard_Boolean NMTTools_DEProcessor::IsDone() const
|
|
{
|
|
return myIsDone;
|
|
}
|
|
//=======================================================================
|
|
// function: Do
|
|
// purpose:
|
|
//=======================================================================
|
|
void NMTTools_DEProcessor::Do()
|
|
{
|
|
Standard_Integer aNbE;
|
|
myIsDone=Standard_False;
|
|
|
|
FindDegeneratedEdges();
|
|
aNbE=myDEMap.Extent();
|
|
|
|
if (!aNbE) {
|
|
myIsDone=Standard_True;
|
|
return;
|
|
}
|
|
DoPaves();
|
|
}
|
|
//=======================================================================
|
|
// function: FindDegeneratedEdges
|
|
// purpose:
|
|
//=======================================================================
|
|
void NMTTools_DEProcessor::FindDegeneratedEdges()
|
|
{
|
|
Standard_Integer i, aNb, nV, nF, nVx, ip, iRankE;
|
|
TopTools_IndexedDataMapOfShapeListOfShape aMEF;
|
|
//
|
|
aNb=myDS->NumberOfShapesOfTheObject();
|
|
//
|
|
for (i=1; i<=aNb; i++) {
|
|
const TopoDS_Shape aF=myDS->Shape(i);//mpv
|
|
if (aF.ShapeType()==TopAbs_FACE) {
|
|
TopExp::MapShapesAndAncestors (aF, TopAbs_EDGE, TopAbs_FACE, aMEF);
|
|
}
|
|
}
|
|
//
|
|
for (i=1; i<=aNb; i++) {
|
|
const TopoDS_Shape aS=myDS->Shape(i);//mpv
|
|
if (aS.ShapeType()==TopAbs_EDGE) {
|
|
const TopoDS_Edge& aE=TopoDS::Edge(aS);
|
|
|
|
if (BRep_Tool::Degenerated(aE)) {
|
|
iRankE=myDS->Rank(i);
|
|
|
|
TopoDS_Vertex aV=TopExp::FirstVertex(aE);
|
|
|
|
nVx=myDS->ShapeIndex(aV, iRankE);
|
|
//
|
|
nV=nVx;
|
|
ip=myFiller->FindSDVertex(nV);
|
|
if (ip) {
|
|
nV=ip;
|
|
}
|
|
//
|
|
TColStd_ListOfInteger aLFn;
|
|
const TopTools_ListOfShape& aLF=aMEF.FindFromKey(aE);
|
|
TopTools_ListIteratorOfListOfShape anIt(aLF);
|
|
for (; anIt.More(); anIt.Next()) {
|
|
const TopoDS_Shape& aF=anIt.Value();
|
|
nF=myDS->ShapeIndex(aF, iRankE);
|
|
aLFn.Append(nF);
|
|
}
|
|
BOPTools_DEInfo aDEInfo;
|
|
aDEInfo.SetVertex(nV);
|
|
aDEInfo.SetFaces(aLFn);
|
|
|
|
myDEMap.Add (i, aDEInfo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//=======================================================================
|
|
// function: DoPaves
|
|
// purpose:
|
|
//=======================================================================
|
|
void NMTTools_DEProcessor::DoPaves()
|
|
{
|
|
|
|
Standard_Integer i, aNbE, nED, nVD, nFD, aNbLPB;
|
|
//
|
|
nFD=0;
|
|
aNbE=myDEMap.Extent();
|
|
for (i=1; i<=aNbE; i++) {
|
|
nED=myDEMap.FindKey(i);
|
|
|
|
const BOPTools_DEInfo& aDEInfo=myDEMap(i);
|
|
nVD=aDEInfo.Vertex();
|
|
// Fill PaveSet for the edge nED
|
|
const TColStd_ListOfInteger& nLF=aDEInfo.Faces();
|
|
TColStd_ListIteratorOfListOfInteger anIt(nLF);
|
|
for (; anIt.More(); anIt.Next()) {
|
|
nFD=anIt.Value();
|
|
//
|
|
BOPTools_ListOfPaveBlock aLPB;
|
|
FindPaveBlocks(nED, nVD, nFD, aLPB);
|
|
//modified by NIZNHY-PKV Fri Mar 23 10:35:27 2007f
|
|
//
|
|
aNbLPB=aLPB.Extent();
|
|
if (!aNbLPB) {
|
|
continue;
|
|
}
|
|
//modified by NIZNHY-PKV Fri Mar 23 10:35:33 2007t
|
|
FillPaveSet (nED, nVD, nFD, aLPB);
|
|
}
|
|
//
|
|
// Fill aSplitEdges for the edge nED
|
|
FillSplitEdgesPool(nED);
|
|
//
|
|
// MakeSplitEdges
|
|
MakeSplitEdges(nED, nFD);
|
|
//
|
|
}// next nED
|
|
}
|
|
//=======================================================================
|
|
// function: FindPaveBlocks
|
|
// purpose:
|
|
//=======================================================================
|
|
void NMTTools_DEProcessor::FindPaveBlocks(const Standard_Integer ,
|
|
const Standard_Integer nVD,
|
|
const Standard_Integer nFD,
|
|
BOPTools_ListOfPaveBlock& aLPBOut)
|
|
{
|
|
BOPTools_ListIteratorOfListOfPaveBlock anIt;
|
|
Standard_Integer i, aNb, nF2, nV;
|
|
//
|
|
BOPTools_CArray1OfSSInterference& aFFs=(myFiller->InterfPool())->SSInterferences();
|
|
//
|
|
aNb=aFFs.Extent();
|
|
for (i=1; i<=aNb; ++i) {
|
|
BOPTools_SSInterference& aFF=aFFs(i);
|
|
//
|
|
nF2=aFF.OppositeIndex(nFD);
|
|
if (!nF2) {
|
|
continue;
|
|
}
|
|
//
|
|
// Split Parts
|
|
const BOPTools_ListOfPaveBlock& aLPBSplits=aFF.PaveBlocks();
|
|
anIt.Initialize(aLPBSplits);
|
|
for (; anIt.More(); anIt.Next()) {
|
|
const BOPTools_PaveBlock& aPBSp=anIt.Value();
|
|
//
|
|
const BOPTools_Pave& aPave1=aPBSp.Pave1();
|
|
nV=aPave1.Index();
|
|
if (nV==nVD) {
|
|
aLPBOut.Append(aPBSp);
|
|
continue;
|
|
}
|
|
//
|
|
const BOPTools_Pave& aPave2=aPBSp.Pave2();
|
|
nV=aPave2.Index();
|
|
if (nV==nVD) {
|
|
aLPBOut.Append(aPBSp);
|
|
continue;
|
|
}
|
|
}
|
|
//
|
|
// Section Parts
|
|
Standard_Integer j, aNbCurves;
|
|
BOPTools_SequenceOfCurves& aSC=aFF.Curves();
|
|
aNbCurves=aSC.Length();
|
|
|
|
for (j=1; j<=aNbCurves; j++) {
|
|
const BOPTools_Curve& aBC=aSC(j);
|
|
const BOPTools_ListOfPaveBlock& aLPBSe=aBC.NewPaveBlocks();
|
|
|
|
anIt.Initialize(aLPBSe);
|
|
for (; anIt.More(); anIt.Next()) {
|
|
const BOPTools_PaveBlock& aPBSe=anIt.Value();
|
|
|
|
const BOPTools_Pave& aPv1=aPBSe.Pave1();
|
|
nV=aPv1.Index();
|
|
if (nV==nVD) {
|
|
aLPBOut.Append(aPBSe);
|
|
continue;
|
|
}
|
|
|
|
const BOPTools_Pave& aPv2=aPBSe.Pave2();
|
|
nV=aPv2.Index();
|
|
if (nV==nVD) {
|
|
aLPBOut.Append(aPBSe);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//=======================================================================
|
|
// function: FillPaveSet
|
|
// purpose:
|
|
//=======================================================================
|
|
void NMTTools_DEProcessor::FillPaveSet (const Standard_Integer nED,
|
|
const Standard_Integer nVD,
|
|
const Standard_Integer nFD,
|
|
const BOPTools_ListOfPaveBlock& aLPB)
|
|
{
|
|
Standard_Boolean bIsDone, bXDir, bRejectFlag;
|
|
Standard_Integer nE, aNbPoints, j;
|
|
Standard_Real aTD1, aTD2, aT1, aT2, aTolInter, aX, aDT;
|
|
//
|
|
aDT=Precision::PConfusion();
|
|
//
|
|
BOPTools_PaveSet& aPaveSet= (myFiller->ChangePavePool()).ChangeValue(myDS->RefEdge(nED));
|
|
//
|
|
// Clear aPaveSet, aSplitEdges
|
|
aPaveSet.ChangeSet().Clear();
|
|
//
|
|
const TopoDS_Edge aDE=TopoDS::Edge(myDS->Shape(nED));//mpv
|
|
const TopoDS_Face aDF=TopoDS::Face(myDS->Shape(nFD));//mpv
|
|
//
|
|
// 2D Curve of degenerated edge on the face aDF
|
|
// Modified Thu Sep 14 14:35:18 2006
|
|
// Contribution of Samtech www.samcef.com BEGIN
|
|
//Handle(Geom2d_Curve) aC2DDE=BRep_Tool::CurveOnSurface(aDE, aDF, aTD1, aTD2);
|
|
Handle(Geom2d_Curve) aC2DDE1=BRep_Tool::CurveOnSurface(aDE, aDF, aTD1, aTD2);
|
|
Handle(Geom2d_TrimmedCurve)aC2DDE=new Geom2d_TrimmedCurve(aC2DDE1, aTD1, aTD2);
|
|
// Contribution of Samtech www.samcef.com END
|
|
//
|
|
// Choose direction for Degenerated Edge
|
|
gp_Pnt2d aP2d1, aP2d2;
|
|
aC2DDE->D0(aTD1, aP2d1);
|
|
aC2DDE->D0(aTD2, aP2d2);
|
|
|
|
bXDir=Standard_False;
|
|
if (fabs(aP2d1.Y()-aP2d2.Y()) < aDT){
|
|
bXDir=!bXDir;
|
|
}
|
|
//
|
|
// Prepare bounding Paves
|
|
BOPTools_Pave aPave1 (nVD, aTD1, BooleanOperations_UnknownInterference);
|
|
aPaveSet.Append(aPave1);
|
|
BOPTools_Pave aPave2 (nVD, aTD2, BooleanOperations_UnknownInterference);
|
|
aPaveSet.Append(aPave2);
|
|
//
|
|
// Fill other paves
|
|
BOPTools_ListIteratorOfListOfPaveBlock anIt(aLPB);
|
|
for (; anIt.More(); anIt.Next()) {
|
|
const BOPTools_PaveBlock& aPB=anIt.Value();
|
|
nE=aPB.Edge();
|
|
const TopoDS_Edge aE=TopoDS::Edge(myDS->Shape(nE));//mpv
|
|
|
|
Handle(Geom2d_Curve) aC2D=BRep_Tool::CurveOnSurface(aE, aDF, aT1, aT2);
|
|
//
|
|
// Intersection
|
|
aTolInter=0.001;
|
|
|
|
Geom2dAdaptor_Curve aGAC1, aGAC2;
|
|
|
|
aGAC1.Load(aC2DDE, aTD1, aTD2);
|
|
Handle(Geom2d_Line) aL2D= Handle(Geom2d_Line)::DownCast(aC2D);
|
|
if (!aL2D.IsNull()) {
|
|
aGAC2.Load(aC2D);
|
|
}
|
|
else {
|
|
aGAC2.Load(aC2D, aT1, aT2);
|
|
}
|
|
|
|
Geom2dInt_GInter aGInter(aGAC1, aGAC2, aTolInter, aTolInter);
|
|
|
|
bIsDone=aGInter.IsDone();
|
|
if(bIsDone) {
|
|
aNbPoints=aGInter.NbPoints();
|
|
if (aNbPoints) {
|
|
for (j=1; j<=aNbPoints; ++j) {
|
|
gp_Pnt2d aP2D=aGInter.Point(j).Value();
|
|
//
|
|
// Modified to obtain exact parameter Thu Sep 14 14:35:18 2006
|
|
// Contribution of Samtech www.samcef.com BEGIN
|
|
Handle(Geom2d_Line) aCLDE=Handle(Geom2d_Line)::DownCast(aC2DDE1);
|
|
if (aCLDE.IsNull()) {
|
|
continue;
|
|
}
|
|
gp_Lin2d aLDE=aCLDE->Lin2d();
|
|
aX=ElCLib::Parameter(aLDE, aP2D);
|
|
//
|
|
//aX=(bXDir) ? aP2D.X(): aP2D.Y();
|
|
// Contribution of Samtech www.samcef.com END
|
|
//
|
|
if (fabs (aX-aTD1) < aDT || fabs (aX-aTD2) < aDT) {
|
|
continue;
|
|
}
|
|
if (aX < aTD1 || aX > aTD2) {
|
|
continue;
|
|
}
|
|
//
|
|
bRejectFlag=Standard_False;
|
|
const BOPTools_ListOfPave& aListOfPave=aPaveSet.Set();
|
|
BOPTools_ListIteratorOfListOfPave aPaveIt(aListOfPave);
|
|
for (; aPaveIt.More(); aPaveIt.Next()) {
|
|
const BOPTools_Pave& aPavex=aPaveIt.Value();
|
|
Standard_Real aXx=aPavex.Param();
|
|
if (fabs (aX-aXx) < aDT) {
|
|
bRejectFlag=Standard_True;
|
|
break;
|
|
}
|
|
}
|
|
if (bRejectFlag) {
|
|
continue;
|
|
}
|
|
//
|
|
BOPTools_Pave aPave(nVD, aX, BooleanOperations_UnknownInterference);
|
|
aPaveSet.Append(aPave);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//=======================================================================
|
|
// function: FillSplitEdgesPool
|
|
// purpose:
|
|
//=======================================================================
|
|
void NMTTools_DEProcessor::FillSplitEdgesPool (const Standard_Integer nED)
|
|
{
|
|
BOPTools_SplitShapesPool& aSplitShapesPool=myFiller->ChangeSplitShapesPool();
|
|
BOPTools_ListOfPaveBlock& aSplitEdges=aSplitShapesPool.ChangeValue(myDS->RefEdge(nED));
|
|
//
|
|
aSplitEdges.Clear();
|
|
//
|
|
const BOPTools_PavePool& aPavePool=myFiller->PavePool();
|
|
BOPTools_PavePool* pPavePool=(BOPTools_PavePool*) &aPavePool;
|
|
BOPTools_PaveSet& aPaveSet= pPavePool->ChangeValue(myDS->RefEdge(nED));
|
|
|
|
BOPTools_PaveBlockIterator aPBIt(nED, aPaveSet);
|
|
for (; aPBIt.More(); aPBIt.Next()) {
|
|
BOPTools_PaveBlock& aPB=aPBIt.Value();
|
|
aSplitEdges.Append(aPB);
|
|
}
|
|
}
|
|
//=======================================================================
|
|
// function: MakeSplitEdges
|
|
// purpose:
|
|
//=======================================================================
|
|
void NMTTools_DEProcessor::MakeSplitEdges (const Standard_Integer nED,
|
|
const Standard_Integer nFD)
|
|
{
|
|
const BOPTools_SplitShapesPool& aSplitShapesPool=myFiller->SplitShapesPool();
|
|
const BOPTools_ListOfPaveBlock& aSplitEdges=aSplitShapesPool(myDS->RefEdge(nED));
|
|
|
|
Standard_Integer nV1, nV2, aNewShapeIndex;
|
|
Standard_Real t1, t2;
|
|
TopoDS_Edge aE, aESplit;
|
|
TopoDS_Vertex aV1, aV2;
|
|
|
|
const TopoDS_Edge aDE=TopoDS::Edge(myDS->Shape(nED));//mpv
|
|
const TopoDS_Face aDF=TopoDS::Face(myDS->Shape(nFD));//mpv
|
|
|
|
BOPTools_ListIteratorOfListOfPaveBlock aPBIt(aSplitEdges);
|
|
|
|
for (; aPBIt.More(); aPBIt.Next()) {
|
|
BOPTools_PaveBlock& aPB=aPBIt.Value();
|
|
|
|
const BOPTools_Pave& aPave1=aPB.Pave1();
|
|
nV1=aPave1.Index();
|
|
t1=aPave1.Param();
|
|
aV1=TopoDS::Vertex(myDS->GetShape(nV1));
|
|
aV1.Orientation(TopAbs_FORWARD);
|
|
|
|
const BOPTools_Pave& aPave2=aPB.Pave2();
|
|
nV2=aPave2.Index();
|
|
t2=aPave2.Param();
|
|
aV2=TopoDS::Vertex(myDS->GetShape(nV2));
|
|
aV2.Orientation(TopAbs_REVERSED);
|
|
|
|
MakeSplitEdge(aDE, aDF, aV1, t1, aV2, t2, aESplit);
|
|
//
|
|
// Add Split Part of the Original Edge to the DS
|
|
BooleanOperations_AncestorsSeqAndSuccessorsSeq anASSeq;
|
|
|
|
anASSeq.SetNewSuccessor(nV1);
|
|
anASSeq.SetNewOrientation(aV1.Orientation());
|
|
|
|
anASSeq.SetNewSuccessor(nV2);
|
|
anASSeq.SetNewOrientation(aV2.Orientation());
|
|
|
|
myDS->InsertShapeAndAncestorsSuccessors(aESplit, anASSeq);
|
|
aNewShapeIndex=myDS->NumberOfInsertedShapes();
|
|
myDS->SetState(aNewShapeIndex, BooleanOperations_UNKNOWN);
|
|
//
|
|
// Fill Split Set for the Original Edge
|
|
aPB.SetEdge(aNewShapeIndex);
|
|
//
|
|
}
|
|
}
|
|
//=======================================================================
|
|
// function: MakeSplitEdge
|
|
// purpose:
|
|
//=======================================================================
|
|
void NMTTools_DEProcessor::MakeSplitEdge (const TopoDS_Edge& aE,
|
|
const TopoDS_Face& aF,
|
|
const TopoDS_Vertex& aV1,
|
|
const Standard_Real aP1,
|
|
const TopoDS_Vertex& aV2,
|
|
const Standard_Real aP2,
|
|
TopoDS_Edge& aNewEdge)
|
|
{
|
|
Standard_Real aTol=1.e-7;
|
|
|
|
TopoDS_Edge E=aE;
|
|
|
|
E.EmptyCopy();
|
|
BRep_Builder BB;
|
|
BB.Add (E, aV1);
|
|
BB.Add (E, aV2);
|
|
|
|
BB.Range(E, aF, aP1, aP2);
|
|
|
|
BB.Degenerated(E, Standard_True);
|
|
|
|
BB.UpdateEdge(E, aTol);
|
|
aNewEdge=E;
|
|
}
|
|
/*
|
|
//=======================================================================
|
|
// function: NMTTools_DEProcessor::NMTTools_DEProcessor
|
|
// purpose:
|
|
//=======================================================================
|
|
NMTTools_DEProcessor::NMTTools_DEProcessor(NMTTools_PDSFiller& pDSFiller)
|
|
:
|
|
myIsDone(Standard_False)
|
|
{
|
|
myDSFiller=pDSFiller;
|
|
myFiller=(NMTTools_PaveFiller*) &(myDSFiller->PaveFiller());
|
|
myDS=myFiller->DS();
|
|
}
|
|
*/
|