Files
OCCT/src/ShapeFix/ShapeFix_ComposeShell.cxx
dpasukhi a5a7b3185b Coding - Apply .clang-format formatting #286
Update empty method guards to new style with regex (see PR).
Used clang-format 18.1.8.
New actions to validate code formatting is added.
Update .clang-format with disabling of include sorting.
  It is temporary changes, then include will be sorted.
Apply formatting for /src and /tools folder.
The files with .hxx,.cxx,.lxx,.h,.pxx,.hpp,*.cpp extensions.
2025-01-26 00:43:57 +00:00

3229 lines
113 KiB
C++

// Created on: 1999-04-27
// Created by: Andrey BETENEV
// Copyright (c) 1999-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.
// pdn 01.06.99 S4205: handling not-SameRange edges
// abv 22.07.99 implementing patch indices
// svv 10.01.00 porting on DEC
#include <Bnd_Box2d.hxx>
#include <BRep_Builder.hxx>
#include <BRep_Tool.hxx>
#include <BRepTools.hxx>
#include <BRepTopAdaptor_FClass2d.hxx>
#include <Extrema_ExtPC2d.hxx>
#include <Geom2d_Curve.hxx>
#include <Geom2d_Line.hxx>
#include <Geom2dAdaptor_Curve.hxx>
#include <Geom2dInt_GInter.hxx>
#include <Geom_Curve.hxx>
#include <Geom_ElementarySurface.hxx>
#include <gp_Dir2d.hxx>
#include <gp_Lin2d.hxx>
#include <gp_Pnt.hxx>
#include <gp_Pnt2d.hxx>
#include <IntRes2d_Domain.hxx>
#include <IntRes2d_IntersectionPoint.hxx>
#include <IntRes2d_IntersectionSegment.hxx>
#include <Precision.hxx>
#include <ShapeAnalysis.hxx>
#include <ShapeAnalysis_Curve.hxx>
#include <ShapeAnalysis_Edge.hxx>
#include <ShapeAnalysis_Surface.hxx>
#include <ShapeAnalysis_TransferParametersProj.hxx>
#include <ShapeAnalysis_WireOrder.hxx>
#include <ShapeBuild_Edge.hxx>
#include <ShapeBuild_ReShape.hxx>
#include <ShapeBuild_Vertex.hxx>
#include <ShapeExtend_CompositeSurface.hxx>
#include <ShapeFix_ComposeShell.hxx>
#include <ShapeFix_Edge.hxx>
#include <ShapeFix_Face.hxx>
#include <ShapeFix_Wire.hxx>
#include <ShapeFix_WireSegment.hxx>
#include <Standard_Type.hxx>
#include <TColgp_SequenceOfPnt2d.hxx>
#include <TColStd_Array1OfBoolean.hxx>
#include <TColStd_Array1OfInteger.hxx>
#include <TColStd_Array1OfReal.hxx>
#include <TColStd_HArray1OfReal.hxx>
#include <TColStd_SequenceOfReal.hxx>
#include <TopLoc_Location.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Iterator.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Shell.hxx>
#include <TopoDS_Vertex.hxx>
#include <TopoDS_Wire.hxx>
#include <TopTools_DataMapOfShapeListOfShape.hxx>
#include <TopTools_MapOfShape.hxx>
IMPLEMENT_STANDARD_RTTIEXT(ShapeFix_ComposeShell, ShapeFix_Root)
//=================================================================================================
ShapeFix_ComposeShell::ShapeFix_ComposeShell()
: myOrient(TopAbs_FORWARD),
myStatus(0),
myUResolution(RealLast()),
myVResolution(RealLast()),
myInvertEdgeStatus(Standard_True),
myClosedMode(Standard_False),
myUClosed(Standard_False),
myVClosed(Standard_False),
myUPeriod(0.),
myVPeriod(0.)
{
myTransferParamTool = new ShapeAnalysis_TransferParametersProj;
}
//=================================================================================================
void ShapeFix_ComposeShell::Init(const Handle(ShapeExtend_CompositeSurface)& Grid,
const TopLoc_Location& L,
const TopoDS_Face& Face,
const Standard_Real Prec)
{
myGrid = Grid;
myUClosed = myGrid->IsUClosed();
myVClosed = myGrid->IsVClosed();
myUPeriod = myGrid->UJointValue(myGrid->NbUPatches() + 1) - myGrid->UJointValue(1);
myVPeriod = myGrid->VJointValue(myGrid->NbVPatches() + 1) - myGrid->VJointValue(1);
// DTK-CKY 100531 : protection against very thin face
// Test "isclosed" should be filtered on the overall (non trimmed) surface, must be closed
Handle(Geom_Surface) theSurface = BRep_Tool::Surface(Face, myLoc);
// avoid false detection of 'Closed' on very thin faces
if (theSurface->IsKind(STANDARD_TYPE(Geom_ElementarySurface)))
{
myUClosed = myUClosed && theSurface->IsUClosed();
myVClosed = myVClosed && theSurface->IsVClosed();
}
else
{
Standard_Real U0, U1, V0, V1, GU0 = 0., GU1 = 0., GV0 = 0., GV1 = 0.;
theSurface->Bounds(U0, U1, V0, V1);
if (::Precision::IsInfinite(U0) || ::Precision::IsInfinite(U1) || ::Precision::IsInfinite(V0)
|| ::Precision::IsInfinite(V1))
BRepTools::UVBounds(Face, GU0, GU1, GV0, GV1);
if (myUClosed)
{
if (::Precision::IsInfinite(V0))
V0 = GV0;
if (::Precision::IsInfinite(V1))
V1 = GV1;
gp_Pnt P0 = theSurface->Value(U0, (V0 + V1) / 2.);
gp_Pnt P1 = theSurface->Value(U1, (V0 + V1) / 2.);
if (P0.Distance(P1) > Precision::Confusion() * 10)
myUClosed = Standard_False;
}
if (myVClosed)
{
if (::Precision::IsInfinite(U0))
U0 = GU0;
if (::Precision::IsInfinite(U1))
U1 = GU1;
gp_Pnt P0 = theSurface->Value((U0 + U1) / 2., V0);
gp_Pnt P1 = theSurface->Value((U0 + U1) / 2., V1);
if (P0.Distance(P1) > Precision::Confusion() * 10)
myVClosed = Standard_False;
}
}
// DTK-CKY 100531 end
myLoc = L;
// smh#8
TopoDS_Shape tmpF = Face.Oriented(TopAbs_FORWARD);
myFace = TopoDS::Face(tmpF); // for correct dealing with seams
myOrient = Face.Orientation();
SetPrecision(Prec);
myStatus = ShapeExtend::EncodeStatus(ShapeExtend_OK);
// Compute resolution (checking in 2d is necessary for splitting
// degenerated edges and avoiding NotClosed)
myUResolution = myVResolution = RealLast();
for (Standard_Integer i = 1; i <= myGrid->NbUPatches(); i++)
{
Standard_Real uRange = myGrid->UJointValue(i + 1) - myGrid->UJointValue(i);
for (Standard_Integer j = 1; j <= myGrid->NbVPatches(); j++)
{
Standard_Real vRange = myGrid->VJointValue(j + 1) - myGrid->VJointValue(j);
Standard_Real u1, u2, v1, v2;
myGrid->Patch(i, j)->Bounds(u1, u2, v1, v2);
GeomAdaptor_Surface GAS(myGrid->Patch(i, j));
Standard_Real ures = GAS.UResolution(1.) * uRange / (u2 - u1);
Standard_Real vres = GAS.VResolution(1.) * vRange / (v2 - v1);
if (ures > 0. && myUResolution > ures)
myUResolution = ures;
if (vres > 0. && myVResolution > vres)
myVResolution = vres;
}
}
if (myUResolution == RealLast())
myUResolution = ::Precision::Parametric(1.);
if (myVResolution == RealLast())
myVResolution = ::Precision::Parametric(1.);
}
//=================================================================================================
Standard_Boolean ShapeFix_ComposeShell::Perform()
{
myStatus = ShapeExtend::EncodeStatus(ShapeExtend_OK);
myInvertEdgeStatus = Standard_False;
ShapeFix_SequenceOfWireSegment seqw; // working data: wire segments
// Init seqw by initial set of wires (with corresponding orientation)
LoadWires(seqw);
if (seqw.Length() == 0)
{
myStatus = ShapeExtend::EncodeStatus(ShapeExtend_FAIL6);
return Standard_False;
}
// Split edges in the wires by grid and add internal segments of grid (parts of cutting lines)
SplitByGrid(seqw);
// Split all the wires into segments by common vertices (intersections)
BreakWires(seqw);
// Then, collect resulting wires
ShapeFix_SequenceOfWireSegment wires; // resulting wires
CollectWires(wires, seqw);
// And construct resulting faces
TopTools_SequenceOfShape faces;
DispatchWires(faces, wires);
// Finally, construct resulting shell
if (faces.Length() != 1)
{
TopoDS_Shell S;
BRep_Builder B;
B.MakeShell(S);
for (Standard_Integer i = 1; i <= faces.Length(); i++)
B.Add(S, faces(i));
myResult = S;
}
else
myResult = faces(1);
myResult.Orientation(myOrient);
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_DONE1);
return Standard_True;
}
//=================================================================================================
void ShapeFix_ComposeShell::SplitEdges()
{
myStatus = ShapeExtend::EncodeStatus(ShapeExtend_OK);
ShapeFix_SequenceOfWireSegment seqw; // working data: wire segments
// Init seqw by initial set of wires (with corresponding orientation)
LoadWires(seqw);
// Split edges in the wires by grid and add internal segments of grid (parts of cutting lines)
SplitByGrid(seqw);
}
//=================================================================================================
const TopoDS_Shape& ShapeFix_ComposeShell::Result() const
{
return myResult;
}
//=================================================================================================
Standard_Boolean ShapeFix_ComposeShell::Status(const ShapeExtend_Status status) const
{
return ShapeExtend::DecodeStatus(myStatus, status);
}
//=======================================================================
// PRIVATE (working) METHODS
//=======================================================================
#define TOLINT 1.e-10 // precision for intersection
// Local definitions: characteristics of intersection point
#define IOR_UNDEF 0 // undefined side
#define IOR_LEFT 1 // to left side of cutting line
#define IOR_RIGHT 2 // to right side of cutting line
#define IOR_BOTH 3 // crossing
#define IOR_POS 4 // in case of cycle on full period, whether first point is right
#define ITP_INTER 8 // crossing
#define ITP_BEGSEG 16 // start of tangential segment
#define ITP_ENDSEG 32 // stop of tangential segment
#define ITP_TANG 64 // tangential point
//=================================================================================================
// Return (signed) deviation of point from line
static Standard_Real PointLineDeviation(const gp_Pnt2d& p, const gp_Lin2d& line)
{
gp_Dir2d dir = line.Direction();
gp_Dir2d n(-dir.Y(), dir.X());
return n.XY() * (p.XY() - line.Location().XY());
}
//=================================================================================================
// Define position of point relative to line
static Standard_Integer PointLinePosition(const gp_Pnt2d& p,
const gp_Lin2d& line,
Standard_Real& dev)
{
dev = PointLineDeviation(p, line);
return (dev > TOLINT ? IOR_LEFT : (dev < -TOLINT ? IOR_RIGHT : IOR_UNDEF));
}
//=================================================================================================
// Define position of point relative to line
static Standard_Integer PointLinePosition(const gp_Pnt2d& p, const gp_Lin2d& line)
{
Standard_Real dev;
return PointLinePosition(p, line, dev);
}
//=================================================================================================
// Compute parameter of point on line
static inline Standard_Real ParamPointOnLine(const gp_Pnt2d& p, const gp_Lin2d& line)
{
return line.Direction().XY() * (p.XY() - line.Location().XY());
}
//=================================================================================================
// Compute parameter of two points on line (as intersection of segment)
static Standard_Real ParamPointsOnLine(const gp_Pnt2d& p1, const gp_Pnt2d& p2, const gp_Lin2d& line)
{
Standard_Real dist1 = PointLineDeviation(p1, line);
Standard_Real dist2 = PointLineDeviation(p2, line);
// in most cases, one of points is on line
if (Abs(dist1) < ::Precision::PConfusion())
{
if (Abs(dist2) < ::Precision::PConfusion())
return 0.5 * (ParamPointOnLine(p1, line) + ParamPointOnLine(p2, line));
return ParamPointOnLine(p1, line);
}
if (Abs(dist2) < ::Precision::PConfusion())
return ParamPointOnLine(p2, line);
// just protection
if (dist2 * dist1 > 0)
return 0.5 * (ParamPointOnLine(p1, line) + ParamPointOnLine(p2, line));
// else compute intersection
return (ParamPointOnLine(p1, line) * dist2 - ParamPointOnLine(p2, line) * dist1)
/ (dist2 - dist1);
}
//=================================================================================================
// Compute projection of point on line
static inline gp_Pnt2d ProjectPointOnLine(const gp_Pnt2d& p, const gp_Lin2d& line)
{
return line.Location().XY() + line.Direction().XY() * ParamPointOnLine(p, line);
}
//=================================================================================================
// Apply context to one edge in the wire and put result into this wire
static Standard_Integer ApplyContext(ShapeFix_WireSegment& wire,
const Standard_Integer iedge,
const Handle(ShapeBuild_ReShape)& context)
{
TopoDS_Edge edge = wire.Edge(iedge);
TopoDS_Shape res = context->Apply(edge);
if (res.IsSame(edge))
return 1;
if (res.ShapeType() == TopAbs_EDGE)
{
wire.SetEdge(iedge, TopoDS::Edge(res));
return 1;
}
Standard_Integer index = iedge;
Handle(ShapeExtend_WireData) segw = new ShapeExtend_WireData;
segw->ManifoldMode() = Standard_False;
for (TopoDS_Iterator it(res); it.More(); it.Next())
{
TopoDS_Edge E = TopoDS::Edge(it.Value());
if (!E.IsNull())
segw->Add(E);
#ifdef OCCT_DEBUG
else
std::cout << "Error: ShapeFix_ComposeShell, ApplyContext: wrong mapping of edge" << std::endl;
#endif
}
// add edges into the wire in correct order
if (segw->NbEdges() > 0)
{
Standard_Integer ind, iumin, iumax, ivmin, ivmax;
wire.GetPatchIndex(iedge, iumin, iumax, ivmin, ivmax);
Standard_Integer nbEdges = segw->NbEdges();
for (Standard_Integer i = 1; i <= nbEdges; i++, index++)
{
ind = (edge.Orientation() == TopAbs_FORWARD || edge.Orientation() == TopAbs_INTERNAL
? i
: segw->NbEdges() - i + 1);
TopoDS_Edge aE = segw->Edge(ind);
if (i == 1)
wire.SetEdge(index, aE);
else
wire.AddEdge(index, aE, iumin, iumax, ivmin, ivmax);
}
}
#ifdef OCCT_DEBUG
else
std::cout << "Warning: ShapeFix_ComposeShell, ApplyContext: edge is to remove - not implemented"
<< std::endl;
#endif
return index - iedge;
}
//=================================================================================================
// check points coincidence
static inline Standard_Boolean IsCoincided(const gp_Pnt2d& p1,
const gp_Pnt2d& p2,
const Standard_Real UResolution,
const Standard_Real VResolution,
const Standard_Real tol)
{
// pdn Maximal accuracy is working precision of intersector.
Standard_Real UTolerance = UResolution * tol;
Standard_Real VTolerance = VResolution * tol;
return Abs(p1.X() - p2.X()) <= Max(TOLINT, UTolerance)
&& Abs(p1.Y() - p2.Y()) <= Max(TOLINT, VTolerance);
}
//=================================================================================================
// computes index for the patch by given parameter Param
static Standard_Integer GetPatchIndex(const Standard_Real Param,
const Handle(TColStd_HArray1OfReal)& Params,
const Standard_Boolean isClosed)
{
Standard_Integer NP = Params->Upper();
Standard_Real period = Params->Value(NP) - Params->Value(1);
Standard_Real shift = 0;
if (isClosed)
shift = ShapeAnalysis::AdjustToPeriod(Param, Params->Value(1), Params->Value(NP));
Standard_Real p = Param + shift;
// locate patch: the same algo as in SE_CS::LocateParameter()
Standard_Integer i; // svv #1
for (i = 2; i < NP; i++)
{
// Standard_Real par = Params->Value(i);
if (p < Params->Value(i))
break;
}
i--;
Standard_Real ish = shift / period;
Standard_Integer ishift = (Standard_Integer)(ish < 0 ? ish - 0.5 : ish + 0.5);
return i - ishift * (NP - 1);
}
//=================================================================================================
void ShapeFix_ComposeShell::LoadWires(ShapeFix_SequenceOfWireSegment& seqw) const
{
seqw.Clear();
// Init seqw by initial set of wires (with corresponding orientation)
for (TopoDS_Iterator iw(myFace, Standard_False); iw.More(); iw.Next())
{
TopoDS_Shape tmpW = Context()->Apply(iw.Value());
if (tmpW.ShapeType() != TopAbs_WIRE)
{
if (tmpW.ShapeType() == TopAbs_VERTEX)
{
ShapeFix_WireSegment seg; //(( isOuter ? TopAbs_REVERSED : TopAbs_FORWARD ) );
seg.SetVertex(TopoDS::Vertex(tmpW));
seg.Orientation(tmpW.Orientation());
seqw.Append(seg);
}
continue;
}
TopoDS_Wire wire = TopoDS::Wire(tmpW);
Standard_Boolean isNonManifold =
(wire.Orientation() != TopAbs_REVERSED && wire.Orientation() != TopAbs_FORWARD);
// protect against INTERNAL/EXTERNAL wires
// if ( wire.Orientation() != TopAbs_REVERSED &&
// wire.Orientation() != TopAbs_FORWARD ) continue;
// determine orientation of the wire
// TopoDS_Face face = TopoDS::Face ( myFace.EmptyCopied() );
// B.Add ( face, wire );
// Standard_Boolean isOuter = ShapeAnalysis::IsOuterBound ( face );
if (isNonManifold)
{
Handle(ShapeExtend_WireData) sbwd =
new ShapeExtend_WireData(wire, Standard_True, Standard_False);
// pdn protection against wires w/o edges
Standard_Integer nbEdges = sbwd->NbEdges();
if (nbEdges)
{
// wire segments for non-manifold topology should have INTERNAL orientation
ShapeFix_WireSegment seg(sbwd, TopAbs_INTERNAL);
seqw.Append(seg);
}
}
else
{
// splitting wires containing manifold and non-manifold parts on a separate
// wire segment
Handle(ShapeExtend_WireData) sbwdM = new ShapeExtend_WireData();
Handle(ShapeExtend_WireData) sbwdNM = new ShapeExtend_WireData();
sbwdNM->ManifoldMode() = Standard_False;
TopoDS_Iterator aIt(wire);
for (; aIt.More(); aIt.Next())
{
TopoDS_Edge E = TopoDS::Edge(aIt.Value());
if (E.Orientation() == TopAbs_FORWARD || E.Orientation() == TopAbs_REVERSED)
sbwdM->Add(E);
else
sbwdNM->Add(E);
}
Standard_Integer nbMEdges = sbwdM->NbEdges();
Standard_Integer nbNMEdges = sbwdNM->NbEdges();
if (nbNMEdges)
{
// clang-format off
ShapeFix_WireSegment seg ( sbwdNM, TopAbs_INTERNAL); //(( isOuter ? TopAbs_REVERSED : TopAbs_FORWARD ) );
// clang-format on
seqw.Append(seg);
}
if (nbMEdges)
{
// Orientation is set so as to allow the segment to be traversed in only one direction
// skl 01.04.2002
Handle(ShapeFix_Wire) sfw = new ShapeFix_Wire;
sfw->Load(sbwdM);
Standard_Integer stat = 0;
Handle(Geom_Surface) gs = BRep_Tool::Surface(myFace);
if (gs->IsUPeriodic() && gs->IsVPeriodic())
{
// For torus-like shapes, first reorder in 2d since reorder is indifferent in 3d
ShapeAnalysis_WireOrder sawo(Standard_False, 0);
ShapeAnalysis_Edge sae;
for (Standard_Integer i = 1; i <= nbMEdges; i++)
{
Standard_Real f, l;
Handle(Geom2d_Curve) c2d;
// smh#8
TopoDS_Shape tmpF = myFace.Oriented(TopAbs_FORWARD);
if (!sae.PCurve(sbwdM->Edge(i), TopoDS::Face(tmpF), c2d, f, l))
continue;
sawo.Add(c2d->Value(f).XY(), c2d->Value(l).XY());
}
sawo.Perform();
stat = (sawo.Status() < 0 ? -1 : 1);
sfw->FixReorder(sawo);
}
sfw->FixReorder();
if (sfw->StatusReorder(ShapeExtend_DONE3))
stat = -1;
if (stat < 0)
{
BRep_Builder B;
TopoDS_Shape dummy = myFace.EmptyCopied();
TopoDS_Face face = TopoDS::Face(dummy);
B.Add(face, wire);
Standard_Boolean isOuter = ShapeAnalysis::IsOuterBound(face);
TopoDS_Wire w = sbwdM->Wire();
dummy = myFace.EmptyCopied();
face = TopoDS::Face(dummy);
B.Add(face, w);
Standard_Boolean isOuterAfter = ShapeAnalysis::IsOuterBound(face);
if (isOuter != isOuterAfter)
sbwdM->Reverse(face);
}
// clang-format off
ShapeFix_WireSegment seg ( sbwdM, TopAbs_REVERSED ); //(( isOuter ? TopAbs_REVERSED : TopAbs_FORWARD ) );
// clang-format on
seqw.Append(seg);
}
}
}
}
//=======================================================================
// function : ComputeCode
// purpose : compute code for wire segment between two intersections (by deviation)
//=======================================================================
Standard_Integer ShapeFix_ComposeShell::ComputeCode(const Handle(ShapeExtend_WireData)& wire,
const gp_Lin2d& line,
const Standard_Integer begInd,
const Standard_Integer endInd,
const Standard_Real begPar,
const Standard_Real endPar,
const Standard_Boolean isInternal)
{
Standard_Integer code = IOR_UNDEF;
ShapeAnalysis_Edge sae;
const Standard_Integer NPOINTS = 5; // number of points for measuring deviation
// track special closed case: segment starts at end of edge and ends at its beginning
Standard_Integer special = (begInd == endInd
&& (wire->Edge(begInd).Orientation() == TopAbs_FORWARD
|| wire->Edge(begInd).Orientation() == TopAbs_INTERNAL)
== (begPar > endPar)
? 1
: 0);
if (!special && begInd == endInd && begPar == endPar && (myClosedMode || isInternal))
special = 1;
// for tracking cases in closed mode
Standard_Boolean begin = Standard_True;
Standard_Real shift = 0;
gp_Pnt2d p2d0;
// check if segment is tangency
// Segment is considered as tangency if deviation of pcurve from line
// (in 2d) measured by NPOINTS points is less than tolerance of edge
// (recomputed to 2d using Resolution).
Standard_Integer nb = wire->NbEdges();
Standard_Integer i; // svv #1
for (i = begInd;; i++)
{
if (i > nb)
i = 1;
TopoDS_Edge edge = wire->Edge(i);
Handle(Geom2d_Curve) c2d;
Standard_Real f, l;
if (!sae.PCurve(edge, myFace, c2d, f, l, Standard_False))
{
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_FAIL3);
continue;
}
Standard_Real tol = LimitTolerance(BRep_Tool::Tolerance(edge));
Standard_Boolean isreversed = (edge.Orientation() == TopAbs_REVERSED);
Standard_Real par1 = (i == begInd && special >= 0 ? begPar : (isreversed ? l : f));
Standard_Real par2 = (i == endInd && special <= 0 ? endPar : (isreversed ? f : l));
Standard_Real dpar = (par2 - par1) / (NPOINTS - 1);
Standard_Integer np = (Abs(dpar) < ::Precision::PConfusion() ? 1 : NPOINTS);
Standard_Integer j; // svv #1
for (j = 0; j < np; j++)
{
Standard_Real par = par1 + dpar * j;
gp_Pnt2d p2d = c2d->Value(par);
if (myClosedMode)
{
if (myUClosed && Abs(line.Direction().X()) < ::Precision::PConfusion())
{
if (begin)
shift = ShapeAnalysis::AdjustByPeriod(p2d.X(), line.Location().X(), myUPeriod);
else if (!j)
shift = ShapeAnalysis::AdjustByPeriod(p2d.X() - p2d0.X(), 0., myUPeriod);
p2d.SetX(p2d.X() + shift);
}
if (myVClosed && Abs(line.Direction().Y()) < ::Precision::PConfusion())
{
if (begin)
shift = ShapeAnalysis::AdjustByPeriod(p2d.Y(), line.Location().Y(), myVPeriod);
else if (!j)
shift = ShapeAnalysis::AdjustByPeriod(p2d.Y() - p2d0.Y(), 0., myVPeriod);
p2d.SetY(p2d.Y() + shift);
}
begin = Standard_False;
}
p2d0 = p2d;
Standard_Integer pos = PointLinePosition(p2d, line);
if (pos == IOR_UNDEF)
continue;
// analyse the deviation
gp_Pnt2d p2dl = ProjectPointOnLine(p2d, line);
if (!IsCoincided(p2d, p2dl, myUResolution, myVResolution, tol))
{
if (!myClosedMode)
{
code = pos;
break;
}
else
{
code |= pos;
}
}
}
if (j < np)
{
i = 0;
break;
} // not tangency
if (i == endInd)
{
if (special <= 0)
break;
else
special = -1;
}
}
if (myClosedMode)
{
if (code != IOR_UNDEF && !begin)
{
// in closed mode, if segment is of 2*pi length, it is BOTH
Standard_Real dev = PointLineDeviation(p2d0, line);
if (myUClosed && Abs(line.Direction().X()) < ::Precision::PConfusion())
{
if (Abs(Abs(dev) - myUPeriod) < 0.1 * myUPeriod)
{
code = IOR_BOTH;
if (dev > 0)
code |= IOR_POS;
}
else if (code == IOR_BOTH)
code = IOR_UNDEF;
}
if (myVClosed && Abs(line.Direction().Y()) < ::Precision::PConfusion())
{
if (Abs(Abs(dev) - myVPeriod) < 0.1 * myVPeriod)
{
code = IOR_BOTH;
if (dev > 0)
code |= IOR_POS;
}
else if (code == IOR_BOTH)
code = IOR_UNDEF;
}
}
return code;
}
if (i)
code = IOR_UNDEF; // tangency
else if (code == IOR_BOTH)
{ // parity error in intersector
code = IOR_LEFT;
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_FAIL2);
#ifdef OCCT_DEBUG
std::cout << "Warning: ShapeFix_ComposeShell::ComputeCode: lost intersection point"
<< std::endl;
#endif
}
return code;
}
//=================================================================================================
// After applying context to (seam) edge, distribute its indices on new edges,
// according to their parameters on that edge
static void DistributeSplitPoints(const Handle(ShapeExtend_WireData)& sbwd,
const TopoDS_Face& myFace,
const Standard_Integer index,
const Standard_Integer nsplit,
TColStd_SequenceOfInteger& indexes,
const TColStd_SequenceOfReal& values)
{
Standard_Boolean isreversed = (nsplit > 0 && sbwd->Edge(index).Orientation() == TopAbs_REVERSED);
TColStd_Array1OfReal params(0, nsplit);
Standard_Integer i; // svv #1
for (i = 0; i < nsplit; i++)
{
Standard_Real f, l;
BRep_Tool::Range(sbwd->Edge(index + i), myFace, f, l);
params.SetValue(i, (isreversed ? l : f));
}
for (i = 1; i <= indexes.Length() && indexes(i) < index; i++)
;
for (Standard_Integer shift = 1; i <= indexes.Length() && indexes(i) == index; i++)
{
while (shift < nsplit && isreversed != (values(i) > params(shift)))
shift++;
indexes.SetValue(i, index + shift - 1);
}
for (; i <= indexes.Length(); i++)
indexes.SetValue(i, indexes(i) + nsplit - 1);
}
//=================================================================================================
static Standard_Integer CheckByCurve3d(const gp_Pnt& pos,
const Handle(Geom_Curve)& c3d,
const Standard_Real param,
const gp_Trsf& T,
const Standard_Real tol)
{
if (c3d.IsNull())
return Standard_True;
gp_Pnt p = c3d->Value(param);
if (T.Form() != gp_Identity)
p.Transform(T);
return pos.SquareDistance(p) <= tol * tol;
}
//=================================================================================================
static void DefinePatch(ShapeFix_WireSegment& wire,
const Standard_Integer code,
const Standard_Boolean isCutByU,
const Standard_Integer cutIndex,
const Standard_Integer number = -1)
{
Standard_Integer nb = (number > 0 ? number : wire.NbEdges());
if (isCutByU)
{
if (!(code & IOR_LEFT))
wire.DefineIUMin(nb, cutIndex);
if (!(code & IOR_RIGHT))
wire.DefineIUMax(nb, cutIndex);
}
else
{
if (!(code & IOR_RIGHT))
wire.DefineIVMin(nb, cutIndex);
if (!(code & IOR_LEFT))
wire.DefineIVMax(nb, cutIndex);
}
}
//=================================================================================================
static Standard_Real GetGridResolution(const Handle(TColStd_HArray1OfReal)& SplitValues,
const Standard_Integer cutIndex)
{
Standard_Integer nb = SplitValues->Length();
Standard_Real leftLen =
(cutIndex > 1 ? SplitValues->Value(cutIndex) - SplitValues->Value(cutIndex - 1)
: SplitValues->Value(nb) - SplitValues->Value(nb - 1));
Standard_Real rigthLen =
(cutIndex < nb ? SplitValues->Value(cutIndex + 1) - SplitValues->Value(cutIndex)
: SplitValues->Value(2) - SplitValues->Value(1));
return Min(leftLen, rigthLen) / 3.;
}
//=================================================================================================
ShapeFix_WireSegment ShapeFix_ComposeShell::SplitWire(ShapeFix_WireSegment& wire,
TColStd_SequenceOfInteger& indexes,
const TColStd_SequenceOfReal& values,
TopTools_SequenceOfShape& vertices,
const TColStd_SequenceOfInteger& SegmentCodes,
const Standard_Boolean isCutByU,
const Standard_Integer cutIndex)
{
BRep_Builder B;
ShapeFix_WireSegment result;
Handle(ShapeAnalysis_Surface) aSurfTool = new ShapeAnalysis_Surface(BRep_Tool::Surface(myFace));
Standard_Integer nbSplits = indexes.Length();
ShapeAnalysis_Edge sae;
Standard_Integer start = 1;
TopAbs_Orientation anWireOrient = wire.Orientation();
gp_Trsf T;
if (!myLoc.IsIdentity())
T = myLoc.Inverted().Transformation();
// Processing edge by edge (assuming that split points are sorted along the wire)
for (Standard_Integer i = 1; i <= wire.NbEdges(); i++)
{
// for already split seam edge, redistribute its splitting points
Standard_Integer nsplit = ApplyContext(wire, i, Context());
if (nsplit != 1)
{
DistributeSplitPoints(wire.WireData(), myFace, i, nsplit, indexes, values);
if (nsplit <= 0)
{
#ifdef OCCT_DEBUG
std::cout << "Error: ShapeFix_ComposeShell::SplitWire: edge dismissed" << std::endl;
#endif
i--;
continue;
}
}
TopoDS_Edge edge = wire.Edge(i);
Standard_Integer iumin, iumax, ivmin, ivmax;
wire.GetPatchIndex(i, iumin, iumax, ivmin, ivmax);
// Position code for first segment of edge
Standard_Integer code = SegmentCodes(start > 1 ? start - 1 : SegmentCodes.Length());
// Defining split parameters on edge
Standard_Integer stop = start;
while (stop <= nbSplits && indexes(stop) == i)
stop++;
if (stop == start)
{
result.AddEdge(0, edge, iumin, iumax, ivmin, ivmax);
if (code != 0
|| wire.Orientation() != TopAbs_EXTERNAL) // pdn 0 code handling for extertnal wires
DefinePatch(result, code, isCutByU, cutIndex);
continue;
}
// find non-manifold vertices on edge
TopTools_SequenceOfShape aNMVertices;
TopoDS_Iterator aIt(edge, Standard_False);
for (; aIt.More(); aIt.Next())
{
if (aIt.Value().Orientation() != TopAbs_FORWARD
&& aIt.Value().Orientation() != TopAbs_REVERSED)
aNMVertices.Append(aIt.Value());
}
// Collect data on edge
Standard_Real tolEdge = BRep_Tool::Tolerance(edge);
TopoDS_Vertex prevV = sae.FirstVertex(edge);
TopoDS_Vertex lastV = sae.LastVertex(edge);
Standard_Real prevVTol = LimitTolerance(BRep_Tool::Tolerance(prevV));
Standard_Real lastVTol = LimitTolerance(BRep_Tool::Tolerance(lastV));
gp_Pnt prevVPnt = BRep_Tool::Pnt(prevV);
gp_Pnt lastVPnt = BRep_Tool::Pnt(lastV);
if (T.Form() != gp_Identity)
{
prevVPnt.Transform(T);
lastVPnt.Transform(T);
}
Handle(Geom_Curve) c3d;
Standard_Real f3d, l3d;
if (!sae.Curve3d(edge, c3d, f3d, l3d))
{ // not a crime
c3d.Nullify();
f3d = l3d = 0;
}
Standard_Real firstPar, lastPar;
Handle(Geom2d_Curve) C2d;
if (!sae.PCurve(edge, myFace, C2d, firstPar, lastPar))
{
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_FAIL2);
}
// finding sequence of non-manifold parameters
Standard_Integer nbNMVert = aNMVertices.Length();
TColStd_SequenceOfReal aNMVertParams;
if (nbNMVert)
{
Geom2dAdaptor_Curve adc(C2d);
Standard_Integer n = 1;
for (; n <= nbNMVert; n++)
{
gp_Pnt apV = BRep_Tool::Pnt(TopoDS::Vertex(aNMVertices.Value(n)));
Standard_Real apar = firstPar;
Standard_Real adist2 = RealLast();
gp_Pnt aPproj;
if (!c3d.IsNull())
{
ShapeAnalysis_Curve asae;
adist2 = asae.Project(c3d, apV, Precision::Confusion(), aPproj, apar);
adist2 *= adist2;
}
else
{
gp_Pnt2d aP2d = aSurfTool->ValueOfUV(apV, Precision::Confusion());
Extrema_ExtPC2d aExtr(aP2d, adc);
if (aExtr.IsDone() && aExtr.NbExt())
{
adist2 = aExtr.SquareDistance(1);
Standard_Integer index = 1;
Standard_Integer k = 2;
for (; k <= aExtr.NbExt(); k++)
{
Standard_Real ad2 = aExtr.SquareDistance(k);
if (ad2 < adist2)
{
adist2 = ad2;
index = k;
}
}
apar = aExtr.Point(index).Parameter();
}
}
aNMVertParams.Append(apar);
}
}
// pdn Calculating parametric shift
Standard_Boolean sp = (f3d == firstPar && l3d == lastPar);
Standard_Real span2d = lastPar - firstPar;
// Standard_Real ln2d = lastPar-prevPar;
// Standard_Real ln3d = l3d - f3d;
// Standard_Real fact = ln2d/ln3d;
// Standard_Real shift = prevPar - f3d*fact;
Standard_Real prevPar = firstPar;
gp_Pnt2d prevPnt2d = C2d->Value(prevPar);
gp_Pnt2d lastPnt2d = C2d->Value(lastPar);
gp_Pnt prevPnt = myGrid->Value(prevPnt2d);
gp_Pnt lastPnt = myGrid->Value(lastPnt2d);
Standard_Boolean isPeriodic = C2d->IsPeriodic();
Standard_Real aPeriod = (isPeriodic ? C2d->Period() : 0.);
// Splitting edge
Standard_Integer NbEdgesStart = result.NbEdges();
Standard_Boolean splitted = Standard_False;
Standard_Real currPar = lastPar; // SK
for (Standard_Integer j = start; j <= stop; prevPar = currPar, j++)
{
if (!splitted && j >= stop)
{ // no splitting at all
// code = SegmentCodes ( j >1 ? j-1 : SegmentCodes.Length() ); // classification code
break;
}
currPar = (j < stop ? values.Value(j) : lastPar);
// fix for case when pcurve is periodic and first parameter of edge is more than 2P
// method ShapeBuild_Edge::CopyRanges shift pcurve to range 0-2P and parameters of cutting
// should be shifted too. gka SAMTECH 28.07.06
if (isPeriodic)
{
if (currPar > (Max(lastPar, firstPar) + Precision::PConfusion())
|| currPar < (Min(firstPar, lastPar) - Precision::PConfusion()))
{
Standard_Real aShift =
ShapeAnalysis::AdjustByPeriod(currPar, (firstPar + lastPar) * 0.5, aPeriod);
currPar += aShift;
}
}
gp_Pnt2d currPnt2d;
gp_Pnt currPnt;
// Try to adjust current splitting point to previous or end of edge
Standard_Boolean doCut = Standard_True;
TopoDS_Vertex V;
if (Abs(currPar - lastPar) < ::Precision::PConfusion())
{
V = lastV;
doCut = Standard_False;
}
else if (Abs(currPar - prevPar) < ::Precision::PConfusion())
{
vertices.Append(prevV);
code = SegmentCodes(j); // classification code - update for next segment
continue; // no splitting at this point, go to next one
}
else
{
currPnt2d = C2d->Value(currPar);
currPnt = myGrid->Value(currPnt2d);
if (currPnt.Distance(lastVPnt) <= lastVTol &&
// Tolerance is increased to prevent degenerated cuts in cases where all vertex
// tolerance is covered by distance of the edge curve from vertex point.
// Doubled to prevent edge being fully covered by its vertices tolerance (invalid edge).
CheckByCurve3d(lastVPnt,
c3d,
f3d + (currPar - firstPar) * (l3d - f3d) / span2d,
T,
lastVTol + 2 * Precision::Confusion())
&& lastPnt.Distance(myGrid->Value(C2d->Value(0.5 * (currPar + lastPar)))) <= lastVTol)
{
V = lastV;
Standard_Real uRes = myUResolution;
Standard_Real vRes = myVResolution;
if (isCutByU)
{
Standard_Real gridRes = GetGridResolution(myGrid->UJointValues(), cutIndex) / lastVTol;
uRes = Min(myUResolution, gridRes);
}
else
{
Standard_Real gridRes = GetGridResolution(myGrid->VJointValues(), cutIndex) / lastVTol;
vRes = Min(myVResolution, gridRes);
}
if (IsCoincided(lastPnt2d, currPnt2d, uRes, vRes, lastVTol)
&& IsCoincided(lastPnt2d,
C2d->Value(0.5 * (currPar + lastPar)),
uRes,
vRes,
lastVTol))
doCut = Standard_False;
}
else if (currPnt.Distance(prevVPnt) <= prevVTol &&
// Tolerance is increased to prevent degenerated cuts in cases where all vertex
// tolerance is covered by distance of the edge curve from vertex point.
// Doubled to prevent edge being fully covered by its vertices tolerance (invalid
// edge).
CheckByCurve3d(prevVPnt,
c3d,
f3d + (currPar - firstPar) * (l3d - f3d) / span2d,
T,
prevVTol + 2 * Precision::Confusion())
&& prevPnt.Distance(myGrid->Value(C2d->Value(0.5 * (currPar + prevPar))))
<= prevVTol)
{
V = prevV;
Standard_Real uRes = myUResolution;
Standard_Real vRes = myVResolution;
if (isCutByU)
{
Standard_Real gridRes = GetGridResolution(myGrid->UJointValues(), cutIndex) / prevVTol;
uRes = Min(myUResolution, gridRes);
}
else
{
Standard_Real gridRes = GetGridResolution(myGrid->VJointValues(), cutIndex) / prevVTol;
vRes = Min(myVResolution, gridRes);
}
if (IsCoincided(prevPnt2d, currPnt2d, uRes, vRes, prevVTol)
&& IsCoincided(prevPnt2d,
C2d->Value(0.5 * (currPar + prevPar)),
uRes,
vRes,
prevVTol))
{
vertices.Append(prevV);
code = SegmentCodes(j); // classification code - update for next segment
continue; // no splitting at this point, go to next one
}
}
//: abv 28.05.02: OCC320 Sample_2: if maxtol = 1e-7, the vertex tolerance
// is actually ignored - protect against new vertex on degenerated edge
else if (BRep_Tool::Degenerated(edge) && prevV.IsSame(lastV))
{
V = prevV;
}
}
// classification code for current segment
if (j > start)
code = SegmentCodes(j > 1 ? j - 1 : SegmentCodes.Length());
// if not adjusted, make new vertex
if (V.IsNull())
{
B.MakeVertex(V, currPnt.Transformed(myLoc.Transformation()), tolEdge);
vertices.Append(V);
}
// else adjusted to end, fill all resting vertices
else if (!doCut)
{
for (; j < stop; j++)
vertices.Append(lastV);
if (!splitted)
break; // no splitting at all
currPar = lastPar;
}
else
vertices.Append(V);
// When edge is about to be split, copy end vertices to protect
// original shape from increasing tolerance after fixing SameParameter
if (!splitted)
{
// smh#8
TopoDS_Shape emptyCopiedfV = prevV.EmptyCopied();
TopoDS_Vertex fV = TopoDS::Vertex(emptyCopiedfV);
Context()->Replace(prevV, fV);
TopoDS_Vertex lV;
if (prevV.IsSame(lastV))
{
// smh#8
TopoDS_Shape tmpV = fV.Oriented(lastV.Orientation());
lV = TopoDS::Vertex(tmpV);
}
else
{
// smh#8
TopoDS_Shape emptyCopied = lastV.EmptyCopied();
lV = TopoDS::Vertex(emptyCopied);
Context()->Replace(lastV, lV);
}
if (V.IsSame(lastV))
V = lV;
else if (V.IsSame(prevV))
V = fV;
lastV = lV;
prevV = fV;
}
// Splitting of the edge
splitted = Standard_True;
prevV.Orientation(TopAbs_FORWARD);
V.Orientation(TopAbs_REVERSED);
ShapeBuild_Edge sbe;
TopoDS_Edge anInitEdge = edge;
Standard_Boolean ismanifold =
(edge.Orientation() == TopAbs_FORWARD || edge.Orientation() == TopAbs_REVERSED);
if (!ismanifold)
anInitEdge.Orientation(TopAbs_FORWARD);
TopoDS_Edge newEdge = sbe.CopyReplaceVertices(anInitEdge, prevV, V);
// addition internal vertices if they exists on edge
Standard_Integer n = 1;
for (; n <= aNMVertParams.Length(); n++)
{
Standard_Real apar = aNMVertParams.Value(n);
TopoDS_Vertex aNMVert = TopoDS::Vertex(aNMVertices.Value(n));
TopoDS_Vertex atmpV = TopoDS::Vertex(Context()->Apply(aNMVert));
if (fabs(apar - prevPar) <= Precision::PConfusion())
{
Context()->Replace(atmpV, prevV);
aNMVertParams.Remove(n);
aNMVertices.Remove(n);
n--;
}
else if (fabs(apar - currPar) <= Precision::PConfusion())
{
Context()->Replace(atmpV, V);
aNMVertParams.Remove(n);
aNMVertices.Remove(n);
n--;
}
if (apar > prevPar && apar < currPar)
{
B.Add(newEdge, atmpV);
aNMVertParams.Remove(n);
aNMVertices.Remove(n);
n--;
}
}
sbe.CopyPCurves(newEdge, anInitEdge);
Handle(ShapeAnalysis_TransferParameters) theTransferParamtool = GetTransferParamTool();
theTransferParamtool->SetMaxTolerance(MaxTolerance());
theTransferParamtool->Init(anInitEdge, myFace);
theTransferParamtool->TransferRange(newEdge, prevPar, currPar, Standard_True);
if (!ismanifold)
{
if (code == IOR_UNDEF) // tangential segment
newEdge.Orientation(TopAbs_EXTERNAL);
else
newEdge.Orientation(edge.Orientation());
}
if (!sp && !BRep_Tool::Degenerated(newEdge))
B.SameRange(newEdge, Standard_False);
// pdn take into account 0 codes (if ext)
if (code == 0 && wire.Orientation() == TopAbs_EXTERNAL)
{
code = ((isCutByU == (j == 1)) ? 1 : 2);
}
result.AddEdge(0, newEdge, iumin, iumax, ivmin, ivmax);
DefinePatch(result, code, isCutByU, cutIndex);
// Changing prev parameters
prevV = V;
prevVTol = LimitTolerance(BRep_Tool::Tolerance(V));
prevVPnt = BRep_Tool::Pnt(V);
prevPnt = currPnt;
prevPnt2d = currPnt2d;
}
start = stop;
if (splitted)
{
// record replacement in context
// NOTE: order of edges in the replacing wire corresponds to FORWARD orientation of the edge
TopoDS_Wire resWire;
B.MakeWire(resWire);
for (Standard_Integer k = NbEdgesStart; k < result.NbEdges(); k++)
{
if (edge.Orientation() == TopAbs_FORWARD || edge.Orientation() == TopAbs_INTERNAL)
B.Add(resWire, result.Edge(k + 1));
else
B.Add(resWire, result.Edge(result.NbEdges() - k + NbEdgesStart));
}
Context()->Replace(edge, resWire);
}
else
{
if (anWireOrient == TopAbs_INTERNAL && code == 0)
{
ShapeBuild_Edge sbe;
if (edge.Orientation() == TopAbs_INTERNAL)
edge.Orientation(TopAbs_FORWARD);
TopoDS_Edge e1 = sbe.Copy(edge, Standard_False);
Handle(Geom2d_Curve) C2d2 = Handle(Geom2d_Curve)::DownCast(C2d->Copy());
B.UpdateEdge(e1, C2d, C2d2, myFace, 0.);
e1.Orientation(TopAbs_EXTERNAL);
Context()->Replace(edge, e1);
result.AddEdge(0, e1, iumin, iumax, ivmin, ivmax);
}
else
result.AddEdge(0, edge, iumin, iumax, ivmin, ivmax);
if (code == 0 && wire.Orientation() == TopAbs_EXTERNAL)
{
// pdn defining code for intersection of two isos
code = ((isCutByU == (Abs(firstPar - currPar) < Abs(lastPar - currPar))) ? 2 : 1);
}
DefinePatch(result, code, isCutByU, cutIndex);
}
}
result.Orientation(anWireOrient);
return result;
}
//=================================================================================================
Standard_Boolean ShapeFix_ComposeShell::SplitByLine(ShapeFix_WireSegment& wire,
const gp_Lin2d& line,
const Standard_Boolean isCutByU,
const Standard_Integer cutIndex,
TColStd_SequenceOfReal& SplitLinePar,
TColStd_SequenceOfInteger& SplitLineCode,
TopTools_SequenceOfShape& SplitLineVertex)
{
ShapeAnalysis_Edge sae;
// prepare data on cutting line
Handle(Geom2d_Line) jC2d = new Geom2d_Line(line);
Geom2dAdaptor_Curve jGAC(jC2d);
TColStd_SequenceOfInteger IntEdgeInd; // index of intersecting edge
TColStd_SequenceOfReal IntEdgePar; // parameter of intersection point on edge
TColStd_SequenceOfReal IntLinePar; // parameter of intersection point on line
Standard_Boolean isnonmanifold = (wire.Orientation() == TopAbs_INTERNAL);
// gka correction for non-manifold vertices SAMTECH
if (wire.IsVertex())
{
Handle(ShapeAnalysis_Surface) aSurfTool = new ShapeAnalysis_Surface(BRep_Tool::Surface(myFace));
TopoDS_Vertex aVert = wire.GetVertex();
gp_Pnt aP3d = BRep_Tool::Pnt(aVert);
gp_Pnt2d aP2d = aSurfTool->ValueOfUV(aP3d, Precision::Confusion());
Standard_Real dev = 0.;
Standard_Integer code = PointLinePosition(aP2d, line, dev);
if (code != IOR_UNDEF)
return Standard_False;
Standard_Real par = ParamPointOnLine(aP2d, line);
SplitLinePar.Append(par);
// splitting codes for non-manifold topology should be tangential
SplitLineCode.Append(ITP_TANG); // ITP_INTER);
TopoDS_Vertex aVertNew;
BRep_Builder aB;
aB.MakeVertex(aVertNew, aP3d, BRep_Tool::Tolerance(aVert));
aVertNew.Orientation(TopAbs_FORWARD);
Context()->Replace(aVert, aVertNew);
SplitLineVertex.Append(aVertNew);
wire.SetVertex(aVertNew);
return Standard_True;
}
const Handle(ShapeExtend_WireData) sewd = wire.WireData();
Standard_Integer nbe = sewd->NbEdges();
//: abv 31.10.01: for closed mode
Standard_Integer closedDir = 0;
if (myClosedMode)
{
if (myUClosed && Abs(line.Direction().X()) < ::Precision::PConfusion())
closedDir = -1;
else if (myVClosed && Abs(line.Direction().Y()) < ::Precision::PConfusion())
closedDir = 1;
}
Standard_Real halfPeriod = 0.5 * (closedDir ? closedDir < 0 ? myUPeriod : myVPeriod : 0.);
//============================================
// make intersections and collect all data on intersection points
Standard_Integer firstCode = 0, prevCode = 0;
gp_Pnt2d firstPos, prevPos;
Standard_Real firstDev = 0., prevDev = 0.;
for (Standard_Integer iedge = 1; iedge <= nbe; iedge++)
{
TopoDS_Edge E = sewd->Edge(iedge);
Standard_Boolean isreversed = (E.Orientation() == TopAbs_REVERSED);
Standard_Real f, l;
Handle(Geom2d_Curve) c2d;
if (!sae.PCurve(E, myFace, c2d, f, l, Standard_False))
continue;
Handle(Geom2d_Curve) c2d_sav = c2d;
// get end points
gp_Pnt2d posf = c2d->Value(f), posl = c2d->Value(l);
gp_XY pppf = posf.XY(), pppl = posl.XY();
// In case of ClosedMode, adjust curve and end points to period on closed surface
//: abv 16.10.01: Ziegler_CADDY01.sat -18: if pcurve is longer than period,
// ensure processing of all intersections
Standard_Integer nbIter = 1;
gp_Vec2d shiftNext(0., 0.);
if (myClosedMode)
{
// get bounding box of pcurve
ShapeAnalysis_Curve sac;
Bnd_Box2d box;
const Standard_Integer aNbPoints = 41;
sac.FillBndBox(c2d, f, l, aNbPoints, Standard_True, box);
Standard_Real umin, vmin, umax, vmax;
box.Get(umin, vmin, umax, vmax);
// compute shifts and adjust points adjust
if (closedDir < 0)
{
Standard_Real x = line.Location().X();
Standard_Real shift = ShapeAnalysis::AdjustToPeriod(umin, x - myUPeriod, x);
if (shift != 0.)
{
c2d = Handle(Geom2d_Curve)::DownCast(c2d->Copy());
gp_Vec2d V(shift, 0.);
c2d->Translate(V);
pppf.SetX(pppf.X() + shift);
pppl.SetX(pppl.X() + shift);
}
Standard_Real dUmax = umax + shift - x;
shiftNext.SetX(dUmax > 0 ? -myUPeriod : myUPeriod);
nbIter = (Standard_Integer)(1 + Abs(dUmax) / myUPeriod);
shift = ShapeAnalysis::AdjustByPeriod(posf.X(), x, myUPeriod);
posf.SetX(posf.X() + shift);
shift = ShapeAnalysis::AdjustByPeriod(posl.X(), x, myUPeriod);
posl.SetX(posl.X() + shift);
}
else if (closedDir > 0)
{
Standard_Real y = line.Location().Y();
Standard_Real shift = ShapeAnalysis::AdjustToPeriod(vmin, y - myVPeriod, y);
if (shift != 0.)
{
c2d = Handle(Geom2d_Curve)::DownCast(c2d->Copy());
gp_Vec2d V(0., shift);
c2d->Translate(V);
pppf.SetY(pppf.Y() + shift);
pppl.SetY(pppl.Y() + shift);
}
Standard_Real dVmax = vmax + shift - y;
shiftNext.SetY(dVmax > 0 ? -myVPeriod : myVPeriod);
nbIter = (Standard_Integer)(1 + Abs(dVmax) / myVPeriod);
shift = ShapeAnalysis::AdjustByPeriod(posf.Y(), y, myVPeriod);
posf.SetY(posf.Y() + shift);
shift = ShapeAnalysis::AdjustByPeriod(posl.Y(), y, myVPeriod);
posl.SetY(posl.Y() + shift);
}
}
// detect intersections at junction of two edges
gp_Pnt2d pos = (isreversed ? posl : posf);
Standard_Real dev;
Standard_Integer code = PointLinePosition(pos, line, dev);
if (iedge == 1)
{
firstCode = code;
firstPos = pos;
firstDev = dev;
}
else if (code == IOR_UNDEF || code != prevCode)
{
if (!closedDir || Abs(dev - prevDev) < halfPeriod)
{
// clang-format off
IntLinePar.Append ( ParamPointsOnLine ( pos, prevPos, line ) ); // !! - maybe compute exactly ?
// clang-format on
IntEdgePar.Append(isreversed ? l : f);
IntEdgeInd.Append(iedge);
}
}
// fill data on end point (for next edge)
pos = (isreversed ? posf : posl);
prevCode = PointLinePosition(pos, line, prevDev);
prevPos = pos;
// cycle with shift in order to track all possible intersections
for (Standard_Integer iter = 1; iter <= nbIter; iter++)
{
// data for intersection
IntRes2d_Domain iDom(pppf, f, TOLINT, pppl, l, TOLINT);
Geom2dAdaptor_Curve iGAC(c2d);
// intersection
Geom2dInt_GInter Inter;
Inter.Perform(jGAC, /*jDom,*/ iGAC, iDom, TOLINT, TOLINT);
// Fill arrays with new intersection points
if (Inter.IsDone())
{
Standard_Integer i;
for (i = 1; i <= Inter.NbPoints(); i++)
{
IntRes2d_IntersectionPoint IP = Inter.Point(i);
if (IP.TransitionOfSecond().PositionOnCurve() == IntRes2d_Middle
|| (code != IOR_UNDEF && prevCode != IOR_UNDEF))
{
IntLinePar.Append(IP.ParamOnFirst());
IntEdgePar.Append(IP.ParamOnSecond());
}
}
for (i = 1; i <= Inter.NbSegments(); i++)
{
IntRes2d_IntersectionSegment IS = Inter.Segment(i);
if (IS.HasFirstPoint())
{
IntRes2d_IntersectionPoint IP = IS.FirstPoint();
IntLinePar.Append(IP.ParamOnFirst());
IntEdgePar.Append(IP.ParamOnSecond());
}
if (IS.HasLastPoint())
{
IntRes2d_IntersectionPoint IP = IS.LastPoint();
IntLinePar.Append(IP.ParamOnFirst());
IntEdgePar.Append(IP.ParamOnSecond());
}
}
}
if (iter < nbIter)
{
if (iter == 1)
c2d = Handle(Geom2d_Curve)::DownCast(c2d->Copy());
pppf += shiftNext.XY();
pppl += shiftNext.XY();
c2d->Translate(shiftNext);
}
}
Standard_Integer start = IntEdgeInd.Length() + 1; // first of the new points
// Move all points into range [f,l] (intersector sometimes gives params out of range)
Standard_Integer i;
for (i = start; i <= IntEdgePar.Length(); i++)
{
if (IntEdgePar(i) < f)
IntEdgePar.SetValue(i, f);
else if (IntEdgePar(i) > l)
IntEdgePar.SetValue(i, l);
}
// Sort by parameter on edge
for (i = IntEdgePar.Length(); i > start; i--)
for (Standard_Integer j = start; j < i; j++)
{
if (isreversed == (IntEdgePar(j + 1) < IntEdgePar(j)))
continue;
IntLinePar.Exchange(j, j + 1);
IntEdgePar.Exchange(j, j + 1);
}
// and fill indices
for (i = start; i <= IntEdgePar.Length(); i++)
IntEdgeInd.Append(iedge);
// Detect intersection at closing point
// Only wires which are not EXTERNAL are considered (as closed)
if (iedge == nbe && wire.Orientation() != TopAbs_EXTERNAL
&& wire.Orientation() != TopAbs_INTERNAL
&& (prevCode == IOR_UNDEF || prevCode != firstCode))
{
if (!closedDir || Abs(firstDev - prevDev) < halfPeriod)
{
IntLinePar.Append(ParamPointsOnLine(pos, firstPos, line));
IntEdgePar.Append(isreversed ? f : l);
IntEdgeInd.Append(iedge);
}
}
}
if (IntEdgePar.Length() < 1)
{
// pdn Defining position of wire. There is no intersection, so by any point.
// DefinePatchForWire ( wire, firstCode, isCutByU, cutIndex );
return Standard_False; // pdn ??
}
//======================================
// Fill sequence of transition codes for intersection points
TColStd_SequenceOfInteger IntCode; // parameter of intersection point on line
TColStd_SequenceOfInteger SegmentCodes; // classification codes for segments of wire
// remove duplicated points to ensure correct results of ComputeCode
Standard_Integer i, j = IntEdgePar.Length();
if (myClosedMode && j > 1)
{
for (i = 1; i <= IntEdgePar.Length();)
{
if (i == j)
break;
if (IntEdgeInd(i) == IntEdgeInd(j)
&& Abs(IntEdgePar(i) - IntEdgePar(j)) < ::Precision::PConfusion())
{
IntLinePar.Remove(i);
IntEdgePar.Remove(i);
IntEdgeInd.Remove(i);
if (j > i)
j--;
continue;
}
else if (nbe == 1 || IntEdgeInd(i) == (IntEdgeInd(j) % nbe) + 1)
{
TopoDS_Edge E1 = sewd->Edge(IntEdgeInd(j));
TopoDS_Edge E2 = sewd->Edge(IntEdgeInd(i));
Standard_Real a1, b1, a2, b2;
BRep_Tool::Range(E1, myFace, a1, b1);
BRep_Tool::Range(E2, myFace, a2, b2);
if (Abs(IntEdgePar(j) - (E1.Orientation() == TopAbs_FORWARD ? b1 : a1))
< ::Precision::PConfusion()
&& Abs(IntEdgePar(i) - (E2.Orientation() == TopAbs_FORWARD ? a2 : b2))
< ::Precision::PConfusion())
{
IntLinePar.Remove(i);
IntEdgePar.Remove(i);
IntEdgeInd.Remove(i);
if (j > i)
j--;
continue;
}
}
j = i++;
}
}
// sequence of real codes for each segment
TColStd_SequenceOfInteger aNewSegCodes;
// Compute segment codes (left side of line, right or tangential)
for (i = 1; i <= IntEdgePar.Length(); i++)
{
j = (i < IntEdgePar.Length() ? i + 1 : 1);
Standard_Integer code = ComputeCode(sewd,
line,
IntEdgeInd(i),
IntEdgeInd(j),
IntEdgePar(i),
IntEdgePar(j),
isnonmanifold);
SegmentCodes.Append(code);
}
// for EXTERNAL wire, i.e. another joint line, every point is double intersection
if (wire.Orientation() == TopAbs_EXTERNAL)
{
for (i = 1; i <= IntEdgePar.Length(); i++)
{
IntCode.Append(ITP_TANG | IOR_BOTH);
aNewSegCodes.Append(SegmentCodes(i));
}
}
// For real (closed) wire, analyze tangencies
else
{
if (wire.Orientation() != TopAbs_INTERNAL)
{
// Two consecutive tangential segments are considered as one, merge them.
for (i = 1; i <= IntEdgePar.Length(); i++)
{
j = (i > 1 ? i - 1 : IntEdgePar.Length());
int k = (i < IntEdgePar.Length() ? i + 1 : 1); // [ACIS22539]
if (SegmentCodes(j) == IOR_UNDEF && SegmentCodes(i) == IOR_UNDEF)
{
// Very specific case when the constructed seam edge
// overlaps with spur edge [ACIS22539]
if (myClosedMode
&& (IntLinePar(i) - IntLinePar(j)) * (IntLinePar(k) - IntLinePar(i)) <= 0.)
continue;
IntEdgeInd.Remove(i);
IntEdgePar.Remove(i);
IntLinePar.Remove(i);
SegmentCodes.Remove(i);
i--;
}
}
}
// pdn exit if all split points removed
if (IntEdgePar.Length() < 1)
{
return Standard_False; // pdn ??
}
// Analyze type of intersection point and encode it
// Three kinds of points (ITP): clear intersection, tangency in-point,
// beginning and end of tangential segment.
// Orientation (IOR) tells on which side of line edge crosses it
j = IntEdgePar.Length();
for (i = 1; i <= IntEdgePar.Length(); j = i++)
{
Standard_Integer codej = SegmentCodes(j);
Standard_Integer codei = SegmentCodes(i);
if (myClosedMode)
{
if ((codej & IOR_BOTH) == IOR_BOTH) // IOR_LEFT : IOR_RIGHT
codej = (codej & IOR_POS ? IOR_RIGHT : IOR_LEFT);
if ((codei & IOR_BOTH) == IOR_BOTH) // IOR_RIGHT : IOR_LEFT
codei = (codei & IOR_POS ? IOR_LEFT : IOR_RIGHT);
aNewSegCodes.Append(codei);
if (IntEdgeInd(i) == IntEdgeInd(j))
aNewSegCodes.Append(codej);
}
else
aNewSegCodes.Append(codei);
Standard_Integer ipcode = (codej | codei);
if (codej == IOR_UNDEF)
{ // previous segment was tangency
if (IntLinePar(i) > IntLinePar(j))
ipcode |= ITP_ENDSEG; // end of segment
else
ipcode |= ITP_BEGSEG; // beginning of segment
}
else if (codei == IOR_UNDEF)
{ // current segment is tangency
if (IntLinePar(i < IntLinePar.Length() ? i + 1 : 1) > IntLinePar(i))
ipcode |= ITP_BEGSEG; // beginning of segment
else
ipcode |= ITP_ENDSEG; // end of segment
}
// internal wire can be only tangent
else if (i == j)
ipcode |= ((ipcode & IOR_BOTH) == IOR_BOTH && !isnonmanifold ? ITP_INTER : ITP_TANG);
else if (codei == codej || isnonmanifold)
ipcode |= ITP_TANG; // tangency in-point
else
ipcode |= ITP_INTER; // standard crossing
IntCode.Append(ipcode);
}
}
//=======================================
// Split edges in the wire by intersection points and fill vertices array
TopTools_SequenceOfShape IntVertices;
wire = SplitWire(wire, IntEdgeInd, IntEdgePar, IntVertices, aNewSegCodes, isCutByU, cutIndex);
// add all data to input arrays
for (i = 1; i <= IntLinePar.Length(); i++)
{
SplitLinePar.Append(IntLinePar(i));
SplitLineCode.Append(IntCode(i));
SplitLineVertex.Append(IntVertices(i));
}
return Standard_True;
}
//=================================================================================================
void ShapeFix_ComposeShell::SplitByLine(ShapeFix_SequenceOfWireSegment& wires,
const gp_Lin2d& line,
const Standard_Boolean isCutByU,
const Standard_Integer cutIndex)
{
TColStd_SequenceOfReal SplitLinePar;
TColStd_SequenceOfInteger SplitLineCode;
TopTools_SequenceOfShape SplitLineVertex;
// split wires one by one, collecting data on intersection points
Standard_Integer i; // svv #1
for (i = 1; i <= wires.Length(); i++)
{
SplitByLine(wires(i), line, isCutByU, cutIndex, SplitLinePar, SplitLineCode, SplitLineVertex);
}
// sort intersection points along parameter on cutting line
for (i = SplitLinePar.Length(); i > 1; i--)
for (Standard_Integer j = 1; j < i; j++)
{
if (SplitLinePar(j) > SplitLinePar(j + 1))
{
SplitLinePar.Exchange(j, j + 1);
SplitLineCode.Exchange(j, j + 1);
SplitLineVertex.Exchange(j, j + 1);
}
}
// merge null-length tangential segments into one-point tangencies or intersections
for (i = 1; i < SplitLinePar.Length(); i++)
{
if (Abs(SplitLinePar(i + 1) - SplitLinePar(i)) > ::Precision::PConfusion()
&& !SplitLineVertex(i).IsSame(SplitLineVertex(i + 1)))
continue;
if ((SplitLineCode(i) & ITP_ENDSEG && SplitLineCode(i + 1) & ITP_BEGSEG)
|| (SplitLineCode(i) & ITP_BEGSEG && SplitLineCode(i + 1) & ITP_ENDSEG))
{
Standard_Integer code = (SplitLineCode(i) | SplitLineCode(i + 1)) & IOR_BOTH;
SplitLineCode.SetValue(i, code | (code == IOR_BOTH ? ITP_INTER : ITP_TANG));
SplitLinePar.Remove(i + 1);
SplitLineCode.Remove(i + 1);
SplitLineVertex.Remove(i + 1);
}
}
// go along line, split it by intersection points and create edges
// (only for internal parts, in particular not for tangential segments)
BRep_Builder B;
Standard_Integer parity = 0; // 0 - out, 1 - in
Standard_Integer halfparity = 0; // left/right for tangential segments
Standard_Integer tanglevel = 0; // tangency nesting level
for (i = 1; i <= SplitLinePar.Length(); i++)
{
Standard_Integer code = SplitLineCode(i);
Standard_Boolean interior = (!tanglevel && parity % 2); // create an edge
if (code & ITP_INTER)
{ // crossing
parity++;
}
else if (code & ITP_BEGSEG)
{ // beginning of tangential segment
tanglevel++;
if (!halfparity)
halfparity = (code & IOR_BOTH);
else if (halfparity != (code & IOR_BOTH))
parity++;
}
else if (code & ITP_ENDSEG)
{ // end of tangential segment
tanglevel--;
if (!halfparity)
halfparity = (code & IOR_BOTH);
else if (halfparity != (code & IOR_BOTH))
parity++;
}
if (tanglevel < 0)
{
// myStatus |= ShapeExtend::EncodeStatus ( ShapeExtend_FAIL4 );
#ifdef OCCT_DEBUG
std::cout << "Warning: ShapeFix_ComposeShell::SplitByLine: tangency level <0 !" << std::endl;
#endif
}
if (!interior)
continue;
// apply context to vertices (to perform replacing/merging vertices)
// smh#8
TopoDS_Shape tmpV1 = Context()->Apply(SplitLineVertex(i - 1));
TopoDS_Shape tmpV2 = Context()->Apply(SplitLineVertex(i));
TopoDS_Vertex V1 = TopoDS::Vertex(tmpV1);
TopoDS_Vertex V2 = TopoDS::Vertex(tmpV2);
// protection against creating null-length edges or edges lying inside tolerance of vertices
// first and last vertices for split line can not be merged to each other
Standard_Boolean canbeMerged = (/*myClosedMode &&*/ (i - 1 > 1 || i < SplitLinePar.Length()));
Standard_Real aMaxTol = MaxTolerance();
// case when max tolerance is not defined tolerance of vertices will be used as is
if (aMaxTol <= 2. * Precision::Confusion())
aMaxTol = Precision::Infinite();
Standard_Real aTol1 = Min(BRep_Tool::Tolerance(V1), aMaxTol);
Standard_Real aTol2 = Min(BRep_Tool::Tolerance(V2), aMaxTol);
gp_Pnt aP1 = BRep_Tool::Pnt(V1);
gp_Pnt aP2 = BRep_Tool::Pnt(V2);
Standard_Real aD = aP1.SquareDistance(aP2);
if (SplitLinePar(i) - SplitLinePar(i - 1) < ::Precision::PConfusion()
|| (canbeMerged && (aD <= (aTol1 * aTol1) || aD <= (aTol2 * aTol2))))
{ // BRepTools::Compare(V1, V2)) ) {
#ifdef OCCT_DEBUG
std::cout << "Info: ShapeFix_ComposeShell::SplitByLine: Short segment ignored" << std::endl;
#endif
if (!V1.IsSame(V2))
{ // merge coincident vertices
ShapeBuild_Vertex sbv;
TopoDS_Vertex V = sbv.CombineVertex(V1, V2);
Context()->Replace(V1, V.Oriented(V1.Orientation()));
Context()->Replace(V2, V.Oriented(V2.Orientation()));
V1 = V2 = V;
#ifdef OCCT_DEBUG
std::cout << "Info: ShapeFix_ComposeShell::SplitByLine: Coincided vertices merged"
<< std::endl;
#endif
}
continue;
}
// create an edge (without 3d curve), put it in wire segment and add to sequence
// NOTE: i here is always >1
TopoDS_Edge edge;
B.MakeEdge(edge);
V1.Orientation(TopAbs_FORWARD);
V2.Orientation(TopAbs_REVERSED);
B.Add(edge, V1);
B.Add(edge, V2);
Handle(Geom2d_Line) Lin1 = new Geom2d_Line(line);
Handle(Geom2d_Line) Lin2 = new Geom2d_Line(line);
B.UpdateEdge(edge, Lin1, Lin2, myFace, ::Precision::Confusion());
B.Range(edge, myFace, SplitLinePar(i - 1), SplitLinePar(i));
Handle(ShapeExtend_WireData) sbwd = new ShapeExtend_WireData;
sbwd->Add(edge);
ShapeFix_WireSegment seg(sbwd, TopAbs_EXTERNAL);
// set patch indices
DefinePatch(seg, IOR_UNDEF, isCutByU, cutIndex);
if (!isCutByU)
{
Standard_Real shiftU =
(myClosedMode && myUClosed ? ShapeAnalysis::AdjustToPeriod(SplitLinePar(i - 1) - TOLINT,
myGrid->UJointValue(1),
myGrid->UJointValue(2))
: 0.);
Standard_Real aPar = SplitLinePar(i - 1) + shiftU;
seg.DefineIUMin(
1,
GetPatchIndex(aPar + ::Precision::PConfusion(), myGrid->UJointValues(), myUClosed));
seg.DefineIUMax(
1,
GetPatchIndex(aPar - ::Precision::PConfusion(), myGrid->UJointValues(), myUClosed) + 1);
}
else
{
Standard_Real shiftV =
(myClosedMode && myVClosed ? ShapeAnalysis::AdjustToPeriod(SplitLinePar(i - 1) - TOLINT,
myGrid->VJointValue(1),
myGrid->VJointValue(2))
: 0.);
Standard_Real aPar = SplitLinePar(i - 1) + shiftV;
seg.DefineIVMin(
1,
GetPatchIndex(aPar + ::Precision::PConfusion(), myGrid->VJointValues(), myVClosed));
seg.DefineIVMax(
1,
GetPatchIndex(aPar - ::Precision::PConfusion(), myGrid->VJointValues(), myVClosed) + 1);
}
wires.Append(seg);
}
if (parity % 2)
{
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_FAIL4);
#ifdef OCCT_DEBUG
std::cout << "Error: ShapeFix_ComposeShell::SplitByLine: parity error" << std::endl;
#endif
}
// Apply context to all wires to perform all recorded replacements/merging
for (i = 1; i <= wires.Length(); i++)
{
for (Standard_Integer j = 1; j <= wires(i).NbEdges();)
j += ApplyContext(wires(i), j, Context());
}
}
//=================================================================================================
void ShapeFix_ComposeShell::SplitByGrid(ShapeFix_SequenceOfWireSegment& seqw)
{
// process splitting by U- anv V-seams (i.e. U=const and V=const curves)
// closed composite surface is processed as periodic
Standard_Real Uf, Ul, Vf, Vl;
BRepTools::UVBounds(myFace, Uf, Ul, Vf, Vl);
Standard_Real Umin, Umax, Vmin, Vmax;
myGrid->Bounds(Umin, Umax, Vmin, Vmax);
// value of precision to define number of patch should be the same as used in the definitin
// position of point relatively to seam edge (TOLINT)
Standard_Real pprec = TOLINT; //::Precision::PConfusion();
Standard_Integer i = 1;
if (myClosedMode)
{
// for closed mode when only one patch exist and location of the splitting line is coincident
// with first joint value Therefore in this case it is necessary to move all wire segments in
// the range of the patch between first and last joint values. Then all wire segments are lie
// between -period and period in order to have valid split ranges after splitting. Because for
// closed mode cut index always equal to 1 and parts of segments after splitting always should
// have index either (0,1) or (1,2).
for (i = 1; i <= seqw.Length(); i++)
{
ShapeFix_WireSegment& wire = seqw(i);
TopoDS_Shape atmpF = myFace.EmptyCopied();
BRep_Builder aB;
atmpF.Orientation(TopAbs_FORWARD);
aB.Add(atmpF, wire.WireData()->Wire());
Standard_Real Uf1, Ul1, Vf1, Vl1;
ShapeAnalysis::GetFaceUVBounds(TopoDS::Face(atmpF), Uf1, Ul1, Vf1, Vl1);
// for closed mode it is necessary to move wire segment in the interval defined by first and
// last grid UV values
Standard_Real shiftU =
(myClosedMode && myUClosed ? ShapeAnalysis::AdjustToPeriod(Ul1 - pprec,
myGrid->UJointValue(1),
myGrid->UJointValue(2))
: 0.);
Standard_Real shiftV =
(myClosedMode && myVClosed ? ShapeAnalysis::AdjustToPeriod(Vl1 - pprec,
myGrid->VJointValue(1),
myGrid->VJointValue(2))
: 0.);
Uf1 += shiftU;
Ul1 += shiftU;
Vf1 += shiftV;
Vl1 += shiftV;
// limit patch indices to be in range of grid (extended for periodic) (0, 2)
// in same cases for example trj4_pm2-ug-203.stp (entity #8024) wire in 2D space has length
// greater then period
Standard_Integer iumin =
Max(0, GetPatchIndex(Uf1 + pprec, myGrid->UJointValues(), myUClosed));
Standard_Integer iumax = GetPatchIndex(Ul1 - pprec, myGrid->UJointValues(), myUClosed) + 1;
for (Standard_Integer j = 1; j <= wire.NbEdges(); j++)
{
wire.DefineIUMin(j, iumin);
wire.DefineIUMax(j, iumax);
}
Standard_Integer ivmin =
Max(0, GetPatchIndex(Vf1 + pprec, myGrid->VJointValues(), myVClosed));
Standard_Integer ivmax = GetPatchIndex(Vl1 - pprec, myGrid->VJointValues(), myVClosed) + 1;
for (Standard_Integer j = 1; j <= wire.NbEdges(); j++)
{
wire.DefineIVMin(j, ivmin);
wire.DefineIVMax(j, ivmax);
}
}
}
else
{
// limit patch indices to be in range of grid (extended for periodic)
Standard_Integer iumin = GetPatchIndex(Uf + pprec, myGrid->UJointValues(), myUClosed);
Standard_Integer iumax = GetPatchIndex(Ul - pprec, myGrid->UJointValues(), myUClosed) + 1;
for (i = 1; i <= seqw.Length(); i++)
{
ShapeFix_WireSegment& wire = seqw(i);
for (Standard_Integer j = 1; j <= wire.NbEdges(); j++)
{
wire.DefineIUMin(j, iumin);
wire.DefineIUMax(j, iumax);
}
}
Standard_Integer ivmin = GetPatchIndex(Vf + pprec, myGrid->VJointValues(), myVClosed);
Standard_Integer ivmax = GetPatchIndex(Vl - pprec, myGrid->VJointValues(), myVClosed) + 1;
for (i = 1; i <= seqw.Length(); i++)
{
ShapeFix_WireSegment& wire = seqw(i);
for (Standard_Integer j = 1; j <= wire.NbEdges(); j++)
{
wire.DefineIVMin(j, ivmin);
wire.DefineIVMax(j, ivmax);
}
}
}
// split by u lines
for (i = (myUClosed ? 1 : 2); i <= myGrid->NbUPatches(); i++)
{
// clang-format off
gp_Pnt2d pos ( myGrid->UJointValue(i), 0. ); // 0. - for infinite ranges: myGrid->VJointValue(1) ;
// clang-format on
gp_Lin2d line(pos, gp_Dir2d(0., 1.));
if (!myClosedMode && myUClosed)
{
Standard_Real period = Umax - Umin;
Standard_Real X = pos.X();
Standard_Real sh = ShapeAnalysis::AdjustToPeriod(X, Uf, Uf + period);
for (; X + sh <= Ul + pprec; sh += period)
{
gp_Lin2d ln = line.Translated(gp_Vec2d(sh, 0));
Standard_Integer cutIndex =
GetPatchIndex(X + sh + pprec, myGrid->UJointValues(), myUClosed);
SplitByLine(seqw, ln, Standard_True, cutIndex);
}
}
else
SplitByLine(seqw, line, Standard_True, i);
}
// split by v lines
for (i = (myVClosed ? 1 : 2); i <= myGrid->NbVPatches(); i++)
{
gp_Pnt2d pos(0., myGrid->VJointValue(i));
gp_Lin2d line(pos, gp_Dir2d(1., 0.));
if (!myClosedMode && myVClosed)
{
Standard_Real period = Vmax - Vmin;
Standard_Real Y = pos.Y();
Standard_Real sh = ShapeAnalysis::AdjustToPeriod(Y, Vf, Vf + period);
for (; Y + sh <= Vl + pprec; sh += period)
{
gp_Lin2d ln = line.Translated(gp_Vec2d(0, sh));
Standard_Integer cutIndex =
GetPatchIndex(Y + sh + pprec, myGrid->VJointValues(), myVClosed);
SplitByLine(seqw, ln, Standard_False, cutIndex);
}
}
else
SplitByLine(seqw, line, Standard_False, i);
}
}
//=================================================================================================
void ShapeFix_ComposeShell::BreakWires(ShapeFix_SequenceOfWireSegment& seqw)
{
// split all the wires by vertices
TopTools_MapOfShape splitVertices;
ShapeAnalysis_Edge sae;
// first collect splitting vertices
Standard_Integer i; // svv #1
for (i = 1; i <= seqw.Length(); i++)
{
TopAbs_Orientation ori_wire = seqw(i).Orientation();
if (ori_wire != TopAbs_EXTERNAL && ori_wire != TopAbs_INTERNAL)
continue;
Handle(ShapeExtend_WireData) sbwd = seqw(i).WireData();
for (Standard_Integer j = 1; j <= sbwd->NbEdges(); j++)
{
TopoDS_Edge edge = sbwd->Edge(j);
TopAbs_Orientation ori_edge = (ori_wire == TopAbs_EXTERNAL ? ori_wire : edge.Orientation());
if (ori_edge == TopAbs_EXTERNAL)
{
splitVertices.Add(sae.FirstVertex(edge));
splitVertices.Add(sae.LastVertex(edge));
}
}
}
// and then split each vire
// Here each wire is supposed to be connected (while probably not closed)
for (i = 1; i <= seqw.Length(); i++)
{
TopAbs_Orientation ori = seqw(i).Orientation();
ShapeFix_WireSegment wire = seqw(i);
if (wire.IsVertex())
continue;
const Handle(ShapeExtend_WireData)& sbwd = wire.WireData();
// find first vertex for split
Standard_Integer j; // svv #1
for (j = 1; j <= sbwd->NbEdges(); j++)
{
TopoDS_Vertex V = sae.FirstVertex(sbwd->Edge(j));
if (splitVertices.Contains(V))
break;
}
if (j > sbwd->NbEdges())
continue; // splitting not needed
// if first split of closed edge is not its start, make permutation
Standard_Integer shift = 0;
if (j > 1 && !myClosedMode && wire.IsClosed())
{
TopoDS_Vertex V = sae.FirstVertex(sbwd->Edge(1));
if (!splitVertices.Contains(V))
shift = j - 1;
// wire.SetLast ( j-1 );
}
// perform splitting
Standard_Integer nbnew = 0;
ShapeFix_WireSegment newwire;
TopAbs_Orientation curOri = ori;
for (Standard_Integer ind = 1; ind <= sbwd->NbEdges(); ind++)
{
j = 1 + (ind - 1 + shift) % sbwd->NbEdges();
TopoDS_Edge edge = sbwd->Edge(j);
TopoDS_Vertex V = sae.FirstVertex(edge);
if (ind == 1 || splitVertices.Contains(V))
{
if (newwire.NbEdges())
{
newwire.Orientation(curOri);
// ShapeFix_WireSegment seg ( newwire, ori );
seqw.InsertBefore(i++, newwire);
nbnew++;
}
newwire.Clear();
curOri = ori;
}
Standard_Integer iumin, iumax, ivmin, ivmax;
wire.GetPatchIndex(j, iumin, iumax, ivmin, ivmax);
if (ori == TopAbs_INTERNAL && edge.Orientation() == TopAbs_EXTERNAL)
{
curOri = TopAbs_EXTERNAL;
edge.Orientation(TopAbs_FORWARD);
nbnew++;
}
newwire.AddEdge(0, edge, iumin, iumax, ivmin, ivmax);
}
if (nbnew)
{
newwire.Orientation(curOri);
// ShapeFix_WireSegment seg ( newwire, ori );
seqw.SetValue(i, newwire);
}
}
}
//=================================================================================================
// BUC60035 2053: check if wire segment is very short (in order not to skip it)
// 0 - long
// 1 - short even in 2d (to be taken always)
// -1 - short in 3d but not in 2d (to be checked after algo and atteching to
// another wire if alone)
static Standard_Integer IsShortSegment(const ShapeFix_WireSegment& seg,
const TopoDS_Face& myFace,
const Handle(Geom_Surface)& myGrid,
const TopLoc_Location& myLoc,
const Standard_Real UResolution,
const Standard_Real VResolution)
{
TopoDS_Vertex Vf = seg.FirstVertex();
if (!Vf.IsSame(seg.LastVertex()))
return 0;
gp_Pnt pnt = BRep_Tool::Pnt(Vf);
Standard_Real tol = BRep_Tool::Tolerance(Vf);
Standard_Real tol2 = tol * tol;
Standard_Integer code = 1;
ShapeAnalysis_Edge sae;
const Handle(ShapeExtend_WireData)& sbwd = seg.WireData();
for (Standard_Integer i = 1; i <= sbwd->NbEdges(); i++)
{
TopoDS_Edge edge = sbwd->Edge(i);
if (!Vf.IsSame(sae.LastVertex(edge)))
return 0;
Handle(Geom2d_Curve) c2d;
Standard_Real f, l;
if (!sae.PCurve(edge, myFace, c2d, f, l))
continue;
// check 2d
gp_Pnt2d endPnt = c2d->Value(l);
gp_Pnt2d midPnt = c2d->Value((f + l) / 2);
if (!IsCoincided(endPnt, midPnt, UResolution, VResolution, tol))
code = -1;
// check 3d
gp_Pnt midPnt3d = myGrid->Value(midPnt.X(), midPnt.Y());
if (!myLoc.IsIdentity())
midPnt3d.Transform(myLoc.Transformation());
if (midPnt3d.SquareDistance(pnt) > tol2)
return 0;
}
return code;
}
//=================================================================================================
static Standard_Boolean IsSamePatch(const ShapeFix_WireSegment& wire,
const Standard_Integer NU,
const Standard_Integer NV,
Standard_Integer& iumin,
Standard_Integer& iumax,
Standard_Integer& ivmin,
Standard_Integer& ivmax,
const Standard_Boolean extend = Standard_False)
{
// get patch indices for current segment
Standard_Integer jumin, jumax, jvmin, jvmax;
wire.GetPatchIndex(1, jumin, jumax, jvmin, jvmax);
// shift to the same period
Standard_Integer du = 0, dv = 0;
if (jumin - iumin > NU)
du = -(jumin - iumin) / NU;
else if (iumin - jumin > NU)
du = (iumin - jumin) / NU;
if (jvmin - ivmin > NV)
dv = -(jvmin - ivmin) / NV;
else if (ivmin - jvmin > NV)
dv = (ivmin - jvmin) / NV;
if (du)
{
jumin += du * NU;
jumax += du * NU;
}
if (dv)
{
jvmin += dv * NV;
jvmax += dv * NV;
}
// compute common (extended) indices
Standard_Integer iun = Min(iumin, jumin);
Standard_Integer iux = Max(iumax, jumax);
Standard_Integer ivn = Min(ivmin, jvmin);
Standard_Integer ivx = Max(ivmax, jvmax);
Standard_Boolean ok = (iun == iux || iun + 1 == iux) && (ivn == ivx || ivn + 1 == ivx);
if (ok && extend)
{
iumin = iun;
iumax = iux;
ivmin = ivn;
ivmax = ivx;
}
return ok;
}
//=================================================================================================
void ShapeFix_ComposeShell::CollectWires(ShapeFix_SequenceOfWireSegment& wires,
ShapeFix_SequenceOfWireSegment& seqw)
{
ShapeAnalysis_Edge sae;
Standard_Integer i; // svv #1
// Collect information on short closed segments
TColStd_Array1OfInteger shorts(1, seqw.Length());
for (i = 1; i <= seqw.Length(); i++)
{
if (seqw(i).IsVertex() || seqw(i).Orientation() == TopAbs_INTERNAL)
{
wires.Append(seqw(i));
seqw.Remove(i);
i--;
continue;
}
#ifdef OCCT_DEBUG
for (Standard_Integer k = 1; !myClosedMode && k <= seqw(i).NbEdges(); k++)
if (!seqw(i).CheckPatchIndex(k))
{
std::cout << "Warning: ShapeFix_ComposeShell::CollectWires: Wrong patch indices"
<< std::endl;
break;
}
#endif
Standard_Integer isshort =
IsShortSegment(seqw(i), myFace, myGrid, myLoc, myUResolution, myVResolution);
shorts.SetValue(i, isshort);
if (isshort > 0
&& (seqw(i).Orientation() == TopAbs_EXTERNAL
|| (seqw(i).NbEdges() == 1 && //: abv 13.05.02: OCC320 - remove if degenerated
BRep_Tool::Degenerated(seqw(i).Edge(1)))))
{
#ifdef OCCT_DEBUG
std::cout << "Info: ShapeFix_ComposeShell::CollectWires: Short segment ignored" << std::endl;
#endif
seqw(i).Orientation(TopAbs_INTERNAL);
}
}
Handle(ShapeExtend_WireData) sbwd;
gp_Pnt2d endPnt, firstPnt;
gp_Vec2d endTan, firstTan;
TopoDS_Vertex firstV, endV;
TopoDS_Edge firstEdge, lastEdge;
Standard_Real tol = 0;
Standard_Integer iumin = 0, iumax = 0, ivmin = 0, ivmax = 0;
Standard_Real dsu = 0., dsv = 0.;
Standard_Boolean canBeClosed = Standard_False;
for (;;)
{
Standard_Integer index = 0;
Standard_Boolean misoriented = Standard_True, samepatch = Standard_False;
Standard_Boolean reverse = Standard_False, connected = Standard_False;
Standard_Real angle = -M_PI, mindist = RealLast();
Standard_Integer weigth = 0;
Standard_Real shiftu = 0., shiftv = 0.;
// find next segment to connect (or first if sbwd is NULL)
for (i = 1; i <= seqw.Length(); i++)
{
const ShapeFix_WireSegment& seg = seqw.Value(i);
if (seg.IsVertex())
continue;
TopAbs_Orientation anOr = seg.Orientation();
if (anOr == TopAbs_INTERNAL)
continue;
// for first segment, take any
if (sbwd.IsNull())
{
if (shorts(i) > 0)
continue;
if (anOr == TopAbs_EXTERNAL)
continue;
if (anOr == TopAbs_FORWARD)
reverse = Standard_True;
index = i;
seg.GetPatchIndex(1, iumin, iumax, ivmin, ivmax);
misoriented = Standard_False;
dsu = dsv = 0.;
break;
}
// check whether current segment is on the same patch with previous
Standard_Boolean sp =
IsSamePatch(seg, myGrid->NbUPatches(), myGrid->NbVPatches(), iumin, iumax, ivmin, ivmax);
// not same patch has lowest priority
if (!sp && (canBeClosed || (index && samepatch)))
continue;
// try to connect, with the following priorities:
// The name of property Weigth:
// sharing vertex auto
// samepatch = 1 16
// ! sameedge auto
// misorientation = 0 8
// connected in 2d 4
// distance 2
// short auto
// angle ->> PI 1
const Handle(ShapeExtend_WireData)& wire = seg.WireData();
for (Standard_Integer j = 0; j < 2; j++)
{
if (!endV.IsSame(j ? seg.LastVertex() : seg.FirstVertex()))
continue;
// check for misorientation only if nothing better is found
Standard_Boolean misor = (anOr == (j ? TopAbs_REVERSED : TopAbs_FORWARD));
// if ( misor ) continue; // temporarily, to be improved
// returning back by the same edge is lowest priority
if (lastEdge.IsSame(wire->Edge(j ? wire->NbEdges() : 1)))
{
if (!index && !canBeClosed)
{ // || ( sp && ! samepatch ) ) {
index = i;
reverse = j != 0;
connected = Standard_True;
misoriented = misor;
samepatch = sp;
weigth = (sp ? 16 : 0) + (connected ? 8 : 0) + (misor == 0 ? 4 : 0);
dsu = dsv = 0.;
}
continue;
}
// compute starting tangent
gp_Pnt2d lPnt;
gp_Vec2d lVec;
Standard_Integer k;
Standard_Real edgeTol = 0;
for (k = 1; k <= wire->NbEdges(); k++)
{
TopoDS_Shape tmpE = wire->Edge(wire->NbEdges() - k + 1).Reversed();
TopoDS_Edge edge = (j ? TopoDS::Edge(tmpE) : wire->Edge(k));
edgeTol = BRep_Tool::Tolerance(edge);
// if ( sae.GetEndTangent2d ( edge, myFace, Standard_False, lPnt, lVec ) ) break;
if (sae.GetEndTangent2d(edge, myFace, Standard_False, lPnt, lVec, 1.e-3))
break;
}
if (k > wire->NbEdges())
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_FAIL2);
if (myClosedMode)
{
if (myUClosed)
{
shiftu = ShapeAnalysis::AdjustByPeriod(lPnt.X(), endPnt.X(), myUPeriod);
lPnt.SetX(lPnt.X() + shiftu);
}
if (myVClosed)
{
shiftv = ShapeAnalysis::AdjustByPeriod(lPnt.Y(), endPnt.Y(), myVPeriod);
lPnt.SetY(lPnt.Y() + shiftv);
}
}
// short segment is to be taken with highest priority by angle
Standard_Real ang = (shorts(i) > 0 ? M_PI : endTan.Angle(lVec));
if (myClosedMode && shorts(i) <= 0 && M_PI - ang < ::Precision::Angular())
ang = 0.; // abv 21 Mar 00: trj3_s1-md-214.stp #2471: avoid going back
// abv 05 Feb 02: face from Parasolid: use tolerance of edges for check
// for coincidence (instead of vertex tolerance) in order
// this check to be in agreement with check for position of wire segments
// thus avoiding bad effects on overlapping edges
Standard_Real ctol = Max(edgeTol, BRep_Tool::Tolerance(endV /*lastEdge*/));
Standard_Boolean conn = IsCoincided(endPnt, lPnt, myUResolution, myVResolution, ctol);
Standard_Real dist = endPnt.SquareDistance(lPnt);
// check if case is better than last found
Standard_Integer w1 = (sp ? 16 : 0) + (conn ? 4 : 0) + (misor == 0 ? 8 : 0);
Standard_Integer tail1 = (!conn && (dist < mindist) ? 2 : 0) + (ang > angle ? 1 : 0);
Standard_Integer tail2 = (!connected && (dist > mindist) ? 2 : 0) + (ang < angle ? 1 : 0);
if (w1 + tail1 <= weigth + tail2)
continue;
index = i;
reverse = j != 0;
angle = ang;
mindist = dist;
connected = conn;
misoriented = misor;
samepatch = sp;
weigth = w1;
dsu = shiftu;
dsv = shiftv;
}
}
// if next segment found, connect it
if (index)
{
if (misoriented)
myInvertEdgeStatus = Standard_True;
ShapeFix_WireSegment seg = seqw.Value(index);
if (sbwd.IsNull())
sbwd = new ShapeExtend_WireData;
else if (samepatch)
{ // extend patch indices
IsSamePatch(seg,
myGrid->NbUPatches(),
myGrid->NbVPatches(),
iumin,
iumax,
ivmin,
ivmax,
Standard_True);
}
// for closed mode in case if current segment is seam segment it is necessary to detect
// crossing seam edge in order to have possibility to take candidate from other patch
if (myClosedMode)
seg.GetPatchIndex(1, iumin, iumax, ivmin, ivmax);
// TopAbs_Orientation or = seg.Orientation();
if (!reverse)
sbwd->Add(seg.WireData());
else
{
Handle(ShapeExtend_WireData) wire = new ShapeExtend_WireData;
wire->ManifoldMode() = Standard_False;
wire->Add(seg.WireData());
wire->Reverse(myFace);
sbwd->Add(wire);
}
if (seg.Orientation() == TopAbs_EXTERNAL)
seg.Orientation(reverse ? TopAbs_REVERSED : TopAbs_FORWARD);
else
seg.Orientation(TopAbs_INTERNAL);
seqw.SetValue(index, seg);
}
else if (sbwd.IsNull())
break; // stop when no free segments available
// for first segment, remember start point
if (endV.IsNull())
{
firstEdge = sbwd->Edge(1);
firstV = sae.FirstVertex(firstEdge);
// sae.GetEndTangent2d ( firstEdge, myFace, Standard_False, firstPnt, firstTan );
sae.GetEndTangent2d(firstEdge, myFace, Standard_False, firstPnt, firstTan, 1.e-3);
}
// update last edge and vertex (only for not short segments)
Standard_Boolean doupdate = (index && (shorts(index) <= 0 || endV.IsNull()));
if (doupdate)
{
lastEdge = sbwd->Edge(sbwd->NbEdges());
endV = sae.LastVertex(lastEdge);
tol = BRep_Tool::Tolerance(endV);
// BUC60035 2053: iteration on edges is required
Standard_Integer k; // svv #1
for (k = sbwd->NbEdges(); k >= 1; k--)
// if ( sae.GetEndTangent2d ( sbwd->Edge ( k ), myFace, Standard_True, endPnt, endTan ) )
if (sae.GetEndTangent2d(sbwd->Edge(k), myFace, Standard_True, endPnt, endTan, 1.e-3))
break;
if (k < 1)
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_FAIL2);
if (myUClosed)
endPnt.SetX(endPnt.X() + dsu);
if (myVClosed)
endPnt.SetY(endPnt.Y() + dsv);
}
// if closed or no next segment found, add to wires
canBeClosed = endV.IsSame(firstV);
if (!index
|| (canBeClosed && !lastEdge.IsSame(firstEdge) && // cylinder (seam)
IsCoincided(endPnt, firstPnt, myUResolution, myVResolution, 2. * tol)))
{
if (!endV.IsSame(sae.FirstVertex(firstEdge)))
{
myStatus |= ShapeExtend::EncodeStatus(ShapeExtend_FAIL5);
#ifdef OCCT_DEBUG
std::cout << "Warning: ShapeFix_ComposeShell::CollectWires: can't close wire" << std::endl;
#endif
}
ShapeFix_WireSegment s(sbwd, TopAbs_FORWARD);
s.DefineIUMin(1, iumin);
s.DefineIUMax(1, iumax);
s.DefineIVMin(1, ivmin);
s.DefineIVMax(1, ivmax);
wires.Append(s);
sbwd.Nullify();
endV.Nullify();
canBeClosed = Standard_False;
}
}
// Check if some wires are short in 3d (lie entirely inside one vertex),
// and if yes try to merge them with others
// pdn The short seqments are still placed in "in" sequence.
for (i = 1; i <= seqw.Length(); i++)
{
if (shorts(i) != 1 || seqw(i).IsVertex() || seqw(i).Orientation() == TopAbs_INTERNAL
|| seqw(i).Orientation() == TopAbs_EXTERNAL)
continue;
// find any other wire containing the same vertex
Handle(ShapeExtend_WireData) wd = seqw(i).WireData();
TopoDS_Vertex V = seqw(i).FirstVertex();
Standard_Integer minj = 0, mink = 0;
gp_Pnt2d p2d;
gp_Vec2d vec;
Standard_Real mindist = 0;
Standard_Boolean samepatch = Standard_False;
// Standard_Integer iumin, iumax, ivmin, ivmax;
seqw(i).GetPatchIndex(1, iumin, iumax, ivmin, ivmax);
sae.GetEndTangent2d(wd->Edge(1), myFace, Standard_False, p2d, vec);
for (Standard_Integer j = 1; j <= wires.Length(); j++)
{
// if ( j == i ) continue;
// Handle(ShapeExtend_WireData)
sbwd = wires(j).WireData();
for (Standard_Integer k = 1; k <= sbwd->NbEdges(); k++)
{
// clang-format off
if ( !V.IsSame ( sae.FirstVertex ( sbwd->Edge(k) ) ) ) continue; //pdn I suppose that short segment should be inserted into the SAME vertex.
// clang-format on
Standard_Boolean sp = IsSamePatch(wires(j),
myGrid->NbUPatches(),
myGrid->NbVPatches(),
iumin,
iumax,
ivmin,
ivmax);
if (samepatch && !sp)
continue;
gp_Pnt2d pp;
sae.GetEndTangent2d(sbwd->Edge(k), myFace, Standard_False, pp, vec);
Standard_Real dist = pp.SquareDistance(p2d);
if (sp && !samepatch)
{
minj = j;
mink = k;
mindist = dist;
samepatch = sp;
}
else if (!minj || mindist > dist)
{
minj = j;
mink = k;
mindist = dist;
samepatch = sp;
}
}
}
if (!minj)
{
// pdn add into resulting sequence!
ShapeFix_WireSegment s(wd, TopAbs_FORWARD);
wires.Append(s);
#ifdef OCCT_DEBUG
std::cout << "Warning: Short segment processed as separate wire" << std::endl;
#endif
continue;
}
// and if found, merge
// Handle(ShapeExtend_WireData)
sbwd = wires(minj).WireData();
for (Standard_Integer n = 1; n <= wd->NbEdges(); n++)
sbwd->Add(wd->Edge(n), mink++);
// wires.Remove ( i );
// i--;
}
}
//=================================================================================================
static gp_Pnt2d GetMiddlePoint(const ShapeFix_WireSegment& wire, const TopoDS_Face& face)
{
if (wire.IsVertex())
{
TopoDS_Vertex aV = wire.GetVertex();
gp_Pnt aP3D = BRep_Tool::Pnt(aV);
Handle(Geom_Surface) surf = BRep_Tool::Surface(face);
Handle(ShapeAnalysis_Surface) aSurfTool = new ShapeAnalysis_Surface(surf);
return aSurfTool->ValueOfUV(aP3D, Precision::Confusion());
}
Bnd_Box2d box;
ShapeAnalysis_Edge sae;
ShapeAnalysis_Curve sac;
const Handle(ShapeExtend_WireData)& wd = wire.WireData();
for (Standard_Integer i = 1; i <= wd->NbEdges(); i++)
{
TopoDS_Edge E = wd->Edge(i);
Standard_Real cf, cl;
Handle(Geom2d_Curve) c2d;
if (sae.PCurve(E, face, c2d, cf, cl, Standard_False))
{
sac.FillBndBox(c2d, cf, cl, 3, Standard_False, box);
// box.Add(c2d->Value(cf));
// box.Add(c2d->Value(cl));
// box.Add(c2d->Value((cl+cf)/2.));
}
}
if (box.IsVoid())
return gp_Pnt2d(0., 0.);
Standard_Real aXmin, aYmin, aXmax, aYmax;
box.Get(aXmin, aYmin, aXmax, aYmax);
return gp_Pnt2d(0.5 * (aXmax + aXmin), 0.5 * (aYmax + aYmin));
}
//=================================================================================================
void ShapeFix_ComposeShell::MakeFacesOnPatch(TopTools_SequenceOfShape& faces,
const Handle(Geom_Surface)& surf,
TopTools_SequenceOfShape& loops) const
{
BRep_Builder B;
// Case of single loop: just add it to face
if (loops.Length() == 1)
{
TopoDS_Face newFace;
B.MakeFace(newFace, surf, myLoc, ::Precision::Confusion());
const TopoDS_Shape& aSH = loops.Value(1);
if (aSH.ShapeType() != TopAbs_WIRE)
return;
TopoDS_Wire wire = TopoDS::Wire(loops.Value(1));
B.Add(newFace, wire);
if (myInvertEdgeStatus)
{
Handle(ShapeFix_Face) sff = new ShapeFix_Face(newFace);
sff->FixAddNaturalBoundMode() = Standard_False;
TopTools_DataMapOfShapeListOfShape MapWires;
MapWires.Clear();
sff->FixOrientation(MapWires);
newFace = sff->Face();
}
faces.Append(newFace);
return;
}
// For several loops, first find roots
// make pseudo-face,
TopoDS_Face pf;
B.MakeFace(pf, surf, myLoc, ::Precision::Confusion());
Handle(Geom_Surface) atSurf = BRep_Tool::Surface(pf);
Handle(ShapeAnalysis_Surface) aSurfTool = new ShapeAnalysis_Surface(atSurf);
TopTools_SequenceOfShape roots;
Standard_Integer i; // svv #1
for (i = 1; i <= loops.Length(); i++)
{
gp_Pnt2d unp;
TopoDS_Wire wr;
TopoDS_Shape aShape = loops(i);
if (aShape.ShapeType() != TopAbs_WIRE
|| (aShape.Orientation() != TopAbs_FORWARD && aShape.Orientation() != TopAbs_REVERSED))
continue;
wr = TopoDS::Wire(loops(i));
TopoDS_Iterator ew(wr);
if (!ew.More())
continue;
TopoDS_Edge ed = TopoDS::Edge(ew.Value());
while (ed.Orientation() != TopAbs_FORWARD && ed.Orientation() != TopAbs_REVERSED)
{
ew.Next();
if (ew.More())
ed = TopoDS::Edge(ew.Value());
else
break;
}
if (!ew.More())
continue;
Standard_Real cf, cl;
Handle(Geom2d_Curve) cw = BRep_Tool::CurveOnSurface(ed, pf, cf, cl);
if (cw.IsNull())
continue;
unp = cw->Value(0.5 * (cf + cl));
Standard_Integer j; // svv #1
for (j = 1; j <= loops.Length(); j++)
{
if (i == j)
continue;
TopoDS_Shape aShape2 = loops(j);
if (aShape2.ShapeType() != TopAbs_WIRE
|| (aShape2.Orientation() != TopAbs_FORWARD && aShape2.Orientation() != TopAbs_REVERSED))
continue;
TopoDS_Wire w1 = TopoDS::Wire(aShape2);
TopoDS_Wire awtmp;
B.MakeWire(awtmp);
awtmp.Orientation(TopAbs_FORWARD);
TopoDS_Iterator aIt(w1);
Standard_Integer nbe = 0;
for (; aIt.More(); aIt.Next())
{
if (aIt.Value().Orientation() == TopAbs_FORWARD
|| aIt.Value().Orientation() == TopAbs_REVERSED)
{
B.Add(awtmp, aIt.Value());
nbe++;
}
}
if (!nbe)
continue;
TopoDS_Face fc;
B.MakeFace(fc, surf, myLoc, ::Precision::Confusion());
B.Add(fc, awtmp);
BRepTopAdaptor_FClass2d clas(fc, ::Precision::PConfusion());
TopAbs_State stPoint = clas.Perform(unp, Standard_False);
if (stPoint == TopAbs_ON || stPoint == TopAbs_UNKNOWN)
{
TopoDS_Edge anEdge = TopoDS::Edge(ew.Value());
Standard_Real aCF, aCL;
Handle(Geom2d_Curve) aCW = BRep_Tool::CurveOnSurface(anEdge, pf, aCF, aCL);
// handle tangential case (ON)
while (stPoint == TopAbs_ON || stPoint == TopAbs_UNKNOWN)
{
stPoint = clas.Perform(aCW->Value(aCL), Standard_False);
if (!ew.More())
break;
ew.Next();
if (!ew.More())
break;
TopoDS_Edge edge = TopoDS::Edge(ew.Value());
if (edge.Orientation() != TopAbs_FORWARD && edge.Orientation() != TopAbs_REVERSED)
continue;
Handle(Geom2d_Curve) c2d = BRep_Tool::CurveOnSurface(edge, pf, aCF, aCL);
if (!c2d.IsNull())
aCW = c2d;
}
}
TopAbs_State stInfin = clas.PerformInfinitePoint();
if (stPoint != stInfin)
break;
}
if (j > loops.Length())
{
roots.Append(wr);
// loops.Remove ( i-- );
}
}
// And remove them from the list of loops
for (i = 1; i <= loops.Length(); i++)
for (Standard_Integer j = 1; j <= roots.Length(); j++)
if (loops(i).IsSame(roots(j)))
{
loops.Remove(i--);
break;
}
// check for lost wires, and if they are, make them roots
if (roots.Length() <= 0 && loops.Length() > 0)
{
#ifdef OCCT_DEBUG
std::cout << "Error: ShapeFix_ComposeShell::MakeFacesOnPatch: can't dispatch wires"
<< std::endl;
#endif
for (Standard_Integer j = 1; j <= loops.Length(); j++)
{
roots.Append(loops(j));
}
loops.Clear();
}
// Then iterate on loops
for (i = 1; i <= roots.Length(); i++)
{
Standard_Boolean reverse = Standard_False;
TopoDS_Wire wire = TopoDS::Wire(roots(i));
TopoDS_Face fc;
B.MakeFace(fc, surf, myLoc, ::Precision::Confusion());
B.Add(fc, wire);
BRepTopAdaptor_FClass2d clas(fc, ::Precision::PConfusion());
if (clas.PerformInfinitePoint() == TopAbs_IN)
{
reverse = Standard_True;
#ifdef OCCT_DEBUG
std::cout << "Warning: ShapeFix_ComposeShell::MakeFacesOnPatch: badly oriented wire"
<< std::endl;
#endif
}
// find all holes for that loop
TopTools_SequenceOfShape holes; // holes in holes not supported
Standard_Integer j; // svv #1
for (j = 1; j <= loops.Length(); j++)
{
gp_Pnt2d unp;
if (loops(j).ShapeType() == TopAbs_WIRE)
{
TopoDS_Wire bw = TopoDS::Wire(loops(j));
TopoDS_Iterator ew(bw);
if (!ew.More())
continue;
TopoDS_Edge ed = TopoDS::Edge(ew.Value());
Standard_Real cf, cl;
Handle(Geom2d_Curve) cw = BRep_Tool::CurveOnSurface(ed, pf, cf, cl);
if (cw.IsNull())
continue;
unp = cw->Value(0.5 * (cf + cl));
}
else if (loops(j).ShapeType() == TopAbs_VERTEX)
{
TopoDS_Vertex aV = TopoDS::Vertex(loops(j));
gp_Pnt aP = BRep_Tool::Pnt(aV);
unp = aSurfTool->ValueOfUV(aP, Precision::Confusion());
}
else
continue;
TopAbs_State state = clas.Perform(unp, Standard_False);
if ((state == TopAbs_OUT) == reverse)
{
holes.Append(loops(j));
loops.Remove(j--);
}
}
// and add them to new face (no orienting is done)
TopoDS_Face newFace;
B.MakeFace(newFace, surf, myLoc, ::Precision::Confusion());
B.Add(newFace, wire);
for (j = 1; j <= holes.Length(); j++)
{
TopoDS_Shape aSh = holes(j);
if (aSh.ShapeType() == TopAbs_VERTEX)
{
TopoDS_Vertex aNewV =
ShapeAnalysis_TransferParametersProj::CopyNMVertex(TopoDS::Vertex(aSh), newFace, myFace);
Context()->Replace(aSh, aNewV);
B.Add(newFace, aNewV);
}
else
B.Add(newFace, holes(j));
}
faces.Append(newFace);
// check for lost wires, and if they are, make them roots
if (i == roots.Length() && loops.Length() > 0)
{
#ifdef OCCT_DEBUG
std::cout << "Error: ShapeFix_ComposeShell::MakeFacesOnPatch: can't dispatch wires"
<< std::endl;
#endif
for (j = 1; j <= loops.Length(); j++)
{
TopoDS_Shape aSh = loops(j);
if (aSh.ShapeType() == TopAbs_WIRE
&& (aSh.Orientation() == TopAbs_FORWARD || aSh.Orientation() == TopAbs_REVERSED))
roots.Append(loops(j));
}
loops.Clear();
}
}
}
//=================================================================================================
void ShapeFix_ComposeShell::DispatchWires(TopTools_SequenceOfShape& faces,
ShapeFix_SequenceOfWireSegment& wires) const
{
BRep_Builder B;
// in closed mode, apply FixShifted to all wires before dispatching them
if (myClosedMode)
{
ShapeFix_Wire sfw;
sfw.SetFace(myFace);
sfw.SetPrecision(Precision());
// pdn: shift pcurves in the seam to make OK shape w/o fixshifted
Standard_Integer i;
for (i = 1; i <= wires.Length(); i++)
{
if (wires(i).IsVertex())
continue;
Handle(ShapeExtend_WireData) sbwd = wires(i).WireData();
for (Standard_Integer jL = 1; jL <= sbwd->NbEdges(); jL++)
{
TopoDS_Edge E = sbwd->Edge(jL);
if (E.Orientation() == TopAbs_REVERSED && BRep_Tool::IsClosed(E, myFace))
{
Standard_Real f1, l1, f2, l2;
Handle(Geom2d_Curve) c21 = BRep_Tool::CurveOnSurface(E, myFace, f1, l1);
TopoDS_Shape dummy = E.Reversed();
Handle(Geom2d_Curve) c22 = BRep_Tool::CurveOnSurface(TopoDS::Edge(dummy), myFace, f2, l2);
constexpr Standard_Real dPreci = ::Precision::PConfusion() * Precision::PConfusion();
gp_Pnt2d pf1 = c21->Value(f1);
gp_Pnt2d pl1 = c21->Value(l1);
gp_Pnt2d pf2 = c22->Value(f2);
gp_Pnt2d pl2 = c22->Value(l2);
if (c21 == c22 || pf1.SquareDistance(pf2) < dPreci || pl1.SquareDistance(pl2) < dPreci)
{
gp_Vec2d shift(0., 0.);
if (myUClosed && Abs(pf2.X() - pl2.X()) < ::Precision::PConfusion())
shift.SetX(myUPeriod);
if (myVClosed && Abs(pf2.Y() - pl2.Y()) < ::Precision::PConfusion())
shift.SetY(myVPeriod);
c22->Translate(shift);
}
}
}
}
for (i = 1; i <= wires.Length(); i++)
{
if (wires(i).IsVertex())
continue;
Handle(ShapeExtend_WireData) sbwd = wires(i).WireData();
//: abv 30.08.01: torHalf2.sat: if wire contains single degenerated
// edge, skip that wire
if (sbwd->NbEdges() <= 0 || (sbwd->NbEdges() == 1 && BRep_Tool::Degenerated(sbwd->Edge(1))))
{
wires.Remove(i--);
continue;
}
sfw.Load(sbwd);
sfw.FixShifted();
// force recomputation of degenerated edges (clear pcurves)
ShapeBuild_Edge sbe;
for (Standard_Integer jL = 1; jL <= sbwd->NbEdges(); jL++)
{
if (BRep_Tool::Degenerated(sbwd->Edge(jL)))
sbe.RemovePCurve(sbwd->Edge(jL), myFace);
// sfw.FixDegenerated(jL);
}
sfw.FixDegenerated();
}
}
// Compute center points for wires
TColgp_SequenceOfPnt2d mPnts;
Standard_Integer nb = wires.Length();
// pdn protection on empty sequence
if (nb == 0)
return;
Standard_Integer i; // svv #1
for (i = 1; i <= nb; i++)
mPnts.Append(GetMiddlePoint(wires(i), myFace));
// Put each wire on its own surface patch (by reassigning pcurves)
// and build 3d curve if necessary
ShapeBuild_ReShape rs;
ShapeBuild_Edge sbe;
ShapeAnalysis_Edge sae;
Handle(ShapeFix_Edge) sfe = new ShapeFix_Edge;
Standard_Real U1, U2, V1, V2;
myGrid->Bounds(U1, U2, V1, V2);
for (i = 1; i <= nb; i++)
{
gp_Pnt2d pnt = mPnts(i);
Standard_Real ush = 0., vsh = 0.;
if (myUClosed)
{
ush = ShapeAnalysis::AdjustToPeriod(pnt.X(), U1, U2);
pnt.SetX(pnt.X() + ush);
}
if (myVClosed)
{
vsh = ShapeAnalysis::AdjustToPeriod(pnt.Y(), V1, V2);
pnt.SetY(pnt.Y() + vsh);
}
mPnts(i) = pnt;
Standard_Integer indU = myGrid->LocateUParameter(pnt.X());
Standard_Integer indV = myGrid->LocateVParameter(pnt.Y());
// compute parametric transformation
gp_Trsf2d T;
Standard_Real uFact = 1.;
Standard_Boolean needT = myGrid->GlobalToLocalTransformation(indU, indV, uFact, T);
if (ush != 0. || vsh != 0.)
{
gp_Trsf2d Sh;
Sh.SetTranslation(gp_Vec2d(ush, vsh));
T.Multiply(Sh);
needT = Standard_True;
}
if (wires(i).IsVertex())
continue;
Handle(Geom_Surface) surf = myGrid->Patch(indU, indV);
TopoDS_Face face;
B.MakeFace(face, surf, myLoc, ::Precision::Confusion());
Handle(ShapeExtend_WireData) sewd = wires(i).WireData();
for (Standard_Integer j = 1; j <= sewd->NbEdges(); j++)
{
// Standard_Integer nsplit = ApplyContext ( sewd, j, context );
// if ( nsplit <1 ) { j--; continue; }
TopoDS_Edge edge = sewd->Edge(j);
// !! Accurately copy pcurves for SEAMS and SEAM-like edges !!
// if edge is already copied, don`t copy any more
TopoDS_Edge newEdge;
TopoDS_Edge anInitEdge = edge;
Standard_Boolean ismanifold =
(edge.Orientation() == TopAbs_FORWARD || edge.Orientation() == TopAbs_REVERSED);
if (rs.IsRecorded(edge))
{
// smh#8
TopoDS_Shape tmpNE = rs.Value(edge);
newEdge = TopoDS::Edge(tmpNE);
}
else
{
if (!ismanifold)
anInitEdge.Orientation(TopAbs_FORWARD);
newEdge = sbe.Copy(anInitEdge, Standard_False);
if (!ismanifold)
newEdge.Orientation(edge.Orientation());
rs.Replace(edge, newEdge);
Context()->Replace(edge, newEdge);
}
sbe.ReassignPCurve(newEdge, myFace, face);
// transform pcurve to parametric space of patch
if (needT)
{
Standard_Real f, l;
Handle(Geom2d_Curve) c2d;
if (sae.PCurve(newEdge, face, c2d, f, l, Standard_False))
{
Standard_Real newf = f, newl = l;
Handle(Geom2d_Curve) c2dnew = sbe.TransformPCurve(c2d, T, uFact, newf, newl);
if (BRep_Tool::IsClosed(newEdge, face))
{
Standard_Real cf, cl;
Handle(Geom2d_Curve) c2d2;
// smh#8
TopoDS_Shape tmpE = newEdge.Reversed();
TopoDS_Edge e2 = TopoDS::Edge(tmpE);
if (sae.PCurve(e2, face, c2d2, cf, cl, Standard_False))
{
if (newEdge.Orientation() == TopAbs_FORWARD)
B.UpdateEdge(newEdge, c2dnew, c2d2, face, 0.);
else
B.UpdateEdge(newEdge, c2d2, c2dnew, face, 0.);
}
else
B.UpdateEdge(newEdge, c2dnew, face, 0.);
}
else
B.UpdateEdge(newEdge, c2dnew, face, 0.);
B.Range(newEdge, face, newf, newl);
if ((newf != f || newl != l) && !BRep_Tool::Degenerated(newEdge))
B.SameRange(newEdge, Standard_False);
}
}
if (!BRep_Tool::SameRange(newEdge))
{
TopoDS_Edge etmp;
if (!ismanifold)
{
TopoDS_Edge afe = TopoDS::Edge(newEdge.Oriented(TopAbs_FORWARD));
etmp = sbe.Copy(afe, Standard_False);
}
else
etmp = sbe.Copy(newEdge, Standard_False);
sfe->FixAddCurve3d(etmp);
Standard_Real cf, cl;
Handle(Geom_Curve) c3d;
if (sae.Curve3d(etmp, c3d, cf, cl, Standard_False))
{
B.UpdateEdge(newEdge, c3d, 0.);
sbe.SetRange3d(newEdge, cf, cl);
}
}
else
sfe->FixAddCurve3d(newEdge);
sewd->Set(newEdge, j);
}
}
// Collect wires in packets lying on same surface and dispatch them
TColStd_Array1OfBoolean used(1, nb);
used.Init(Standard_False);
for (;;)
{
TopTools_SequenceOfShape loops;
Handle(Geom_Surface) Surf;
for (i = 1; i <= nb; i++)
{
if (used(i))
continue;
Handle(Geom_Surface) S = myGrid->Patch(mPnts(i));
if (Surf.IsNull())
Surf = S;
else if (S != Surf)
continue;
used(i) = Standard_True;
ShapeFix_WireSegment aSeg = wires(i);
if (aSeg.IsVertex())
{
TopoDS_Vertex aVert = aSeg.GetVertex();
if (aVert.Orientation() == TopAbs_INTERNAL)
loops.Append(wires(i).GetVertex());
}
else
{
const Handle(ShapeExtend_WireData)& aWD = aSeg.WireData();
if (!aWD.IsNull())
loops.Append(aWD->Wire());
}
}
if (Surf.IsNull())
break;
MakeFacesOnPatch(faces, Surf, loops);
}
}
//=================================================================================================
void ShapeFix_ComposeShell::SetTransferParamTool(
const Handle(ShapeAnalysis_TransferParameters)& TransferParam)
{
myTransferParamTool = TransferParam;
}
//=================================================================================================
Handle(ShapeAnalysis_TransferParameters) ShapeFix_ComposeShell::GetTransferParamTool() const
{
return myTransferParamTool;
}
//=================================================================================================
Standard_Boolean& ShapeFix_ComposeShell::ClosedMode()
{
return myClosedMode;
}