mirror of
https://git.dev.opencascade.org/repos/occt.git
synced 2025-04-21 10:13:43 +03:00
New method CheckFaceSelfIntersection has been added to BOPAlgo_CheckerSI: now self-intersection of each face is found as well as pairs of intersecting faces; -method IntPatch_Intersection::Perform(S1,D1,TolArc,TolTang) is modified for more effective search of self-interasections in case of Surface Of Extrusion; -method IntCurve_IntPolyPolyGen::Perform(C1,D1,TolConf,Tol,NbIter) is modified to detect segments of intersections. Small correction. Test cases are corrected. Correction of compiler error Fix of regressions Names of shapes correction
1373 lines
51 KiB
Plaintext
1373 lines
51 KiB
Plaintext
// Created on: 1992-10-13
|
|
// Created by: Laurent BUCHARD
|
|
// Copyright (c) 1992-1999 Matra Datavision
|
|
// Copyright (c) 1999-2014 OPEN CASCADE SAS
|
|
//
|
|
// This file is part of Open CASCADE Technology software library.
|
|
//
|
|
// This library is free software; you can redistribute it and/or modify it under
|
|
// the terms of the GNU Lesser General Public License version 2.1 as published
|
|
// by the Free Software Foundation, with special exception defined in the file
|
|
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
|
// distribution for complete text of the license and disclaimer of any warranty.
|
|
//
|
|
// Alternatively, this file may be used under the terms of Open CASCADE
|
|
// commercial license or contractual agreement.
|
|
|
|
// Modified by skv - Tue Mar 1 14:22:09 2005 OCC8169
|
|
|
|
|
|
#ifndef OCCT_DEBUG
|
|
#define No_Standard_RangeError
|
|
#define No_Standard_OutOfRange
|
|
#endif
|
|
|
|
|
|
#include <Standard_ConstructionError.hxx>
|
|
|
|
#include <IntRes2d_Domain.hxx>
|
|
#include <IntRes2d_Position.hxx>
|
|
#include <IntRes2d_Transition.hxx>
|
|
#include <IntRes2d_IntersectionPoint.hxx>
|
|
#include <IntRes2d_IntersectionSegment.hxx>
|
|
|
|
|
|
#include <IntImpParGen.hxx>
|
|
|
|
#include <Intf_SectionPoint.hxx>
|
|
#include <Intf_SectionLine.hxx>
|
|
#include <Intf_TangentZone.hxx>
|
|
#include <Intf_InterferencePolygon2d.hxx>
|
|
|
|
#include <gp_Vec2d.hxx>
|
|
|
|
#include <math_Vector.hxx>
|
|
#include <math_FunctionSetRoot.hxx>
|
|
#include <math_NewtonFunctionSetRoot.hxx>
|
|
#include <NCollection_Handle.hxx>
|
|
#include <Bnd_Box2d.hxx>
|
|
#include <Precision.hxx>
|
|
|
|
//======================================================================
|
|
|
|
#define NBITER_MAX_POLYGON 10
|
|
#define TOL_CONF_MINI 0.0000000001
|
|
#define TOL_MINI 0.0000000001
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
void GetIntersection(const TheCurve& theC1, const Standard_Real theT1f, const Standard_Real theT1l,
|
|
const TheCurve& theC2, const Standard_Real theT2f, const Standard_Real theT2l,
|
|
const Standard_Real theTolConf,
|
|
const Standard_Integer theMaxCount,
|
|
IntRes2d_IntersectionPoint& thePInt, Standard_Real& theDist,
|
|
Standard_Integer& theCount);
|
|
|
|
|
|
Standard_Boolean HeadOrEndPoint( const IntRes2d_Domain& D1
|
|
,const TheCurve& C1
|
|
,const Standard_Real u
|
|
,const IntRes2d_Domain& D2
|
|
,const TheCurve& C2
|
|
,const Standard_Real v
|
|
,const Standard_Real TolConf
|
|
,IntRes2d_IntersectionPoint& IntPt
|
|
,Standard_Boolean& HeadOn1
|
|
,Standard_Boolean& HeadOn2
|
|
,Standard_Boolean& EndOn1
|
|
,Standard_Boolean& EndOn2
|
|
,Standard_Integer PosSegment);
|
|
|
|
|
|
//======================================================================
|
|
IntCurve_IntPolyPolyGen::IntCurve_IntPolyPolyGen()
|
|
{
|
|
const Standard_Integer aMinPntNb = 20; // Minimum number of samples.
|
|
myMinPntNb = aMinPntNb;
|
|
done = Standard_False;
|
|
}
|
|
//======================================================================
|
|
void IntCurve_IntPolyPolyGen::Perform( const TheCurve& C1
|
|
,const IntRes2d_Domain& D1
|
|
,const TheCurve& C2
|
|
,const IntRes2d_Domain& D2
|
|
,const Standard_Real TheTolConf
|
|
,const Standard_Real TheTol)
|
|
{
|
|
this->ResetFields();
|
|
DomainOnCurve1=D1;
|
|
DomainOnCurve2=D2;
|
|
Standard_Real DU = D1.LastParameter()-D1.FirstParameter();
|
|
Standard_Real DV = D2.LastParameter()-D2.FirstParameter();
|
|
Standard_Real Tl=(TheTol < TOL_MINI)? TOL_MINI : TheTol;
|
|
Standard_Real TlConf=(TheTolConf < TOL_CONF_MINI)? TOL_CONF_MINI : TheTolConf;
|
|
Perform(C1,D1,C2,D2,TlConf,Tl,0,DU,DV);
|
|
//----------------------------------------------------------------------
|
|
//-- Processing of end points
|
|
//----------------------------------------------------------------------
|
|
Standard_Boolean HeadOn1 = Standard_False;
|
|
Standard_Boolean HeadOn2 = Standard_False;
|
|
Standard_Boolean EndOn1 = Standard_False;
|
|
Standard_Boolean EndOn2 = Standard_False;
|
|
Standard_Integer i;
|
|
Standard_Integer n=this->NbPoints();
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
//-- The points Head Head ... End End are not rejected if
|
|
//-- they are already present at the end of segment
|
|
//-- ( It is not possible to test the equities on the parameters)
|
|
//-- ( these points are not found at EpsX precision )
|
|
//-- PosSegment = 1 if Head Head
|
|
//-- 2 if Head End
|
|
//-- 4 if End Head
|
|
//-- 8 if End End
|
|
//--------------------------------------------------------------------
|
|
Standard_Integer PosSegment = 0;
|
|
|
|
for(i=1;i<=n;i++) {
|
|
IntRes2d_Position Pos1 = this->Point(i).TransitionOfFirst().PositionOnCurve();
|
|
if(Pos1 == IntRes2d_Head) HeadOn1 = Standard_True;
|
|
else if(Pos1 == IntRes2d_End) EndOn1 = Standard_True;
|
|
|
|
IntRes2d_Position Pos2 = this->Point(i).TransitionOfSecond().PositionOnCurve();
|
|
if(Pos2 == IntRes2d_Head) HeadOn2 = Standard_True;
|
|
else if(Pos2 == IntRes2d_End) EndOn2 = Standard_True;
|
|
|
|
if(Pos1 == IntRes2d_Head) {
|
|
if(Pos2 == IntRes2d_Head) PosSegment|=1;
|
|
else if(Pos2 == IntRes2d_End) PosSegment|=2;
|
|
}
|
|
else if(Pos1 == IntRes2d_End) {
|
|
if(Pos2 == IntRes2d_Head) PosSegment|=4;
|
|
else if(Pos2 == IntRes2d_End) PosSegment|=8;
|
|
}
|
|
}
|
|
|
|
n=this->NbSegments();
|
|
for(i=1;i<=n;i++) {
|
|
IntRes2d_Position Pos1 = this->Segment(i).FirstPoint().TransitionOfFirst().PositionOnCurve();
|
|
if(Pos1 == IntRes2d_Head) HeadOn1 = Standard_True;
|
|
else if(Pos1 == IntRes2d_End) EndOn1 = Standard_True;
|
|
|
|
IntRes2d_Position Pos2 = this->Segment(i).FirstPoint().TransitionOfSecond().PositionOnCurve();
|
|
if(Pos2 == IntRes2d_Head) HeadOn2 = Standard_True;
|
|
else if(Pos2 == IntRes2d_End) EndOn2 = Standard_True;
|
|
|
|
if(Pos1 == IntRes2d_Head) {
|
|
if(Pos2 == IntRes2d_Head) PosSegment|=1;
|
|
else if(Pos2 == IntRes2d_End) PosSegment|=2;
|
|
}
|
|
else if(Pos1 == IntRes2d_End) {
|
|
if(Pos2 == IntRes2d_Head) PosSegment|=4;
|
|
else if(Pos2 == IntRes2d_End) PosSegment|=8;
|
|
}
|
|
|
|
Pos1 = this->Segment(i).LastPoint().TransitionOfFirst().PositionOnCurve();
|
|
if(Pos1 == IntRes2d_Head) HeadOn1 = Standard_True;
|
|
else if(Pos1 == IntRes2d_End) EndOn1 = Standard_True;
|
|
|
|
Pos2 = this->Segment(i).LastPoint().TransitionOfSecond().PositionOnCurve();
|
|
if(Pos2 == IntRes2d_Head) HeadOn2 = Standard_True;
|
|
else if(Pos2 == IntRes2d_End) EndOn2 = Standard_True;
|
|
|
|
if(Pos1 == IntRes2d_Head) {
|
|
if(Pos2 == IntRes2d_Head) PosSegment|=1;
|
|
else if(Pos2 == IntRes2d_End) PosSegment|=2;
|
|
}
|
|
else if(Pos1 == IntRes2d_End) {
|
|
if(Pos2 == IntRes2d_Head) PosSegment|=4;
|
|
else if(Pos2 == IntRes2d_End) PosSegment|=8;
|
|
}
|
|
}
|
|
|
|
Standard_Real U0 = D1.FirstParameter();
|
|
Standard_Real U1 = D1.LastParameter();
|
|
Standard_Real V0 = D2.FirstParameter();
|
|
Standard_Real V1 = D2.LastParameter();
|
|
IntRes2d_IntersectionPoint IntPt;
|
|
|
|
if(D1.FirstTolerance() || D2.FirstTolerance()) {
|
|
if(HeadOrEndPoint(D1,C1,U0,D2,C2,V0,TheTolConf,IntPt,HeadOn1,HeadOn2,EndOn1,EndOn2,PosSegment))
|
|
this->Insert(IntPt);
|
|
}
|
|
if(D1.FirstTolerance() || D2.LastTolerance()) {
|
|
if(HeadOrEndPoint(D1,C1,U0,D2,C2,V1,TheTolConf,IntPt,HeadOn1,HeadOn2,EndOn1,EndOn2,PosSegment))
|
|
this->Insert(IntPt);
|
|
}
|
|
if(D1.LastTolerance() || D2.FirstTolerance()) {
|
|
if(HeadOrEndPoint(D1,C1,U1,D2,C2,V0,TheTolConf,IntPt,HeadOn1,HeadOn2,EndOn1,EndOn2,PosSegment))
|
|
this->Insert(IntPt);
|
|
}
|
|
if(D1.LastTolerance() || D2.LastTolerance()) {
|
|
if(HeadOrEndPoint(D1,C1,U1,D2,C2,V1,TheTolConf,IntPt,HeadOn1,HeadOn2,EndOn1,EndOn2,PosSegment))
|
|
this->Insert(IntPt);
|
|
}
|
|
}
|
|
|
|
|
|
//======================================================================
|
|
//== A u t o I n t e r s e c t i o n
|
|
//======================================================================
|
|
void IntCurve_IntPolyPolyGen::Perform( const TheCurve& C1
|
|
,const IntRes2d_Domain& D1
|
|
,const Standard_Real TheTolConf
|
|
,const Standard_Real TheTol)
|
|
{
|
|
|
|
this->ResetFields();
|
|
DomainOnCurve1=D1;
|
|
DomainOnCurve2=D1;
|
|
Standard_Real DU = D1.LastParameter()-D1.FirstParameter();
|
|
Standard_Real Tl=(TheTol < TOL_MINI)? TOL_MINI : TheTol;
|
|
Standard_Real TlConf=(TheTolConf < TOL_CONF_MINI)? TOL_CONF_MINI : TheTolConf;
|
|
Perform(C1,D1,TlConf,Tl,0,DU,DU);
|
|
Standard_Integer i;
|
|
Standard_Integer n=this->NbPoints();
|
|
|
|
//--------------------------------------------------------------------
|
|
//-- The points Head Head ... End End are not rejected if
|
|
//-- they are already present at the end of segment
|
|
//-- ( It is not possible to test the equities on the parameters)
|
|
//-- ( these points are not found at EpsX precision )
|
|
//-- PosSegment = 1 if Head Head
|
|
//-- 2 if Head End
|
|
//-- 4 if End Head
|
|
//-- 8 if End End
|
|
//--------------------------------------------------------------------
|
|
Standard_Integer PosSegment = 0;
|
|
|
|
for(i=1;i<=n;i++) {
|
|
IntRes2d_Position Pos1 = this->Point(i).TransitionOfFirst().PositionOnCurve();
|
|
IntRes2d_Position Pos2 = this->Point(i).TransitionOfSecond().PositionOnCurve();
|
|
|
|
if(Pos1 == IntRes2d_Head) {
|
|
if(Pos2 == IntRes2d_Head) PosSegment|=1;
|
|
else if(Pos2 == IntRes2d_End) PosSegment|=2;
|
|
}
|
|
else if(Pos1 == IntRes2d_End) {
|
|
if(Pos2 == IntRes2d_Head) PosSegment|=4;
|
|
else if(Pos2 == IntRes2d_End) PosSegment|=8;
|
|
}
|
|
}
|
|
|
|
n=this->NbSegments();
|
|
for(i=1;i<=n;i++) {
|
|
IntRes2d_Position Pos1 = this->Segment(i).FirstPoint().TransitionOfFirst().PositionOnCurve();
|
|
IntRes2d_Position Pos2 = this->Segment(i).FirstPoint().TransitionOfSecond().PositionOnCurve();
|
|
|
|
if(Pos1 == IntRes2d_Head) {
|
|
if(Pos2 == IntRes2d_Head) PosSegment|=1;
|
|
else if(Pos2 == IntRes2d_End) PosSegment|=2;
|
|
}
|
|
else if(Pos1 == IntRes2d_End) {
|
|
if(Pos2 == IntRes2d_Head) PosSegment|=4;
|
|
else if(Pos2 == IntRes2d_End) PosSegment|=8;
|
|
}
|
|
|
|
Pos1 = this->Segment(i).LastPoint().TransitionOfFirst().PositionOnCurve();
|
|
Pos2 = this->Segment(i).LastPoint().TransitionOfSecond().PositionOnCurve();
|
|
|
|
if(Pos1 == IntRes2d_Head) {
|
|
if(Pos2 == IntRes2d_Head) PosSegment|=1;
|
|
else if(Pos2 == IntRes2d_End) PosSegment|=2;
|
|
}
|
|
else if(Pos1 == IntRes2d_End) {
|
|
if(Pos2 == IntRes2d_Head) PosSegment|=4;
|
|
else if(Pos2 == IntRes2d_End) PosSegment|=8;
|
|
}
|
|
}
|
|
}
|
|
//======================================================================
|
|
|
|
void IntCurve_IntPolyPolyGen::Perform( const TheCurve& C1
|
|
,const IntRes2d_Domain& D1
|
|
,const Standard_Real TolConf
|
|
,const Standard_Real Tol
|
|
,const Standard_Integer NbIter
|
|
,const Standard_Real /*DeltaU*/
|
|
,const Standard_Real) {
|
|
|
|
gp_Vec2d Tan1,Tan2,Norm1,Norm2;
|
|
gp_Pnt2d P1,P2;
|
|
Standard_Integer nbsamples;
|
|
done = Standard_False;
|
|
|
|
nbsamples = TheCurveTool::NbSamples(C1,D1.FirstParameter(),D1.LastParameter());
|
|
|
|
if(NbIter>3 || (NbIter>2 && nbsamples>100)) return;
|
|
|
|
nbsamples*=2; //--- We take systematically two times more points
|
|
//-- than on a normal curve.
|
|
//-- Auto-intersecting curves often produce
|
|
//-- polygons rather far from the curve with parameter ct.
|
|
|
|
if(NbIter>0) {
|
|
nbsamples=(3*(nbsamples*NbIter))/2;
|
|
}
|
|
IntCurve_ThePolygon2d Poly1(C1,nbsamples,D1,Tol);
|
|
if(!Poly1.AutoIntersectionIsPossible()) {
|
|
done = Standard_True;
|
|
return;
|
|
}
|
|
//-- Poly1.Dump();
|
|
//----------------------------------------------------------------------
|
|
//-- If the deflection is less than the Tolerance of Confusion
|
|
//-- then the deflection of the polygon is set in TolConf
|
|
//-- (Detection of Tangency Zones)
|
|
//----------------------------------------------------------------------
|
|
if(Poly1.DeflectionOverEstimation() < TolConf) {
|
|
Poly1.SetDeflectionOverEstimation(TolConf);
|
|
}
|
|
|
|
Intf_InterferencePolygon2d InterPP(Poly1);
|
|
IntCurve_ExactIntersectionPoint EIP(C1,C1,TolConf);
|
|
Standard_Real U,V;
|
|
|
|
//----------------------------------------------------------------------
|
|
//-- Processing of SectionPoint
|
|
//----------------------------------------------------------------------
|
|
Standard_Integer Nbsp = InterPP.NbSectionPoints();
|
|
if(Nbsp>=1) {
|
|
|
|
//-- ---------------------------------------------------------------------
|
|
//-- filtering, filtering, filtering ...
|
|
//--
|
|
Standard_Integer* TriIndex = new Standard_Integer [Nbsp+1];
|
|
Standard_Integer* PtrSegIndex1 = new Standard_Integer [Nbsp+1];
|
|
Standard_Integer* PtrSegIndex2 = new Standard_Integer [Nbsp+1];
|
|
Standard_Boolean Triok;
|
|
Standard_Integer SegIndex1,SegIndex2,SegIndex_1,SegIndex_2;
|
|
// Standard_Real ParamOn1,ParamOn2,ParamOn_1,ParamOn_2;
|
|
Standard_Real ParamOn1,ParamOn2;
|
|
Intf_PIType Type;
|
|
Standard_Integer i ;
|
|
for( i=1;i<=Nbsp;i++) {
|
|
TriIndex[i]=i;
|
|
const Intf_SectionPoint& SPnt1 = InterPP.PntValue(i);
|
|
SPnt1.InfoFirst(Type,PtrSegIndex1[i],ParamOn1);
|
|
SPnt1.InfoSecond(Type,PtrSegIndex2[i],ParamOn2);
|
|
}
|
|
|
|
|
|
do {
|
|
Triok=Standard_True;
|
|
|
|
for(Standard_Integer tr=1;tr<Nbsp;tr++) {
|
|
SegIndex1=PtrSegIndex1[TriIndex[tr]];
|
|
SegIndex_1=PtrSegIndex1[TriIndex[tr+1]];
|
|
|
|
SegIndex2=PtrSegIndex2[TriIndex[tr]];
|
|
SegIndex_2=PtrSegIndex2[TriIndex[tr+1]];
|
|
|
|
if(SegIndex1 > SegIndex_1) {
|
|
Standard_Integer q=TriIndex[tr];
|
|
TriIndex[tr]=TriIndex[tr+1];
|
|
TriIndex[tr+1]=q;
|
|
Triok=Standard_False;
|
|
}
|
|
else if(SegIndex1 == SegIndex_1) {
|
|
if(SegIndex2 > SegIndex_2) {
|
|
Standard_Integer q=TriIndex[tr];
|
|
TriIndex[tr]=TriIndex[tr+1];
|
|
TriIndex[tr+1]=q;
|
|
Triok=Standard_False;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while(Triok==Standard_False);
|
|
|
|
//-- supression des doublons Si Si !
|
|
for(i=1; i<Nbsp;i++) {
|
|
if( (PtrSegIndex1[TriIndex[i]] == PtrSegIndex1[TriIndex[i+1]])
|
|
&& (PtrSegIndex2[TriIndex[i]] == PtrSegIndex2[TriIndex[i+1]])) {
|
|
TriIndex[i]=-i;
|
|
}
|
|
}
|
|
|
|
Standard_Integer Nelarg=(Poly1.NbSegments()/20);
|
|
if(Nelarg<2) Nelarg=2;
|
|
|
|
for(Standard_Integer sp=1; sp <= Nbsp; sp++) {
|
|
if(TriIndex[sp]>0) {
|
|
const Intf_SectionPoint& SPnt = InterPP.PntValue(TriIndex[sp]);
|
|
|
|
SPnt.InfoFirst(Type,SegIndex1,ParamOn1);
|
|
SPnt.InfoSecond(Type,SegIndex2,ParamOn2);
|
|
|
|
if(Abs(SegIndex1-SegIndex2)>1) {
|
|
|
|
EIP.Perform(Poly1,Poly1,SegIndex1,SegIndex2,ParamOn1,ParamOn2);
|
|
if(EIP.NbRoots()==0) {
|
|
//-- All neighbor segments are removed
|
|
for(Standard_Integer k=sp+1;k<=Nbsp;k++) {
|
|
Standard_Integer kk=TriIndex[k];
|
|
// --- avoid negative indicies as well as in outer done
|
|
if( kk > 0 ) {
|
|
if( Abs(SegIndex1-PtrSegIndex1[kk])< Nelarg
|
|
&& Abs(SegIndex2-PtrSegIndex2[kk])< Nelarg) {
|
|
TriIndex[k]=-k;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(EIP.NbRoots()>=1) {
|
|
//--------------------------------------------------------------------
|
|
//-- It is checked if the found point is a root
|
|
//--------------------------------------------------------------------
|
|
EIP.Roots(U,V);
|
|
|
|
TheCurveTool::D1(C1,U,P1,Tan1);
|
|
TheCurveTool::D1(C1,V,P2,Tan2);
|
|
Standard_Real Dist = P1.Distance(P2);
|
|
Standard_Real EpsX1 = 10.0*TheCurveTool::EpsX(C1);
|
|
|
|
if(Abs(U-V)<=EpsX1) {
|
|
//-----------------------------------------
|
|
//-- Solution not valid
|
|
//-- The maths should have converged in a
|
|
//-- trivial solution ( point U = V )
|
|
//-----------------------------------------
|
|
Dist = TolConf+1.0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
//-- It is checked if the point (u,v) already exists
|
|
//--
|
|
done = Standard_True;
|
|
Standard_Integer nbp=NbPoints();
|
|
|
|
for(Standard_Integer p=1; p<=nbp; p++) {
|
|
const IntRes2d_IntersectionPoint& P=Point(p);
|
|
if(Abs(U-P.ParamOnFirst()) <= EpsX1) {
|
|
if(Abs(V-P.ParamOnSecond()) <= EpsX1) {
|
|
Dist = TolConf+1.0; p+=nbp;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(Dist <= TolConf) { //-- Or the point is already present
|
|
IntRes2d_Position Pos1 = IntRes2d_Middle;
|
|
IntRes2d_Position Pos2 = IntRes2d_Middle;
|
|
IntRes2d_Transition Trans1,Trans2;
|
|
//-----------------------------------------------------------------
|
|
//-- Calculate Positions of Points on the curve
|
|
//--
|
|
if(P1.Distance(DomainOnCurve1.FirstPoint())<=DomainOnCurve1.FirstTolerance())
|
|
Pos1 = IntRes2d_Head;
|
|
else if(P1.Distance(DomainOnCurve1.LastPoint())<=DomainOnCurve1.LastTolerance())
|
|
Pos1 = IntRes2d_End;
|
|
|
|
if(P2.Distance(DomainOnCurve2.FirstPoint())<=DomainOnCurve2.FirstTolerance())
|
|
Pos2 = IntRes2d_Head;
|
|
else if(P2.Distance(DomainOnCurve2.LastPoint())<=DomainOnCurve2.LastTolerance())
|
|
Pos2 = IntRes2d_End;
|
|
//-----------------------------------------------------------------
|
|
if(IntImpParGen::DetermineTransition( Pos1,Tan1,Trans1
|
|
,Pos2,Tan2,Trans2
|
|
,TolConf) == Standard_False)
|
|
{
|
|
TheCurveTool::D2(C1,U,P1,Tan1,Norm1);
|
|
TheCurveTool::D2(C1,V,P2,Tan2,Norm2);
|
|
IntImpParGen::DetermineTransition( Pos1,Tan1,Norm1,Trans1
|
|
,Pos2,Tan2,Norm2,Trans2
|
|
,TolConf);
|
|
}
|
|
IntRes2d_IntersectionPoint IP(P1,U,V,Trans1,Trans2,Standard_False);
|
|
Insert(IP);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
delete [] TriIndex;
|
|
delete [] PtrSegIndex1;
|
|
delete [] PtrSegIndex2;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
//-- Processing of TangentZone
|
|
//----------------------------------------------------------------------
|
|
Standard_Integer Nbtz = InterPP.NbTangentZones();
|
|
for(Standard_Integer tz=1; tz <= Nbtz; tz++) {
|
|
Standard_Integer NbPnts = InterPP.ZoneValue(tz).NumberOfPoints();
|
|
//====================================================================
|
|
//== Find the first and the last point in the tangency zone.
|
|
//====================================================================
|
|
Standard_Real ParamSupOnCurve2,ParamInfOnCurve2;
|
|
Standard_Real ParamSupOnCurve1,ParamInfOnCurve1;
|
|
// Standard_Integer SegIndex,SegIndex1onP1,SegIndex1onP2,SegIndex2onP1,SegIndex2onP2;
|
|
Standard_Integer SegIndex1onP1,SegIndex1onP2;
|
|
Intf_PIType Type;
|
|
Standard_Real ParamOnLine;
|
|
Standard_Real PolyUInf,PolyUSup,PolyVInf,PolyVSup;
|
|
ParamSupOnCurve2=ParamSupOnCurve1=PolyUSup=PolyVSup=-RealLast();
|
|
ParamInfOnCurve2=ParamInfOnCurve1=PolyUInf=PolyVInf= RealLast();
|
|
for(Standard_Integer qq=1;qq<=NbPnts;qq++) {
|
|
const Intf_SectionPoint& SPnt1 = InterPP.ZoneValue(tz).GetPoint(qq);
|
|
//====================================================================
|
|
//== The zones of tangency are discretized
|
|
//== Test of stop : Check if
|
|
//== (Deflection < Tolerance)
|
|
//== Or (Sample < EpsX) (normally the first condition is
|
|
//== more strict)
|
|
//====================================================================
|
|
// Standard_Real _PolyUInf,_PolyUSup,_PolyVInf,_PolyVSup;
|
|
Standard_Real _PolyUInf,_PolyVInf;
|
|
|
|
SPnt1.InfoFirst(Type,SegIndex1onP1,ParamOnLine);
|
|
if(SegIndex1onP1 > Poly1.NbSegments()) { SegIndex1onP1--; ParamOnLine = 1.0; }
|
|
if(SegIndex1onP1 <= 0) { SegIndex1onP1=1; ParamOnLine = 0.0; }
|
|
_PolyUInf = Poly1.ApproxParamOnCurve(SegIndex1onP1,ParamOnLine);
|
|
|
|
SPnt1.InfoSecond(Type,SegIndex1onP2,ParamOnLine);
|
|
if(SegIndex1onP2 > Poly1.NbSegments()) { SegIndex1onP2--; ParamOnLine = 1.0; }
|
|
if(SegIndex1onP2 <= 0) { SegIndex1onP2=1; ParamOnLine = 0.0; }
|
|
_PolyVInf = Poly1.ApproxParamOnCurve(SegIndex1onP2,ParamOnLine);
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
if(ParamInfOnCurve1 > _PolyUInf) ParamInfOnCurve1=_PolyUInf;
|
|
if(ParamInfOnCurve2 > _PolyVInf) ParamInfOnCurve2=_PolyVInf;
|
|
|
|
if(ParamSupOnCurve1 < _PolyUInf) ParamSupOnCurve1=_PolyUInf;
|
|
if(ParamSupOnCurve2 < _PolyVInf) ParamSupOnCurve2=_PolyVInf;
|
|
}
|
|
|
|
PolyUInf= ParamInfOnCurve1;
|
|
PolyUSup= ParamSupOnCurve1;
|
|
PolyVInf= ParamInfOnCurve2;
|
|
PolyVSup= ParamSupOnCurve2;
|
|
|
|
TheCurveTool::D0(C1,PolyUInf,P1);
|
|
TheCurveTool::D0(C1,PolyVInf,P2);
|
|
Standard_Real distmemesens = P1.SquareDistance(P2);
|
|
TheCurveTool::D0(C1,PolyVSup,P2);
|
|
Standard_Real distdiffsens = P1.SquareDistance(P2);
|
|
if(distmemesens > distdiffsens) {
|
|
Standard_Real qwerty=PolyVInf; PolyVInf=PolyVSup; PolyVSup=qwerty;
|
|
}
|
|
|
|
//-----------------------------------------------------------------
|
|
//-- Calculate Positions of Points on the curve and
|
|
//-- Transitions on each limit of the segment
|
|
|
|
IntRes2d_Position Pos1 = IntRes2d_Middle;
|
|
IntRes2d_Position Pos2 = IntRes2d_Middle;
|
|
IntRes2d_Transition Trans1,Trans2;
|
|
|
|
TheCurveTool::D1(C1,PolyUInf,P1,Tan1);
|
|
TheCurveTool::D1(C1,PolyVInf,P2,Tan2);
|
|
|
|
if(P1.Distance(DomainOnCurve1.FirstPoint())<=DomainOnCurve1.FirstTolerance()) {
|
|
Pos1 = IntRes2d_Head;
|
|
}
|
|
else if(P1.Distance(DomainOnCurve1.LastPoint())<=DomainOnCurve1.LastTolerance()) {
|
|
Pos1 = IntRes2d_End;
|
|
}
|
|
if(P2.Distance(DomainOnCurve2.FirstPoint())<=DomainOnCurve2.FirstTolerance()) {
|
|
Pos2 = IntRes2d_Head;
|
|
}
|
|
else if(P2.Distance(DomainOnCurve2.LastPoint())<=DomainOnCurve2.LastTolerance()) {
|
|
Pos2 = IntRes2d_End;
|
|
}
|
|
|
|
if(Pos1==IntRes2d_Middle && Pos2!=IntRes2d_Middle) {
|
|
PolyUInf=TheProjPCur::FindParameter( C1,P2,D1.FirstParameter(),D1.LastParameter(),TheCurveTool::EpsX(C1));
|
|
}
|
|
else if(Pos1!=IntRes2d_Middle && Pos2==IntRes2d_Middle) {
|
|
PolyVInf=TheProjPCur::FindParameter( C1,P1,D1.FirstParameter(),D1.LastParameter(),TheCurveTool::EpsX(C1));
|
|
}
|
|
else if(Abs(ParamInfOnCurve1-ParamSupOnCurve1) > Abs(ParamInfOnCurve2-ParamSupOnCurve2)) {
|
|
PolyVInf=TheProjPCur::FindParameter( C1,P1,D1.FirstParameter(),D1.LastParameter(),TheCurveTool::EpsX(C1));
|
|
}
|
|
else {
|
|
PolyUInf=TheProjPCur::FindParameter( C1,P2,D1.FirstParameter(),D1.LastParameter(),TheCurveTool::EpsX(C1));
|
|
}
|
|
|
|
|
|
|
|
if(IntImpParGen::DetermineTransition( Pos1,Tan1,Trans1,Pos2,Tan2,Trans2,TolConf)
|
|
== Standard_False)
|
|
{
|
|
TheCurveTool::D2(C1,PolyUInf,P1,Tan1,Norm1);
|
|
TheCurveTool::D2(C1,PolyVInf,P2,Tan2,Norm2);
|
|
IntImpParGen::DetermineTransition(Pos1,Tan1,Norm1,Trans1,
|
|
Pos2,Tan2,Norm2,Trans2,TolConf);
|
|
}
|
|
IntRes2d_IntersectionPoint PtSeg1(P1,PolyUInf,PolyVInf
|
|
,Trans1,Trans2,Standard_False);
|
|
//----------------------------------------------------------------------
|
|
|
|
if((Abs(PolyUInf-PolyUSup) <= TheCurveTool::EpsX(C1)) ||
|
|
(Abs(PolyVInf-PolyVSup) <= TheCurveTool::EpsX(C1)))
|
|
{
|
|
//bad segment
|
|
}
|
|
else
|
|
{
|
|
TheCurveTool::D1(C1,PolyUSup,P1,Tan1);
|
|
TheCurveTool::D1(C1,PolyVSup,P2,Tan2);
|
|
Pos1 = IntRes2d_Middle; Pos2 = IntRes2d_Middle;
|
|
|
|
if(P1.Distance(DomainOnCurve1.FirstPoint())<=DomainOnCurve1.FirstTolerance()) {
|
|
Pos1 = IntRes2d_Head;
|
|
}
|
|
else if(P1.Distance(DomainOnCurve1.LastPoint())<=DomainOnCurve1.LastTolerance()) {
|
|
Pos1 = IntRes2d_End;
|
|
}
|
|
if(P2.Distance(DomainOnCurve2.FirstPoint())<=DomainOnCurve2.FirstTolerance()) {
|
|
Pos2 = IntRes2d_Head;
|
|
}
|
|
else if(P2.Distance(DomainOnCurve2.LastPoint())<=DomainOnCurve2.LastTolerance()) {
|
|
Pos2 = IntRes2d_End;
|
|
}
|
|
|
|
|
|
if(Pos1==IntRes2d_Middle && Pos2!=IntRes2d_Middle) {
|
|
PolyUSup=TheProjPCur::FindParameter( C1,P2,D1.FirstParameter(),D1.LastParameter(),TheCurveTool::EpsX(C1));
|
|
}
|
|
else if(Pos1!=IntRes2d_Middle && Pos2==IntRes2d_Middle) {
|
|
PolyVSup=TheProjPCur::FindParameter( C1,P1,D1.FirstParameter(),D1.LastParameter(),TheCurveTool::EpsX(C1));
|
|
}
|
|
else if(Abs(ParamInfOnCurve1-ParamSupOnCurve1) > Abs(ParamInfOnCurve2-ParamSupOnCurve2)) {
|
|
PolyVSup=TheProjPCur::FindParameter( C1,P1,D1.FirstParameter(),D1.LastParameter(),TheCurveTool::EpsX(C1));
|
|
}
|
|
else {
|
|
PolyUSup=TheProjPCur::FindParameter( C1,P2,D1.FirstParameter(),D1.LastParameter(),TheCurveTool::EpsX(C1));
|
|
}
|
|
|
|
if(IntImpParGen::DetermineTransition( Pos1,Tan1,Trans1,Pos2,Tan2,Trans2,TolConf)
|
|
==Standard_False) {
|
|
TheCurveTool::D2(C1,PolyUSup,P1,Tan1,Norm1);
|
|
TheCurveTool::D2(C1,PolyVSup,P2,Tan2,Norm2);
|
|
IntImpParGen::DetermineTransition(Pos1,Tan1,Norm1,Trans1,
|
|
Pos2,Tan2,Norm2,Trans2,TolConf);
|
|
}
|
|
IntRes2d_IntersectionPoint PtSeg2(P1,PolyUSup,PolyVSup
|
|
,Trans1,Trans2,Standard_False);
|
|
|
|
Standard_Boolean Oppos = (Tan1.Dot(Tan2) > 0.0)? Standard_False : Standard_True;
|
|
if(ParamInfOnCurve1 > ParamSupOnCurve1) {
|
|
IntRes2d_IntersectionSegment Seg(PtSeg2,PtSeg1,Oppos,Standard_False);
|
|
Append(Seg);
|
|
}
|
|
else {
|
|
IntRes2d_IntersectionSegment Seg(PtSeg1,PtSeg2,Oppos,Standard_False);
|
|
Append(Seg);
|
|
}
|
|
}
|
|
} //end of processing of TangentZone
|
|
|
|
done = Standard_True;
|
|
}
|
|
|
|
|
|
Standard_Boolean HeadOrEndPoint( const IntRes2d_Domain& D1
|
|
,const TheCurve& C1
|
|
,const Standard_Real tu
|
|
,const IntRes2d_Domain& D2
|
|
,const TheCurve& C2
|
|
,const Standard_Real tv
|
|
,const Standard_Real TolConf
|
|
,IntRes2d_IntersectionPoint& IntPt
|
|
,Standard_Boolean& HeadOn1
|
|
,Standard_Boolean& HeadOn2
|
|
,Standard_Boolean& EndOn1
|
|
,Standard_Boolean& EndOn2
|
|
,Standard_Integer PosSegment) {
|
|
|
|
gp_Pnt2d P1,P2,SP1,SP2;
|
|
gp_Vec2d T1,T2,N1,N2;
|
|
Standard_Real u=tu;
|
|
Standard_Real v=tv;
|
|
Standard_Real svu = u;
|
|
Standard_Real svv = v;
|
|
|
|
TheCurveTool::D1(C1,u,P1,T1);
|
|
TheCurveTool::D1(C2,v,P2,T2);
|
|
|
|
IntRes2d_Position Pos1 = IntRes2d_Middle;
|
|
IntRes2d_Position Pos2 = IntRes2d_Middle;
|
|
IntRes2d_Transition Trans1,Trans2;
|
|
|
|
//----------------------------------------------------------------------
|
|
//-- Head On 1 : Head1 <-> P2
|
|
if(P2.Distance(D1.FirstPoint())<=D1.FirstTolerance()) {
|
|
Pos1 = IntRes2d_Head;
|
|
HeadOn1 = Standard_True;
|
|
SP1 = D1.FirstPoint();
|
|
u = D1.FirstParameter();
|
|
}
|
|
//----------------------------------------------------------------------
|
|
//-- End On 1 : End1 <-> P2
|
|
else if(P2.Distance(D1.LastPoint())<=D1.LastTolerance()) {
|
|
Pos1 = IntRes2d_End;
|
|
EndOn1 = Standard_True;
|
|
SP1 = D1.LastPoint();
|
|
u = D1.LastParameter();
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
//-- Head On 2 : Head2 <-> P1
|
|
else if(P1.Distance(D2.FirstPoint())<=D2.FirstTolerance()) {
|
|
Pos2 = IntRes2d_Head;
|
|
HeadOn2 = Standard_True;
|
|
SP2 = D2.FirstPoint();
|
|
v = D2.FirstParameter();
|
|
}
|
|
//----------------------------------------------------------------------
|
|
//-- End On 2 : End2 <-> P1
|
|
else if(P1.Distance(D2.LastPoint())<=D2.LastTolerance()) {
|
|
Pos2 = IntRes2d_End;
|
|
EndOn2 = Standard_True;
|
|
SP2 = D2.LastPoint();
|
|
v = D2.LastParameter();
|
|
}
|
|
|
|
Standard_Real EpsX1 = TheCurveTool::EpsX(C1);
|
|
Standard_Real EpsX2 = TheCurveTool::EpsX(C2);
|
|
|
|
if((Pos1 != IntRes2d_Middle)||(Pos2 != IntRes2d_Middle)) {
|
|
if(Pos1 == IntRes2d_Middle) {
|
|
if(Abs(u-D1.FirstParameter()) <= EpsX1) {
|
|
Pos1 = IntRes2d_Head;
|
|
P1 = D1.FirstPoint();
|
|
HeadOn1 = Standard_True;
|
|
}
|
|
else if(Abs(u-D1.LastParameter()) <= EpsX1) {
|
|
Pos1 = IntRes2d_End;
|
|
P1 = D1.LastPoint();
|
|
EndOn1 = Standard_True;
|
|
}
|
|
}
|
|
else if(u!=tu) {
|
|
P1 = SP1;
|
|
}
|
|
|
|
|
|
if(Pos2 == IntRes2d_Middle) {
|
|
if(Abs(v-D2.FirstParameter()) <= EpsX2) {
|
|
Pos2 = IntRes2d_Head;
|
|
HeadOn2 = Standard_True;
|
|
P2 = D2.FirstPoint();
|
|
if(Pos1 != IntRes2d_Middle) {
|
|
P1.SetCoord(0.5*(P1.X()+P2.X()),0.5*(P1.Y()+P2.Y()));
|
|
}
|
|
else {
|
|
P2 = P1;
|
|
}
|
|
}
|
|
else if(Abs(v-D2.LastParameter()) <= EpsX2) {
|
|
Pos2 = IntRes2d_End;
|
|
EndOn2 = Standard_True;
|
|
P2 = D2.LastPoint();
|
|
if(Pos1 != IntRes2d_Middle) {
|
|
P1.SetCoord(0.5*(P1.X()+P2.X()),0.5*(P1.Y()+P2.Y()));
|
|
}
|
|
else {
|
|
P2 = P1;
|
|
}
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
//-- It is tested if a point at the end of segment already has its transitions
|
|
//-- If Yes, the new point is not created
|
|
//--
|
|
//-- PosSegment = 1 if Head Head
|
|
//-- 2 if Head End
|
|
//-- 4 if End Head
|
|
//-- 8 if End End
|
|
//--------------------------------------------------------------------
|
|
if(Pos1 == IntRes2d_Head) {
|
|
if((Pos2 == IntRes2d_Head)&&(PosSegment & 1)) return(Standard_False);
|
|
if((Pos2 == IntRes2d_End )&&(PosSegment & 2)) return(Standard_False);
|
|
}
|
|
else if(Pos1 == IntRes2d_End) {
|
|
if((Pos2 == IntRes2d_Head)&&(PosSegment & 4)) return(Standard_False);
|
|
if((Pos2 == IntRes2d_End )&&(PosSegment & 8)) return(Standard_False);
|
|
}
|
|
|
|
|
|
if(IntImpParGen::DetermineTransition( Pos1,T1,Trans1,Pos2,T2,Trans2,TolConf)
|
|
== Standard_False) {
|
|
TheCurveTool::D2(C1,svu,P1,T1,N1);
|
|
TheCurveTool::D2(C2,svv,P2,T2,N2);
|
|
IntImpParGen::DetermineTransition(Pos1,T1,N1,Trans1,
|
|
Pos2,T2,N2,Trans2,TolConf);
|
|
}
|
|
IntPt.SetValues(P1,u,v,Trans1,Trans2,Standard_False);
|
|
return(Standard_True);
|
|
}
|
|
else
|
|
return(Standard_False);
|
|
}
|
|
|
|
//=======================================================================
|
|
//function : Perform
|
|
//purpose : Base method to perform polyline / polyline intersection for
|
|
// pair of curves.
|
|
//=======================================================================
|
|
void IntCurve_IntPolyPolyGen::Perform(const TheCurve& C1,
|
|
const IntRes2d_Domain& D1,
|
|
const TheCurve& C2,
|
|
const IntRes2d_Domain& D2,
|
|
const Standard_Real TolConf,
|
|
const Standard_Real Tol,
|
|
const Standard_Integer NbIter,
|
|
const Standard_Real DeltaU,
|
|
const Standard_Real DeltaV)
|
|
{
|
|
Standard_Integer nbsamplesOnC1,nbsamplesOnC2;
|
|
done = Standard_False;
|
|
|
|
if(NbIter>NBITER_MAX_POLYGON) return;
|
|
|
|
// Number of samples tunning.
|
|
nbsamplesOnC1 = TheCurveTool::NbSamples(C1,D1.FirstParameter(),D1.LastParameter());
|
|
nbsamplesOnC2 = TheCurveTool::NbSamples(C2,D2.FirstParameter(),D2.LastParameter());
|
|
|
|
if (NbIter == 0)
|
|
{
|
|
// Minimal number of points.
|
|
nbsamplesOnC1 = Max(nbsamplesOnC1, myMinPntNb);
|
|
nbsamplesOnC2 = Max(nbsamplesOnC2, myMinPntNb);
|
|
}
|
|
else
|
|
{
|
|
// Increase number of samples in second and next iterations.
|
|
nbsamplesOnC1=(5 * (nbsamplesOnC1 * NbIter)) / 4;
|
|
nbsamplesOnC2=(5 * (nbsamplesOnC2 * NbIter)) / 4;
|
|
}
|
|
|
|
NCollection_Handle<IntCurve_ThePolygon2d>
|
|
aPoly1 = new IntCurve_ThePolygon2d(C1,nbsamplesOnC1,D1,Tol),
|
|
aPoly2 = new IntCurve_ThePolygon2d(C2,nbsamplesOnC2,D2,Tol);
|
|
|
|
if( (aPoly1->DeflectionOverEstimation() > TolConf) &&
|
|
(aPoly2->DeflectionOverEstimation() > TolConf))
|
|
{
|
|
const Standard_Real aDeflectionSum =
|
|
Max(aPoly1->DeflectionOverEstimation(), TolConf) +
|
|
Max(aPoly2->DeflectionOverEstimation(), TolConf);
|
|
|
|
if (nbsamplesOnC2 > nbsamplesOnC1)
|
|
{
|
|
aPoly2->ComputeWithBox(C2, aPoly1->Bounding());
|
|
aPoly1->SetDeflectionOverEstimation(aDeflectionSum);
|
|
aPoly1->ComputeWithBox(C1, aPoly2->Bounding());
|
|
}
|
|
else
|
|
{
|
|
aPoly1->ComputeWithBox(C1, aPoly2->Bounding());
|
|
aPoly2->SetDeflectionOverEstimation(aDeflectionSum);
|
|
aPoly2->ComputeWithBox(C2, aPoly1->Bounding());
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
//-- if the deflection less then the Tolerance of Confusion
|
|
//-- Then the deflection of the polygon is set in TolConf
|
|
//-- (Detection of Tangency Zones)
|
|
//----------------------------------------------------------------------
|
|
|
|
if(aPoly1->DeflectionOverEstimation() < TolConf) {
|
|
aPoly1->SetDeflectionOverEstimation(TolConf);
|
|
}
|
|
if(aPoly2->DeflectionOverEstimation() < TolConf) {
|
|
aPoly2->SetDeflectionOverEstimation(TolConf);
|
|
}
|
|
// for case when a few polygon points were replaced by line
|
|
// if exact solution was not found
|
|
// then search of precise solution will be repeated
|
|
// for polygon contains all initial points
|
|
// secondary search will be performed only for case when initial points
|
|
// were dropped
|
|
Standard_Boolean isFullRepresentation = ( aPoly1->NbSegments() == nbsamplesOnC1 &&
|
|
aPoly2->NbSegments() == nbsamplesOnC2 );
|
|
|
|
if( !findIntersect( C1, D1, C2, D2, TolConf, Tol, NbIter,
|
|
DeltaU, DeltaV, *aPoly1, *aPoly2, isFullRepresentation ) && !isFullRepresentation )
|
|
{
|
|
if(aPoly1->NbSegments() < nbsamplesOnC1)
|
|
{
|
|
aPoly1 = new IntCurve_ThePolygon2d(C1,nbsamplesOnC1,D1,Tol);
|
|
}
|
|
if(aPoly2->NbSegments() < nbsamplesOnC2)
|
|
{
|
|
aPoly2 = new IntCurve_ThePolygon2d(C2,nbsamplesOnC2,D2,Tol);
|
|
}
|
|
|
|
findIntersect( C1, D1, C2, D2, TolConf, Tol, NbIter,
|
|
DeltaU, DeltaV, *aPoly1, *aPoly2,
|
|
Standard_True);
|
|
|
|
}
|
|
|
|
done = Standard_True;
|
|
}
|
|
|
|
//======================================================================
|
|
// Purpose : findIntersect
|
|
//======================================================================
|
|
|
|
Standard_Boolean IntCurve_IntPolyPolyGen::findIntersect(
|
|
const TheCurve& C1,
|
|
const IntRes2d_Domain& D1,
|
|
const TheCurve& C2,
|
|
const IntRes2d_Domain& D2,
|
|
const Standard_Real TolConf,
|
|
const Standard_Real Tol,
|
|
const Standard_Integer NbIter,
|
|
const Standard_Real DeltaU,
|
|
const Standard_Real DeltaV,
|
|
const IntCurve_ThePolygon2d& thePoly1,
|
|
const IntCurve_ThePolygon2d& thePoly2,
|
|
Standard_Boolean isFullPolygon )
|
|
{
|
|
|
|
gp_Vec2d Tan1,Tan2,Norm1,Norm2;
|
|
gp_Pnt2d P1,P2;
|
|
Intf_InterferencePolygon2d InterPP(thePoly1,thePoly2);
|
|
IntCurve_ExactIntersectionPoint EIP(C1,C2,TolConf);
|
|
Standard_Real U = 0., V = 0.;
|
|
Standard_Boolean AnErrorOccurred = Standard_False;
|
|
done = Standard_True; // To prevent exception in nbp=NbPoints();
|
|
//----------------------------------------------------------------------
|
|
//-- Processing of SectionPoint
|
|
//----------------------------------------------------------------------
|
|
Standard_Integer Nbsp = InterPP.NbSectionPoints();
|
|
for(Standard_Integer sp=1; sp <= Nbsp; sp++) {
|
|
const Intf_SectionPoint& SPnt = InterPP.PntValue(sp);
|
|
Standard_Integer SegIndex1,SegIndex2;
|
|
Standard_Real ParamOn1,ParamOn2;
|
|
Intf_PIType Type;
|
|
|
|
SPnt.InfoFirst(Type,SegIndex1,ParamOn1);
|
|
SPnt.InfoSecond(Type,SegIndex2,ParamOn2);
|
|
EIP.Perform(thePoly1,thePoly2,SegIndex1,SegIndex2,ParamOn1,ParamOn2);
|
|
AnErrorOccurred = EIP.AnErrorOccurred();
|
|
|
|
if( !EIP.NbRoots() && !isFullPolygon)
|
|
return Standard_False;
|
|
|
|
if(AnErrorOccurred)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
//-- It is checked if the found point is really a root
|
|
//--------------------------------------------------------------------
|
|
|
|
EIP.Roots(U,V);
|
|
TheCurveTool::D1(C1,U,P1,Tan1);
|
|
TheCurveTool::D1(C2,V,P2,Tan2);
|
|
Standard_Real Dist = P1.Distance(P2);
|
|
if(EIP.NbRoots() == 0 && Dist > TolConf)
|
|
{
|
|
IntRes2d_Transition aTrans;
|
|
IntRes2d_IntersectionPoint aPInt(P1, U, V, aTrans, aTrans, Standard_False);
|
|
Standard_Real aT1f, aT1l, aT2f, aT2l;
|
|
aT1f= thePoly1.ApproxParamOnCurve(SegIndex1, 0.0);
|
|
aT1l= thePoly1.ApproxParamOnCurve(SegIndex1, 1.0);
|
|
aT2f= thePoly2.ApproxParamOnCurve(SegIndex2, 0.0);
|
|
aT2l= thePoly2.ApproxParamOnCurve(SegIndex2, 1.0);
|
|
//
|
|
Standard_Integer aMaxCount = 16, aCount = 0;
|
|
GetIntersection(C1, aT1f, aT1l, C2, aT2f, aT2l, TolConf, aMaxCount,
|
|
aPInt, Dist, aCount);
|
|
U = aPInt.ParamOnFirst();
|
|
V = aPInt.ParamOnSecond();
|
|
TheCurveTool::D1(C1,U,P1,Tan1);
|
|
TheCurveTool::D1(C2,V,P2,Tan2);
|
|
Dist = P1.Distance(P2);
|
|
}
|
|
//-----------------------------------------------------------------
|
|
//-- It is checked if the point (u,v) does not exist already
|
|
//--
|
|
Standard_Integer nbp=NbPoints();
|
|
Standard_Real EpsX1 = 10.0*TheCurveTool::EpsX(C1);
|
|
Standard_Real EpsX2 = 10.0*TheCurveTool::EpsX(C2);
|
|
for(Standard_Integer p=1; p<=nbp; p++) {
|
|
const IntRes2d_IntersectionPoint& P=Point(p);
|
|
if(Abs(U-P.ParamOnFirst()) <= EpsX1) {
|
|
if(Abs(V-P.ParamOnSecond()) <= EpsX2) {
|
|
Dist = TolConf+1.0; p+=nbp;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(Dist <= TolConf) { //-- Or the point is already present
|
|
IntRes2d_Position Pos1 = IntRes2d_Middle;
|
|
IntRes2d_Position Pos2 = IntRes2d_Middle;
|
|
IntRes2d_Transition Trans1,Trans2;
|
|
//-----------------------------------------------------------------
|
|
//-- Calculate the Positions of Points on the curve
|
|
//--
|
|
if(P1.Distance(DomainOnCurve1.FirstPoint())<=DomainOnCurve1.FirstTolerance())
|
|
Pos1 = IntRes2d_Head;
|
|
else if(P1.Distance(DomainOnCurve1.LastPoint())<=DomainOnCurve1.LastTolerance())
|
|
Pos1 = IntRes2d_End;
|
|
|
|
if(P2.Distance(DomainOnCurve2.FirstPoint())<=DomainOnCurve2.FirstTolerance())
|
|
Pos2 = IntRes2d_Head;
|
|
else if(P2.Distance(DomainOnCurve2.LastPoint())<=DomainOnCurve2.LastTolerance())
|
|
Pos2 = IntRes2d_End;
|
|
//-----------------------------------------------------------------
|
|
//-- Calculate the Transitions (see IntImpParGen.cxx)
|
|
//--
|
|
if(IntImpParGen::DetermineTransition (Pos1, Tan1, Trans1, Pos2, Tan2, Trans2, TolConf) == Standard_False) {
|
|
TheCurveTool::D2(C1,U,P1,Tan1,Norm1);
|
|
TheCurveTool::D2(C2,V,P2,Tan2,Norm2);
|
|
IntImpParGen::DetermineTransition (Pos1, Tan1, Norm1, Trans1, Pos2, Tan2, Norm2, Trans2, TolConf);
|
|
}
|
|
IntRes2d_IntersectionPoint IP(P1,U,V,Trans1,Trans2,Standard_False);
|
|
Insert(IP);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
//-- Processing of TangentZone
|
|
//----------------------------------------------------------------------
|
|
Standard_Integer Nbtz = InterPP.NbTangentZones();
|
|
for(Standard_Integer tz=1; tz <= Nbtz; tz++) {
|
|
Standard_Integer NbPnts = InterPP.ZoneValue(tz).NumberOfPoints();
|
|
//====================================================================
|
|
//== Find the first and the last point in the tangency zone.
|
|
//====================================================================
|
|
Standard_Real ParamSupOnCurve2,ParamInfOnCurve2;
|
|
Standard_Real ParamSupOnCurve1,ParamInfOnCurve1;
|
|
// Standard_Integer SegIndex,SegIndex1onP1,SegIndex1onP2,SegIndex2onP1,SegIndex2onP2;
|
|
Standard_Integer SegIndex1onP1,SegIndex1onP2;
|
|
Intf_PIType Type;
|
|
Standard_Real ParamOnLine;
|
|
Standard_Real PolyUInf,PolyUSup,PolyVInf,PolyVSup;
|
|
ParamSupOnCurve2=ParamSupOnCurve1=PolyUSup=PolyVSup=-RealLast();
|
|
ParamInfOnCurve2=ParamInfOnCurve1=PolyUInf=PolyVInf= RealLast();
|
|
for(Standard_Integer qq=1;qq<=NbPnts;qq++) {
|
|
const Intf_SectionPoint& SPnt1 = InterPP.ZoneValue(tz).GetPoint(qq);
|
|
//====================================================================
|
|
//== The zones of tangency are discretized
|
|
//== Test of stop : Check if
|
|
//== (Deflection < Tolerance)
|
|
//== Or (Sample < EpsX) (normally the first condition is
|
|
//== more strict)
|
|
//====================================================================
|
|
// Standard_Real _PolyUInf,_PolyUSup,_PolyVInf,_PolyVSup;
|
|
Standard_Real _PolyUInf,_PolyVInf;
|
|
|
|
SPnt1.InfoFirst(Type,SegIndex1onP1,ParamOnLine);
|
|
if(SegIndex1onP1 > thePoly1.NbSegments()) { SegIndex1onP1--; ParamOnLine = 1.0; }
|
|
if(SegIndex1onP1 <= 0) { SegIndex1onP1=1; ParamOnLine = 0.0; }
|
|
_PolyUInf = thePoly1.ApproxParamOnCurve(SegIndex1onP1,ParamOnLine);
|
|
|
|
SPnt1.InfoSecond(Type,SegIndex1onP2,ParamOnLine);
|
|
if(SegIndex1onP2 > thePoly2.NbSegments()) { SegIndex1onP2--; ParamOnLine = 1.0; }
|
|
if(SegIndex1onP2 <= 0) { SegIndex1onP2=1; ParamOnLine = 0.0; }
|
|
_PolyVInf = thePoly2.ApproxParamOnCurve(SegIndex1onP2,ParamOnLine);
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
if(ParamInfOnCurve1 > _PolyUInf) ParamInfOnCurve1=_PolyUInf;
|
|
if(ParamInfOnCurve2 > _PolyVInf) ParamInfOnCurve2=_PolyVInf;
|
|
|
|
if(ParamSupOnCurve1 < _PolyUInf) ParamSupOnCurve1=_PolyUInf;
|
|
if(ParamSupOnCurve2 < _PolyVInf) ParamSupOnCurve2=_PolyVInf;
|
|
}
|
|
|
|
PolyUInf= ParamInfOnCurve1;
|
|
PolyUSup= ParamSupOnCurve1;
|
|
PolyVInf= ParamInfOnCurve2;
|
|
PolyVSup= ParamSupOnCurve2;
|
|
|
|
TheCurveTool::D0(C1,PolyUInf,P1);
|
|
TheCurveTool::D0(C2,PolyVInf,P2);
|
|
Standard_Real distmemesens = P1.SquareDistance(P2);
|
|
TheCurveTool::D0(C2,PolyVSup,P2);
|
|
Standard_Real distdiffsens = P1.SquareDistance(P2);
|
|
if(distmemesens > distdiffsens) {
|
|
Standard_Real qwerty=PolyVInf; PolyVInf=PolyVSup; PolyVSup=qwerty;
|
|
}
|
|
|
|
if( ( (thePoly1.DeflectionOverEstimation() > TolConf)
|
|
||(thePoly2.DeflectionOverEstimation() > TolConf))
|
|
&&(NbIter<NBITER_MAX_POLYGON)) {
|
|
|
|
IntRes2d_Domain RecursD1( TheCurveTool::Value(C1,ParamInfOnCurve1)
|
|
,ParamInfOnCurve1,TolConf
|
|
,TheCurveTool::Value(C1,ParamSupOnCurve1)
|
|
,ParamSupOnCurve1,TolConf);
|
|
IntRes2d_Domain RecursD2( TheCurveTool::Value(C2,ParamInfOnCurve2)
|
|
,ParamInfOnCurve2,TolConf
|
|
,TheCurveTool::Value(C2,ParamSupOnCurve2)
|
|
,ParamSupOnCurve2,TolConf);
|
|
//-- thePoly1(2) are not deleted,
|
|
//-- finally they are destroyed.
|
|
//-- !! No untimely return !!
|
|
Perform(C1,RecursD1,C2,RecursD2,Tol,TolConf,NbIter+1,DeltaU,DeltaV);
|
|
}
|
|
else {
|
|
//-----------------------------------------------------------------
|
|
//-- Calculate Positions of Points on the curve and
|
|
//-- Transitions on each limit of the segment
|
|
|
|
IntRes2d_Position Pos1 = IntRes2d_Middle;
|
|
IntRes2d_Position Pos2 = IntRes2d_Middle;
|
|
IntRes2d_Transition Trans1,Trans2;
|
|
|
|
TheCurveTool::D1(C1,PolyUInf,P1,Tan1);
|
|
TheCurveTool::D1(C2,PolyVInf,P2,Tan2);
|
|
|
|
if(P1.Distance(DomainOnCurve1.FirstPoint())<=DomainOnCurve1.FirstTolerance()) {
|
|
Pos1 = IntRes2d_Head;
|
|
}
|
|
else if(P1.Distance(DomainOnCurve1.LastPoint())<=DomainOnCurve1.LastTolerance()) {
|
|
Pos1 = IntRes2d_End;
|
|
}
|
|
if(P2.Distance(DomainOnCurve2.FirstPoint())<=DomainOnCurve2.FirstTolerance()) {
|
|
Pos2 = IntRes2d_Head;
|
|
}
|
|
else if(P2.Distance(DomainOnCurve2.LastPoint())<=DomainOnCurve2.LastTolerance()) {
|
|
Pos2 = IntRes2d_End;
|
|
}
|
|
|
|
if(Pos1==IntRes2d_Middle && Pos2!=IntRes2d_Middle) {
|
|
PolyUInf=TheProjPCur::FindParameter( C1,P2,D1.FirstParameter(),D1.LastParameter(),TheCurveTool::EpsX(C1));
|
|
}
|
|
else if(Pos1!=IntRes2d_Middle && Pos2==IntRes2d_Middle) {
|
|
PolyVInf=TheProjPCur::FindParameter( C2,P1,D2.FirstParameter(),D2.LastParameter(),TheCurveTool::EpsX(C2));
|
|
}
|
|
else if(Abs(ParamInfOnCurve1-ParamSupOnCurve1) > Abs(ParamInfOnCurve2-ParamSupOnCurve2)) {
|
|
PolyVInf=TheProjPCur::FindParameter( C2,P1,D2.FirstParameter(),D2.LastParameter(),TheCurveTool::EpsX(C2));
|
|
}
|
|
else {
|
|
PolyUInf=TheProjPCur::FindParameter( C1,P2,D1.FirstParameter(),D1.LastParameter(),TheCurveTool::EpsX(C1));
|
|
}
|
|
|
|
|
|
|
|
if(IntImpParGen::DetermineTransition( Pos1,Tan1,Trans1,Pos2,Tan2,Trans2,TolConf)
|
|
== Standard_False)
|
|
{
|
|
TheCurveTool::D2(C1,PolyUInf,P1,Tan1,Norm1);
|
|
TheCurveTool::D2(C2,PolyVInf,P2,Tan2,Norm2);
|
|
IntImpParGen::DetermineTransition(Pos1,Tan1,Norm1,Trans1,
|
|
Pos2,Tan2,Norm2,Trans2,TolConf);
|
|
}
|
|
IntRes2d_IntersectionPoint PtSeg1(P1,PolyUInf,PolyVInf
|
|
,Trans1,Trans2,Standard_False);
|
|
//----------------------------------------------------------------------
|
|
|
|
if((Abs(PolyUInf-PolyUSup) <= TheCurveTool::EpsX(C1))
|
|
|| (Abs(PolyVInf-PolyVSup) <= TheCurveTool::EpsX(C2)))
|
|
{
|
|
Insert(PtSeg1);
|
|
}
|
|
else
|
|
{
|
|
TheCurveTool::D1(C1,PolyUSup,P1,Tan1);
|
|
TheCurveTool::D1(C2,PolyVSup,P2,Tan2);
|
|
Pos1 = IntRes2d_Middle; Pos2 = IntRes2d_Middle;
|
|
|
|
if(P1.Distance(DomainOnCurve1.FirstPoint())<=DomainOnCurve1.FirstTolerance()) {
|
|
Pos1 = IntRes2d_Head;
|
|
}
|
|
else if(P1.Distance(DomainOnCurve1.LastPoint())<=DomainOnCurve1.LastTolerance()) {
|
|
Pos1 = IntRes2d_End;
|
|
}
|
|
if(P2.Distance(DomainOnCurve2.FirstPoint())<=DomainOnCurve2.FirstTolerance()) {
|
|
Pos2 = IntRes2d_Head;
|
|
}
|
|
else if(P2.Distance(DomainOnCurve2.LastPoint())<=DomainOnCurve2.LastTolerance()) {
|
|
Pos2 = IntRes2d_End;
|
|
}
|
|
|
|
|
|
if(Pos1==IntRes2d_Middle && Pos2!=IntRes2d_Middle) {
|
|
PolyUSup=TheProjPCur::FindParameter( C1,P2,D1.FirstParameter(),D1.LastParameter(),TheCurveTool::EpsX(C1));
|
|
}
|
|
else if(Pos1!=IntRes2d_Middle && Pos2==IntRes2d_Middle) {
|
|
PolyVSup=TheProjPCur::FindParameter( C2,P1,D2.FirstParameter(),D2.LastParameter(),TheCurveTool::EpsX(C2));
|
|
}
|
|
else if(Abs(ParamInfOnCurve1-ParamSupOnCurve1) > Abs(ParamInfOnCurve2-ParamSupOnCurve2)) {
|
|
PolyVSup=TheProjPCur::FindParameter( C2,P1,D2.FirstParameter(),D2.LastParameter(),TheCurveTool::EpsX(C2));
|
|
}
|
|
else {
|
|
PolyUSup=TheProjPCur::FindParameter( C1,P2,D1.FirstParameter(),D1.LastParameter(),TheCurveTool::EpsX(C1));
|
|
}
|
|
|
|
if(IntImpParGen::DetermineTransition( Pos1,Tan1,Trans1,Pos2,Tan2,Trans2,TolConf)
|
|
==Standard_False) {
|
|
TheCurveTool::D2(C1,PolyUSup,P1,Tan1,Norm1);
|
|
TheCurveTool::D2(C2,PolyVSup,P2,Tan2,Norm2);
|
|
IntImpParGen::DetermineTransition(Pos1,Tan1,Norm1,Trans1,
|
|
Pos2,Tan2,Norm2,Trans2,TolConf);
|
|
}
|
|
IntRes2d_IntersectionPoint PtSeg2(P1,PolyUSup,PolyVSup
|
|
,Trans1,Trans2,Standard_False);
|
|
|
|
Standard_Boolean Oppos = (Tan1.Dot(Tan2) > 0.0)? Standard_False : Standard_True;
|
|
if(ParamInfOnCurve1 > ParamSupOnCurve1) {
|
|
IntRes2d_IntersectionSegment Seg(PtSeg2,PtSeg1,Oppos,Standard_False);
|
|
Append(Seg);
|
|
}
|
|
else {
|
|
IntRes2d_IntersectionSegment Seg(PtSeg1,PtSeg2,Oppos,Standard_False);
|
|
Append(Seg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return Standard_True;
|
|
}
|
|
|
|
//======================================================================
|
|
// GetIntersection
|
|
//======================================================================
|
|
|
|
void GetIntersection(const TheCurve& theC1, const Standard_Real theT1f, const Standard_Real theT1l,
|
|
const TheCurve& theC2, const Standard_Real theT2f, const Standard_Real theT2l,
|
|
const Standard_Real theTolConf,
|
|
const Standard_Integer theMaxCount,
|
|
IntRes2d_IntersectionPoint& thePInt, Standard_Real& theDist,
|
|
Standard_Integer& theCount)
|
|
{
|
|
theCount++;
|
|
//
|
|
Standard_Real aTol2 = theTolConf*theTolConf;
|
|
Standard_Real aPTol1 = Max(100.*Epsilon(Max(Abs(theT1f), Abs(theT1l))), Precision::PConfusion());
|
|
Standard_Real aPTol2 = Max(100.*Epsilon(Max(Abs(theT2f), Abs(theT2l))), Precision::PConfusion());
|
|
gp_Pnt2d aP1f, aP1l, aP2f, aP2l;
|
|
Bnd_Box2d aB1, aB2;
|
|
//
|
|
TheCurveTool::D0(theC1, theT1f, aP1f);
|
|
TheCurveTool::D0(theC1, theT1l, aP1l);
|
|
aB1.Add(aP1f);
|
|
aB1.Add(aP1l);
|
|
aB1.Enlarge(theTolConf);
|
|
//
|
|
TheCurveTool::D0(theC2, theT2f, aP2f);
|
|
TheCurveTool::D0(theC2, theT2l, aP2l);
|
|
aB2.Add(aP2f);
|
|
aB2.Add(aP2l);
|
|
aB2.Enlarge(theTolConf);
|
|
//
|
|
if(aB1.IsOut(aB2))
|
|
{
|
|
theCount--;
|
|
return;
|
|
}
|
|
//
|
|
Standard_Boolean isSmall1 = (theT1l - theT1f) <= aPTol1 || aP1f.SquareDistance(aP1l) / 4. <= aTol2;
|
|
Standard_Boolean isSmall2 = (theT2l - theT2f) <= aPTol2 || aP2f.SquareDistance(aP2l) / 4. <= aTol2;
|
|
|
|
if((isSmall1 && isSmall2) || (theCount > theMaxCount))
|
|
{
|
|
//Seems to be intersection
|
|
//Simple treatment of segment intersection
|
|
gp_XY aPnts1[3] = {aP1f.XY(), (aP1f.XY() + aP1l.XY()) / 2., aP1l.XY()};
|
|
gp_XY aPnts2[3] = {aP2f.XY(), (aP2f.XY() + aP2l.XY()) / 2., aP2l.XY()};
|
|
Standard_Integer i, j, imin = -1, jmin = -1;
|
|
Standard_Real dmin = RealLast(), d;
|
|
for(i = 0; i < 3; i++)
|
|
{
|
|
for(j = 0; j < 3; j++)
|
|
{
|
|
d = (aPnts1[i] - aPnts2[j]).SquareModulus();
|
|
if(d < dmin)
|
|
{
|
|
dmin=d;
|
|
imin = i;
|
|
jmin = j;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
dmin = Sqrt(dmin);
|
|
if(theDist > dmin)
|
|
{
|
|
theDist = dmin;
|
|
//
|
|
Standard_Real t1;
|
|
if(imin == 0)
|
|
{
|
|
t1 = theT1f;
|
|
}
|
|
else if(imin == 1)
|
|
{
|
|
t1 = (theT1f + theT1l) / 2.;
|
|
}
|
|
else
|
|
{
|
|
t1 = theT1l;
|
|
}
|
|
//
|
|
Standard_Real t2;
|
|
if(jmin == 0)
|
|
{
|
|
t2 = theT2f;
|
|
}
|
|
else if(jmin == 1)
|
|
{
|
|
t2 = (theT2f + theT2l) / 2.;
|
|
}
|
|
else
|
|
{
|
|
t2 = theT2l;
|
|
}
|
|
//
|
|
gp_Pnt2d aPint((aPnts1[imin] + aPnts2[jmin])/2.);
|
|
//
|
|
IntRes2d_Transition aTrans1, aTrans2;
|
|
thePInt.SetValues(aPint, t1, t2, aTrans1, aTrans2, Standard_False);
|
|
}
|
|
theCount--;
|
|
return;
|
|
}
|
|
|
|
if(isSmall1)
|
|
{
|
|
Standard_Real aT2m = (theT2l + theT2f) / 2.;
|
|
GetIntersection(theC1, theT1f, theT1l, theC2, theT2f, aT2m, theTolConf, theMaxCount, thePInt, theDist, theCount);
|
|
GetIntersection(theC1, theT1f, theT1l, theC2, aT2m, theT2l, theTolConf, theMaxCount, thePInt, theDist, theCount);
|
|
}
|
|
else if(isSmall2)
|
|
{
|
|
Standard_Real aT1m = (theT1l + theT1f) / 2.;
|
|
GetIntersection(theC1, theT1f, aT1m, theC2, theT2f, theT2l, theTolConf, theMaxCount, thePInt, theDist, theCount);
|
|
GetIntersection(theC1, aT1m, theT1l, theC2, theT2f, theT2l, theTolConf, theMaxCount, thePInt, theDist, theCount);
|
|
}
|
|
else
|
|
{
|
|
Standard_Real aT1m = (theT1l + theT1f) / 2.;
|
|
Standard_Real aT2m = (theT2l + theT2f) / 2.;
|
|
GetIntersection(theC1, theT1f, aT1m, theC2, theT2f, aT2m, theTolConf, theMaxCount, thePInt, theDist, theCount);
|
|
GetIntersection(theC1, theT1f, aT1m, theC2, aT2m, theT2l, theTolConf, theMaxCount, thePInt, theDist, theCount);
|
|
GetIntersection(theC1, aT1m, theT1l, theC2, theT2f, aT2m, theTolConf, theMaxCount, thePInt, theDist, theCount);
|
|
GetIntersection(theC1, aT1m, theT1l, theC2, aT2m, theT2l, theTolConf, theMaxCount, thePInt, theDist, theCount);
|
|
}
|
|
|
|
}
|
|
|
|
//=======================================================================
|
|
//function : GetMinNbPoints
|
|
//purpose :
|
|
//=======================================================================
|
|
Standard_Integer IntCurve_IntPolyPolyGen::GetMinNbSamples() const
|
|
{
|
|
return myMinPntNb;
|
|
}
|
|
|
|
//=======================================================================
|
|
//function : SetMinNbPoints
|
|
//purpose :
|
|
//=======================================================================
|
|
void IntCurve_IntPolyPolyGen::SetMinNbSamples(const Standard_Integer theMinNbSamples)
|
|
{
|
|
myMinPntNb = theMinNbSamples;
|
|
}
|