FreeCAD/src/Mod/Part/App/modelRefine.cpp

1286 lines
47 KiB
C++

/***************************************************************************
* Copyright (c) 2011 Thomas Anderson <blobfish[at]gmx.com> *
* *
* This file is part of the FreeCAD CAx development system. *
* *
* This library is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Library General Public *
* License as published by the Free Software Foundation; either *
* version 2 of the License, or (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Library General Public License for more details. *
* *
* You should have received a copy of the GNU Library General Public *
* License along with this library; see the file COPYING.LIB. If not, *
* write to the Free Software Foundation, Inc., 59 Temple Place, *
* Suite 330, Boston, MA 02111-1307, USA *
* *
***************************************************************************/
#include "PreCompiled.h"
#include <Base/Tools.h>
#include <algorithm>
#include <iterator>
#include <Geom_Surface.hxx>
#include <Geom_RectangularTrimmedSurface.hxx>
#include <GeomAdaptor_Surface.hxx>
#include <Geom_Plane.hxx>
#include <Geom_CylindricalSurface.hxx>
#include <gp_Ax3.hxx>
#include <Geom_BSplineSurface.hxx>
#include <gp_Pln.hxx>
#include <gp_Cylinder.hxx>
#include <TColgp_Array2OfPnt.hxx>
#include <TColStd_Array1OfReal.hxx>
#include <TopoDS_Shape.hxx>
#include <TopoDS_Compound.hxx>
#include <TopoDS.hxx>
#include <TopExp.hxx>
#include <TopExp_Explorer.hxx>
#include <BRep_Tool.hxx>
#include <BRepLib_MakeWire.hxx>
#include <BRepLib_FuseEdges.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRepBuilderAPI_MakeSolid.hxx>
#include <BRepBuilderAPI_Sewing.hxx>
#include <Geom_Conic.hxx>
#include <ShapeBuild_ReShape.hxx>
#include <ShapeFix_Face.hxx>
#include <TopTools_ListOfShape.hxx>
#include <TopTools_ListIteratorOfListOfShape.hxx>
#include <TopTools_DataMapIteratorOfDataMapOfShapeShape.hxx>
#include <TopTools_DataMapIteratorOfDataMapOfIntegerListOfShape.hxx>
#include <BRep_Builder.hxx>
#include <Bnd_Box.hxx>
#include <BRepBndLib.hxx>
#include <ShapeAnalysis_Edge.hxx>
#include <ShapeAnalysis_Curve.hxx>
#include <BRepAdaptor_Curve.hxx>
#include <TColgp_SequenceOfPnt.hxx>
#include <GeomAPI_ProjectPointOnSurf.hxx>
#include <Base/Console.h>
#include "modelRefine.h"
using namespace ModelRefine;
void ModelRefine::getFaceEdges(const TopoDS_Face &face, EdgeVectorType &edges)
{
TopExp_Explorer it;
for (it.Init(face, TopAbs_EDGE); it.More(); it.Next())
edges.push_back(TopoDS::Edge(it.Current()));
}
void ModelRefine::boundaryEdges(const FaceVectorType &faces, EdgeVectorType &edgesOut)
{
//this finds all the boundary edges. Maybe more than one boundary.
std::list<TopoDS_Edge> edges;
FaceVectorType::const_iterator faceIt;
for (faceIt = faces.begin(); faceIt != faces.end(); ++faceIt)
{
EdgeVectorType faceEdges;
EdgeVectorType::iterator faceEdgesIt;
getFaceEdges(*faceIt, faceEdges);
for (faceEdgesIt = faceEdges.begin(); faceEdgesIt != faceEdges.end(); ++faceEdgesIt)
{
bool foundSignal(false);
std::list<TopoDS_Edge>::iterator edgesIt;
for (edgesIt = edges.begin(); edgesIt != edges.end(); ++edgesIt)
{
if ((*edgesIt).IsSame(*faceEdgesIt))
{
edgesIt = edges.erase(edgesIt);
foundSignal = true;
break;
}
}
if (!foundSignal)
edges.push_back(*faceEdgesIt);
}
}
edgesOut.reserve(edges.size());
std::copy(edges.begin(), edges.end(), back_inserter(edgesOut));
}
TopoDS_Shell ModelRefine::removeFaces(const TopoDS_Shell &shell, const FaceVectorType &faces)
{
ShapeBuild_ReShape rebuilder;
FaceVectorType::const_iterator it;
for (it = faces.begin(); it != faces.end(); ++it)
rebuilder.Remove(*it);
return TopoDS::Shell(rebuilder.Apply(shell));
}
namespace ModelRefine
{
class WireSort
{
public:
bool operator() (const TopoDS_Wire& wire1, const TopoDS_Wire& wire2)
{
Bnd_Box box1, box2;
BRepBndLib::Add(wire1, box1);
BRepBndLib::Add(wire2, box2);
// Note: This works because we can be sure that the wires do not intersect
return box2.SquareExtent() < box1.SquareExtent();
}
};
}
////////////////////////////////////////////////////////////////////////////////////////////
void FaceTypeSplitter::addShell(const TopoDS_Shell &shellIn)
{
shell = shellIn;
}
void FaceTypeSplitter::registerType(const GeomAbs_SurfaceType &type)
{
typeMap.insert(SplitMapType::value_type(type, FaceVectorType()));
}
bool FaceTypeSplitter::hasType(const GeomAbs_SurfaceType &type) const
{
return typeMap.find(type) != typeMap.end();
}
void FaceTypeSplitter::split()
{
TopExp_Explorer shellIt;
for (shellIt.Init(shell, TopAbs_FACE); shellIt.More(); shellIt.Next())
{
TopoDS_Face tempFace(TopoDS::Face(shellIt.Current()));
GeomAbs_SurfaceType currentType = FaceTypedBase::getFaceType(tempFace);
SplitMapType::iterator mapIt = typeMap.find(currentType);
if (mapIt == typeMap.end())
continue;
(*mapIt).second.push_back(tempFace);
}
}
const FaceVectorType& FaceTypeSplitter::getTypedFaceVector(const GeomAbs_SurfaceType &type) const
{
if (this->hasType(type))
return (*(typeMap.find(type))).second;
//error here.
static FaceVectorType error;
return error;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
FaceAdjacencySplitter::FaceAdjacencySplitter(const TopoDS_Shell &shell)
{
TopExp_Explorer shellIt;
for (shellIt.Init(shell, TopAbs_FACE); shellIt.More(); shellIt.Next())
{
TopTools_ListOfShape shapeList;
TopExp_Explorer it;
for (it.Init(shellIt.Current(), TopAbs_EDGE); it.More(); it.Next())
shapeList.Append(it.Current());
faceToEdgeMap.Add(shellIt.Current(), shapeList);
}
TopExp::MapShapesAndAncestors(shell, TopAbs_EDGE, TopAbs_FACE, edgeToFaceMap);
}
void FaceAdjacencySplitter::split(const FaceVectorType &facesIn)
{
facesInMap.Clear();
processedMap.Clear();
adjacencyArray.clear();
FaceVectorType::const_iterator it;
for (it = facesIn.begin(); it != facesIn.end(); ++it)
facesInMap.Add(*it);
//the reserve call guarantees the vector will never get "pushed back" in the
//recursiveFind calls, thus invalidating the iterators. We can be sure of this as any one
//matched set can't be bigger than the set passed in. if we have seg faults, we will
//want to turn this tempFaces vector back into a std::list ensuring valid iterators
//at the expense of std::find speed.
FaceVectorType tempFaces;
tempFaces.reserve(facesIn.size() + 1);
for (it = facesIn.begin(); it != facesIn.end(); ++it)
{
//skip already processed shapes.
if (processedMap.Contains(*it))
continue;
tempFaces.clear();
processedMap.Add(*it);
recursiveFind(*it, tempFaces);
if (tempFaces.size() > 1)
{
adjacencyArray.push_back(tempFaces);
}
}
}
void FaceAdjacencySplitter::recursiveFind(const TopoDS_Face &face, FaceVectorType &outVector)
{
outVector.push_back(face);
const TopTools_ListOfShape &edges = faceToEdgeMap.FindFromKey(face);
TopTools_ListIteratorOfListOfShape edgeIt;
for (edgeIt.Initialize(edges); edgeIt.More(); edgeIt.Next())
{
const TopTools_ListOfShape &faces = edgeToFaceMap.FindFromKey(edgeIt.Value());
TopTools_ListIteratorOfListOfShape faceIt;
for (faceIt.Initialize(faces); faceIt.More(); faceIt.Next())
{
if (!facesInMap.Contains(faceIt.Value()))
continue;
if (processedMap.Contains(faceIt.Value()))
continue;
processedMap.Add(faceIt.Value());
recursiveFind(TopoDS::Face(faceIt.Value()), outVector);
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
void FaceEqualitySplitter::split(const FaceVectorType &faces, FaceTypedBase *object)
{
std::vector<FaceVectorType> tempVector;
tempVector.reserve(faces.size());
FaceVectorType::const_iterator faceIt;
for (faceIt = faces.begin(); faceIt != faces.end(); ++faceIt)
{
bool foundMatch(false);
std::vector<FaceVectorType>::iterator tempIt;
for (tempIt = tempVector.begin(); tempIt != tempVector.end(); ++tempIt)
{
if (object->isEqual((*tempIt).front(), *faceIt))
{
(*tempIt).push_back(*faceIt);
foundMatch = true;
break;
}
}
if (!foundMatch)
{
FaceVectorType another;
another.reserve(faces.size());
another.push_back(*faceIt);
tempVector.push_back(another);
}
}
std::vector<FaceVectorType>::iterator it;
for (it = tempVector.begin(); it != tempVector.end(); ++it)
{
if ((*it).size() < 2)
continue;
equalityVector.push_back(*it);
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
GeomAbs_SurfaceType FaceTypedBase::getFaceType(const TopoDS_Face &faceIn)
{
Handle(Geom_Surface) surface = BRep_Tool::Surface(faceIn);
GeomAdaptor_Surface surfaceTest(surface);
return surfaceTest.GetType();
}
void FaceTypedBase::boundarySplit(const FaceVectorType &facesIn, std::vector<EdgeVectorType> &boundariesOut) const
{
EdgeVectorType bEdges;
boundaryEdges(facesIn, bEdges);
std::list<TopoDS_Edge> edges;
std::copy(bEdges.begin(), bEdges.end(), back_inserter(edges));
while(!edges.empty())
{
TopoDS_Vertex destination = TopExp::FirstVertex(edges.front(), Standard_True);
TopoDS_Vertex lastVertex = TopExp::LastVertex(edges.front(), Standard_True);
EdgeVectorType boundary;
boundary.push_back(edges.front());
edges.pop_front();
//single edge closed check.
if (destination.IsSame(lastVertex))
{
boundariesOut.push_back(boundary);
continue;
}
bool closedSignal(false);
std::list<TopoDS_Edge>::iterator it;
for (it = edges.begin(); it != edges.end();)
{
TopoDS_Vertex currentVertex = TopExp::FirstVertex(*it, Standard_True);
if (lastVertex.IsSame(currentVertex))
{
boundary.push_back(*it);
lastVertex = TopExp::LastVertex(*it, Standard_True);
edges.erase(it);
it = edges.begin();
if (lastVertex.IsSame(destination))
{
closedSignal = true;
break;
}
continue;
}
++it;
}
if (closedSignal)
boundariesOut.push_back(boundary);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
FaceTypedPlane::FaceTypedPlane() : FaceTypedBase(GeomAbs_Plane)
{
}
static Handle(Geom_Plane) getGeomPlane(const TopoDS_Face &faceIn)
{
Handle_Geom_Plane planeSurfaceOut;
Handle_Geom_Surface surface = BRep_Tool::Surface(faceIn);
if (!surface.IsNull())
{
planeSurfaceOut = Handle(Geom_Plane)::DownCast(surface);
if (planeSurfaceOut.IsNull())
{
Handle_Geom_RectangularTrimmedSurface trimmedSurface = Handle(Geom_RectangularTrimmedSurface)::DownCast(surface);
if (!trimmedSurface.IsNull())
planeSurfaceOut = Handle(Geom_Plane)::DownCast(trimmedSurface->BasisSurface());
}
}
return planeSurfaceOut;
}
bool FaceTypedPlane::isEqual(const TopoDS_Face &faceOne, const TopoDS_Face &faceTwo) const
{
Handle(Geom_Plane) planeSurfaceOne = getGeomPlane(faceOne);
Handle(Geom_Plane) planeSurfaceTwo = getGeomPlane(faceTwo);
if (planeSurfaceOne.IsNull() || planeSurfaceTwo.IsNull())
return false;//error?
gp_Pln planeOne(planeSurfaceOne->Pln());
gp_Pln planeTwo(planeSurfaceTwo->Pln());
return (planeOne.Position().Direction().IsParallel(planeTwo.Position().Direction(), Precision::Confusion()) &&
planeOne.Distance(planeTwo.Position().Location()) < Precision::Confusion());
}
GeomAbs_SurfaceType FaceTypedPlane::getType() const
{
return GeomAbs_Plane;
}
TopoDS_Face FaceTypedPlane::buildFace(const FaceVectorType &faces) const
{
std::vector<TopoDS_Wire> wires;
std::vector<EdgeVectorType> splitEdges;
this->boundarySplit(faces, splitEdges);
if (splitEdges.empty())
return TopoDS_Face();
std::vector<EdgeVectorType>::iterator splitIt;
for (splitIt = splitEdges.begin(); splitIt != splitEdges.end(); ++splitIt)
{
BRepLib_MakeWire wireMaker;
EdgeVectorType::iterator it;
for (it = (*splitIt).begin(); it != (*splitIt).end(); ++it)
wireMaker.Add(*it);
TopoDS_Wire currentWire = wireMaker.Wire();
wires.push_back(currentWire);
}
std::sort(wires.begin(), wires.end(), ModelRefine::WireSort());
BRepLib_MakeFace faceMaker(wires.at(0), Standard_True);
if (faceMaker.Error() != BRepLib_FaceDone)
return TopoDS_Face();
TopoDS_Face current = faceMaker.Face();
if (wires.size() > 1)
{
ShapeFix_Face faceFix(current);
faceFix.SetContext(new ShapeBuild_ReShape());
for (size_t index(1); index<wires.size(); ++index)
faceFix.Add(wires.at(index));
faceFix.Perform();
if (faceFix.Status(ShapeExtend_FAIL))
return TopoDS_Face();
faceFix.FixOrientation();
faceFix.Perform();
if(faceFix.Status(ShapeExtend_FAIL))
return TopoDS_Face();
current = faceFix.Face();
}
return current;
}
FaceTypedPlane& ModelRefine::getPlaneObject()
{
static FaceTypedPlane object;
return object;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
FaceTypedCylinder::FaceTypedCylinder() : FaceTypedBase(GeomAbs_Cylinder)
{
}
static Handle(Geom_CylindricalSurface) getGeomCylinder(const TopoDS_Face &faceIn)
{
Handle_Geom_CylindricalSurface cylinderSurfaceOut;
Handle_Geom_Surface surface = BRep_Tool::Surface(faceIn);
if (!surface.IsNull())
{
cylinderSurfaceOut = Handle(Geom_CylindricalSurface)::DownCast(surface);
if (cylinderSurfaceOut.IsNull())
{
Handle_Geom_RectangularTrimmedSurface trimmedSurface = Handle(Geom_RectangularTrimmedSurface)::DownCast(surface);
if (!trimmedSurface.IsNull())
cylinderSurfaceOut = Handle(Geom_CylindricalSurface)::DownCast(trimmedSurface->BasisSurface());
}
}
return cylinderSurfaceOut;
}
bool FaceTypedCylinder::isEqual(const TopoDS_Face &faceOne, const TopoDS_Face &faceTwo) const
{
Handle(Geom_CylindricalSurface) surfaceOne = getGeomCylinder(faceOne);
Handle(Geom_CylindricalSurface) surfaceTwo = getGeomCylinder(faceTwo);
if (surfaceOne.IsNull() || surfaceTwo.IsNull())
return false;//probably need an error
gp_Cylinder cylinderOne = surfaceOne->Cylinder();
gp_Cylinder cylinderTwo = surfaceTwo->Cylinder();
if (fabs(cylinderOne.Radius() - cylinderTwo.Radius()) > Precision::Confusion())
return false;
if (!cylinderOne.Axis().IsCoaxial(cylinderTwo.Axis(), Precision::Angular(), Precision::Confusion()) &&
!cylinderOne.Axis().IsCoaxial(cylinderTwo.Axis().Reversed(), Precision::Angular(), Precision::Confusion()))
return false;
return true;
}
GeomAbs_SurfaceType FaceTypedCylinder::getType() const
{
return GeomAbs_Cylinder;
}
// Auxiliary method
const TopoDS_Face fixFace(const TopoDS_Face& f) {
static TopoDS_Face dummy;
// Fix the face. Orientation doesn't seem to get fixed the first call.
ShapeFix_Face faceFixer(f);
faceFixer.SetContext(new ShapeBuild_ReShape());
faceFixer.Perform();
if (faceFixer.Status(ShapeExtend_FAIL))
return dummy;
faceFixer.FixMissingSeam();
faceFixer.Perform();
if (faceFixer.Status(ShapeExtend_FAIL))
return dummy;
faceFixer.FixOrientation();
faceFixer.Perform();
if (faceFixer.Status(ShapeExtend_FAIL))
return dummy;
return faceFixer.Face();
}
// Detect whether a wire encircles the cylinder axis or not. This is done by calculating the
// wire length, oriented with respect to the cylinder axis
bool wireEncirclesAxis(const TopoDS_Wire& wire, const Handle(Geom_CylindricalSurface)& cylinder)
{
double radius = cylinder->Radius();
gp_Ax1 cylAxis = cylinder->Axis();
gp_Vec cv(cylAxis.Location().X(), cylAxis.Location().Y(), cylAxis.Location().Z()); // center of cylinder
gp_Vec av(cylAxis.Direction().X(), cylAxis.Direction().Y(), cylAxis.Direction().Z()); // axis of cylinder
Handle_Geom_Plane plane = new Geom_Plane(gp_Ax3(cylAxis.Location(), cylAxis.Direction()));
double totalArc = 0.0;
bool firstSegment = false;
bool secondSegment = false;
gp_Pnt first, last;
for (TopExp_Explorer ex(wire, TopAbs_EDGE); ex.More(); ex.Next()) {
TopoDS_Edge segment(TopoDS::Edge(ex.Current()));
BRepAdaptor_Curve adapt(segment);
// Get curve data
double fp = adapt.FirstParameter();
double lp = adapt.LastParameter();
gp_Pnt segFirst, segLast;
gp_Vec segTangent;
adapt.D1(fp, segFirst, segTangent);
segLast = adapt.Value(lp);
double length = 0.0;
if (adapt.GetType() == GeomAbs_Line) {
// Any line on the cylinder must be parallel to the cylinder axis
length = 0.0;
} else if (adapt.GetType() == GeomAbs_Circle) {
// Arc segment
length = (lp - fp) * radius;
// Check orientation in relation to cylinder axis
GeomAPI_ProjectPointOnSurf proj(segFirst, plane);
gp_Vec bv = gp_Vec(proj.Point(1).X(), proj.Point(1).Y(), proj.Point(1).Z());
if ((bv - cv).Crossed(segTangent).IsOpposite(av, Precision::Confusion()))
length = -length;
} else {
// Linearize the edge. Idea taken from ShapeAnalysis.cxx ShapeAnalysis::TotCross2D()
TColgp_SequenceOfPnt SeqPnt;
ShapeAnalysis_Curve::GetSamplePoints(adapt.Curve().Curve(), fp, lp, SeqPnt);
// Calculate the oriented length of the edge
gp_Pnt begin;
for (Standard_Integer j=1; j <= SeqPnt.Length(); j++) {
gp_Pnt end = SeqPnt.Value(j);
// Project end point onto the plane
GeomAPI_ProjectPointOnSurf proj(end, plane);
if (!proj.IsDone())
return false; // FIXME: What else can we do?
gp_Pnt pend = proj.Point(1);
if (j > 1) {
// Get distance between the points, equal to (linearised) arc length
gp_Vec bv = gp_Vec(begin.X(), begin.Y(), begin.Z());
gp_Vec dv = gp_Vec(pend.X(), pend.Y(), pend.Z()) - bv;
double dist = dv.Magnitude();
if (dist > 0) {
// Check orientation of this piece in relation to cylinder axis
if ((bv - cv).Crossed(dv).IsOpposite(av, Precision::Confusion()))
dist = -dist;
length += dist;
}
}
begin = pend;
}
}
if (!firstSegment) {
// First wire segment. Save the start and end point of the segment
firstSegment = true;
first = segFirst;
last = segLast;
} else if (!secondSegment) {
// Second wire segment. Determine whether the second segment continues from
// the first or from the last point of the first segment
secondSegment = true;
if (last.IsEqual(segFirst, Precision::Confusion())) {
last = segLast; // Third segment must begin here
} else if (last.IsEqual(segLast, Precision::Confusion())) {
last = segFirst;
length = -length;
} else if (first.IsEqual(segLast, Precision::Confusion())) {
last = segFirst;
totalArc = -totalArc;
length = -length;
} else { // first.IsEqual(segFirst)
last = segLast;
totalArc = -totalArc;
}
} else {
if (!last.IsEqual(segFirst, Precision::Confusion())) {
// The length was calculated in the opposite direction of the wire traversal
length = -length;
last = segFirst;
} else {
last = segLast;
}
}
totalArc += length;
}
// For an exact calculation, only two results would be possible:
// totalArc = 0.0: The wire does not encircle the axis
// totalArc = 2 * M_PI * radius: The wire encircles the axis
return (fabs(totalArc) > M_PI * radius);
}
TopoDS_Face FaceTypedCylinder::buildFace(const FaceVectorType &faces) const
{
static TopoDS_Face dummy;
std::vector<EdgeVectorType> boundaries;
boundarySplit(faces, boundaries);
if (boundaries.size() < 1)
return dummy;
//make wires
std::vector<TopoDS_Wire> allWires;
std::vector<EdgeVectorType>::iterator boundaryIt;
for (boundaryIt = boundaries.begin(); boundaryIt != boundaries.end(); ++boundaryIt)
{
BRepLib_MakeWire wireMaker;
EdgeVectorType::iterator it;
for (it = (*boundaryIt).begin(); it != (*boundaryIt).end(); ++it)
wireMaker.Add(*it);
if (wireMaker.Error() != BRepLib_WireDone)
return dummy;
allWires.push_back(wireMaker.Wire());
}
if (allWires.size() < 1)
return dummy;
// Sort wires by size, that is, the innermost wire comes last
std::sort(allWires.begin(), allWires.end(), ModelRefine::WireSort());
// Find outer boundary wires that cut the cylinder into segments. This will be the case f we
// have removed the seam edges of a complete (360 degrees) cylindrical face
Handle(Geom_CylindricalSurface) surface = getGeomCylinder(faces.at(0));
if (surface.IsNull())
return dummy;
std::vector<TopoDS_Wire> innerWires, encirclingWires;
std::vector<TopoDS_Wire>::iterator wireIt;
for (wireIt = allWires.begin(); wireIt != allWires.end(); ++wireIt) {
if (wireEncirclesAxis(*wireIt, surface))
encirclingWires.push_back(*wireIt);
else
innerWires.push_back(*wireIt);
}
if (encirclingWires.empty()) {
// We can use the result of the bounding box sort. First wire is the outer wire
wireIt = allWires.begin();
BRepBuilderAPI_MakeFace faceMaker(surface, *wireIt);
if (!faceMaker.IsDone())
return dummy;
// Add additional boundaries (inner wires).
for (wireIt++; wireIt != allWires.end(); ++wireIt)
{
faceMaker.Add(*wireIt);
if (!faceMaker.IsDone())
return dummy;
}
return fixFace(faceMaker.Face());
} else {
if (encirclingWires.size() != 2)
return dummy;
if (innerWires.empty()) {
// We have just two outer boundaries
BRepBuilderAPI_MakeFace faceMaker(surface, encirclingWires.front());
if (!faceMaker.IsDone())
return dummy;
faceMaker.Add(encirclingWires.back());
if (!faceMaker.IsDone())
return dummy;
return fixFace(faceMaker.Face());
} else {
// Add the inner wires first, because otherwise those that cut the seam edge will fail
wireIt = innerWires.begin();
BRepBuilderAPI_MakeFace faceMaker(surface, *wireIt, false);
if (!faceMaker.IsDone())
return dummy;
// Add additional boundaries (inner wires).
for (wireIt++; wireIt != innerWires.end(); ++wireIt)
{
faceMaker.Add(*wireIt);
if (!faceMaker.IsDone())
return dummy;
}
// Add outer boundaries
faceMaker.Add(encirclingWires.front());
if (!faceMaker.IsDone())
return dummy;
faceMaker.Add(encirclingWires.back());
if (!faceMaker.IsDone())
return dummy;
return fixFace(faceMaker.Face());
}
}
}
void FaceTypedCylinder::boundarySplit(const FaceVectorType &facesIn, std::vector<EdgeVectorType> &boundariesOut) const
{
//normal edges.
EdgeVectorType normalEdges;
ModelRefine::boundaryEdges(facesIn, normalEdges);
std::list<TopoDS_Edge> sortedEdges;
std::copy(normalEdges.begin(), normalEdges.end(), back_inserter(sortedEdges));
while (!sortedEdges.empty())
{
TopoDS_Vertex destination = TopExp::FirstVertex(sortedEdges.back(), Standard_True);
TopoDS_Vertex lastVertex = TopExp::LastVertex(sortedEdges.back(), Standard_True);
bool closedSignal(false);
std::list<TopoDS_Edge> boundary;
boundary.push_back(sortedEdges.back());
sortedEdges.pop_back();
if (destination.IsSame(lastVertex)) {
// Single circular edge
closedSignal = true;
} else {
std::list<TopoDS_Edge>::iterator sortedIt;
for (sortedIt = sortedEdges.begin(); sortedIt != sortedEdges.end();)
{
TopoDS_Vertex currentVertex = TopExp::FirstVertex(*sortedIt, Standard_True);
//Seam edges lie on top of each other. i.e. same. and we remove every match from the list
//so we don't actually ever compare the same edge.
if ((*sortedIt).IsSame(boundary.back()))
{
++sortedIt;
continue;
}
if (lastVertex.IsSame(currentVertex))
{
boundary.push_back(*sortedIt);
lastVertex = TopExp::LastVertex(*sortedIt, Standard_True);
if (lastVertex.IsSame(destination))
{
closedSignal = true;
sortedEdges.erase(sortedIt);
break;
}
sortedEdges.erase(sortedIt);
sortedIt = sortedEdges.begin();
continue;
}
++sortedIt;
}
}
if (closedSignal)
{
EdgeVectorType temp;
std::copy(boundary.begin(), boundary.end(), std::back_inserter(temp));
boundariesOut.push_back(temp);
}
}
}
FaceTypedCylinder& ModelRefine::getCylinderObject()
{
static FaceTypedCylinder object;
return object;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// TODO: change this version after occ fix. Freecad Mantis 1450
#if OCC_VERSION_HEX <= 0x7fffff
void collectConicEdges(const TopoDS_Shell &shell, TopTools_IndexedMapOfShape &map)
{
TopTools_IndexedMapOfShape edges;
TopExp::MapShapes(shell, TopAbs_EDGE, edges);
for (int index = 1; index <= edges.Extent(); ++index)
{
const TopoDS_Edge &currentEdge = TopoDS::Edge(edges.FindKey(index));
if (currentEdge.IsNull())
continue;
TopLoc_Location location;
Standard_Real first, last;
const Handle_Geom_Curve &curve = BRep_Tool::Curve(currentEdge, location, first, last);
if (curve.IsNull())
continue;
if (curve->IsKind(STANDARD_TYPE(Geom_Conic)))
map.Add(currentEdge);
}
}
#endif
FaceTypedBSpline::FaceTypedBSpline() : FaceTypedBase(GeomAbs_BSplineSurface)
{
}
bool FaceTypedBSpline::isEqual(const TopoDS_Face &faceOne, const TopoDS_Face &faceTwo) const
{
try
{
Handle(Geom_BSplineSurface) surfaceOne = Handle(Geom_BSplineSurface)::DownCast(BRep_Tool::Surface(faceOne));
Handle(Geom_BSplineSurface) surfaceTwo = Handle(Geom_BSplineSurface)::DownCast(BRep_Tool::Surface(faceTwo));
if (surfaceOne.IsNull() || surfaceTwo.IsNull())
return false;
if (surfaceOne->IsURational() != surfaceTwo->IsURational()) return false;
if (surfaceOne->IsVRational() != surfaceTwo->IsVRational()) return false;
if (surfaceOne->IsUPeriodic() != surfaceTwo->IsUPeriodic()) return false;
if (surfaceOne->IsVPeriodic() != surfaceTwo->IsVPeriodic()) return false;
if (surfaceOne->IsUClosed() != surfaceTwo->IsUClosed()) return false;
if (surfaceOne->IsVClosed() != surfaceTwo->IsVClosed()) return false;
if (surfaceOne->UDegree() != surfaceTwo->UDegree()) return false;
if (surfaceOne->VDegree() != surfaceTwo->VDegree()) return false;
//pole test
int uPoleCountOne(surfaceOne->NbUPoles());
int vPoleCountOne(surfaceOne->NbVPoles());
int uPoleCountTwo(surfaceTwo->NbUPoles());
int vPoleCountTwo(surfaceTwo->NbVPoles());
if (uPoleCountOne != uPoleCountTwo || vPoleCountOne != vPoleCountTwo)
return false;
TColgp_Array2OfPnt polesOne(1, uPoleCountOne, 1, vPoleCountOne);
TColgp_Array2OfPnt polesTwo(1, uPoleCountTwo, 1, vPoleCountTwo);
surfaceOne->Poles(polesOne);
surfaceTwo->Poles(polesTwo);
for (int indexU = 1; indexU <= uPoleCountOne; ++indexU)
{
for (int indexV = 1; indexV <= vPoleCountOne; ++indexV)
{
if (!(polesOne.Value(indexU, indexV).IsEqual(polesTwo.Value(indexU, indexV), Precision::Confusion())))
return false;
}
}
//knot test
int uKnotCountOne(surfaceOne->NbUKnots());
int vKnotCountOne(surfaceOne->NbVKnots());
int uKnotCountTwo(surfaceTwo->NbUKnots());
int vKnotCountTwo(surfaceTwo->NbVKnots());
if (uKnotCountOne != uKnotCountTwo || vKnotCountOne != vKnotCountTwo)
return false;
TColStd_Array1OfReal uKnotsOne(1, uKnotCountOne);
TColStd_Array1OfReal vKnotsOne(1, vKnotCountOne);
TColStd_Array1OfReal uKnotsTwo(1, uKnotCountTwo);
TColStd_Array1OfReal vKnotsTwo(1, vKnotCountTwo);
surfaceOne->UKnots(uKnotsOne);
surfaceOne->VKnots(vKnotsOne);
surfaceTwo->UKnots(uKnotsTwo);
surfaceTwo->VKnots(vKnotsTwo);
for (int indexU = 1; indexU <= uKnotCountOne; ++indexU)
if (uKnotsOne.Value(indexU) != uKnotsTwo.Value(indexU))
return false;
for (int indexV = 1; indexV <= vKnotCountOne; ++indexV)
if (vKnotsOne.Value(indexV) != vKnotsTwo.Value(indexV))
return false;
//knot sequence.
int uKnotSequenceOneCount(uPoleCountOne + surfaceOne->UDegree() + 1);
int vKnotSequenceOneCount(vPoleCountOne + surfaceOne->VDegree() + 1);
int uKnotSequenceTwoCount(uPoleCountTwo + surfaceTwo->UDegree() + 1);
int vKnotSequenceTwoCount(vPoleCountTwo + surfaceTwo->VDegree() + 1);
if (uKnotSequenceOneCount != uKnotSequenceTwoCount || vKnotSequenceOneCount != vKnotSequenceTwoCount)
return false;
TColStd_Array1OfReal uKnotSequenceOne(1, uKnotSequenceOneCount);
TColStd_Array1OfReal vKnotSequenceOne(1, vKnotSequenceOneCount);
TColStd_Array1OfReal uKnotSequenceTwo(1, uKnotSequenceTwoCount);
TColStd_Array1OfReal vKnotSequenceTwo(1, vKnotSequenceTwoCount);
surfaceOne->UKnotSequence(uKnotSequenceOne);
surfaceOne->VKnotSequence(vKnotSequenceOne);
surfaceTwo->UKnotSequence(uKnotSequenceTwo);
surfaceTwo->VKnotSequence(vKnotSequenceTwo);
for (int indexU = 1; indexU <= uKnotSequenceOneCount; ++indexU)
if (uKnotSequenceOne.Value(indexU) != uKnotSequenceTwo.Value(indexU))
return false;
for (int indexV = 1; indexV <= vKnotSequenceOneCount; ++indexV)
if (vKnotSequenceOne.Value(indexV) != vKnotSequenceTwo.Value(indexV))
return false;
return true;
}
catch (Standard_Failure)
{
Handle(Standard_Failure) e = Standard_Failure::Caught();
std::ostringstream stream;
stream << "FaceTypedBSpline::isEqual: OCC Error: " << e->GetMessageString() << std::endl;
Base::Console().Message(stream.str().c_str());
}
catch (...)
{
std::ostringstream stream;
stream << "FaceTypedBSpline::isEqual: Unknown Error" << std::endl;
Base::Console().Message(stream.str().c_str());
}
return false;
}
GeomAbs_SurfaceType FaceTypedBSpline::getType() const
{
return GeomAbs_BSplineSurface;
}
TopoDS_Face FaceTypedBSpline::buildFace(const FaceVectorType &faces) const
{
std::vector<TopoDS_Wire> wires;
std::vector<EdgeVectorType> splitEdges;
this->boundarySplit(faces, splitEdges);
if (splitEdges.empty())
return TopoDS_Face();
std::vector<EdgeVectorType>::iterator splitIt;
for (splitIt = splitEdges.begin(); splitIt != splitEdges.end(); ++splitIt)
{
BRepLib_MakeWire wireMaker;
EdgeVectorType::iterator it;
for (it = (*splitIt).begin(); it != (*splitIt).end(); ++it)
wireMaker.Add(*it);
TopoDS_Wire currentWire = wireMaker.Wire();
wires.push_back(currentWire);
}
std::sort(wires.begin(), wires.end(), ModelRefine::WireSort());
//make face from surface and outer wire.
Handle(Geom_BSplineSurface) surface = Handle(Geom_BSplineSurface)::DownCast(BRep_Tool::Surface(faces.at(0)));
if (!surface)
return TopoDS_Face();
std::vector<TopoDS_Wire>::iterator wireIt;
wireIt = wires.begin();
BRepBuilderAPI_MakeFace faceMaker(surface, *wireIt);
if (!faceMaker.IsDone())
return TopoDS_Face();
//add additional boundaries.
for (wireIt++; wireIt != wires.end(); ++wireIt)
{
faceMaker.Add(*wireIt);
if (!faceMaker.IsDone())
return TopoDS_Face();
}
//fix newly constructed face. Orientation doesn't seem to get fixed the first call.
ShapeFix_Face faceFixer(faceMaker.Face());
faceFixer.SetContext(new ShapeBuild_ReShape());
faceFixer.Perform();
if (faceFixer.Status(ShapeExtend_FAIL))
return TopoDS_Face();
faceFixer.FixOrientation();
faceFixer.Perform();
if (faceFixer.Status(ShapeExtend_FAIL))
return TopoDS_Face();
return faceFixer.Face();
}
FaceTypedBSpline& ModelRefine::getBSplineObject()
{
static FaceTypedBSpline object;
return object;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
FaceUniter::FaceUniter(const TopoDS_Shell &shellIn) : modifiedSignal(false)
{
workShell = shellIn;
}
bool FaceUniter::process()
{
if (workShell.IsNull())
return false;
modifiedShapes.clear();
deletedShapes.clear();
typeObjects.push_back(&getPlaneObject());
typeObjects.push_back(&getCylinderObject());
typeObjects.push_back(&getBSplineObject());
//add more face types.
ModelRefine::FaceTypeSplitter splitter;
splitter.addShell(workShell);
std::vector<FaceTypedBase *>::iterator typeIt;
for(typeIt = typeObjects.begin(); typeIt != typeObjects.end(); ++typeIt)
splitter.registerType((*typeIt)->getType());
splitter.split();
ModelRefine::FaceVectorType facesToRemove;
ModelRefine::FaceVectorType facesToSew;
ModelRefine::FaceAdjacencySplitter adjacencySplitter(workShell);
for(typeIt = typeObjects.begin(); typeIt != typeObjects.end(); ++typeIt)
{
ModelRefine::FaceVectorType typedFaces = splitter.getTypedFaceVector((*typeIt)->getType());
ModelRefine::FaceEqualitySplitter equalitySplitter;
equalitySplitter.split(typedFaces, *typeIt);
for (std::size_t indexEquality(0); indexEquality < equalitySplitter.getGroupCount(); ++indexEquality)
{
adjacencySplitter.split(equalitySplitter.getGroup(indexEquality));
// std::cout << " adjacency group count: " << adjacencySplitter.getGroupCount() << std::endl;
for (std::size_t adjacentIndex(0); adjacentIndex < adjacencySplitter.getGroupCount(); ++adjacentIndex)
{
// std::cout << " face count is: " << adjacencySplitter.getGroup(adjacentIndex).size() << std::endl;
TopoDS_Face newFace = (*typeIt)->buildFace(adjacencySplitter.getGroup(adjacentIndex));
if (!newFace.IsNull())
{
facesToSew.push_back(newFace);
if (facesToRemove.capacity() <= facesToRemove.size() + adjacencySplitter.getGroup(adjacentIndex).size())
facesToRemove.reserve(facesToRemove.size() + adjacencySplitter.getGroup(adjacentIndex).size());
FaceVectorType temp = adjacencySplitter.getGroup(adjacentIndex);
facesToRemove.insert(facesToRemove.end(), temp.begin(), temp.end());
// the first shape will be marked as modified, i.e. replaced by newFace, all others are marked as deleted
// jrheinlaender: IMHO this is not correct because references to the deleted faces will be broken, whereas they should
// be replaced by references to the new face. To achieve this all shapes should be marked as
// modified, producing one single new face. This is the inverse behaviour to faces that are split e.g.
// by a boolean cut, where one old shape is marked as modified, producing multiple new shapes
if (!temp.empty())
{
for (FaceVectorType::iterator f = temp.begin(); f != temp.end(); ++f)
modifiedShapes.push_back(std::make_pair(*f, newFace));
}
}
}
}
}
if (facesToSew.size() > 0)
{
modifiedSignal = true;
workShell = ModelRefine::removeFaces(workShell, facesToRemove);
TopExp_Explorer xp;
bool emptyShell = true;
for (xp.Init(workShell, TopAbs_FACE); xp.More(); xp.Next())
{
emptyShell = false;
break;
}
if (!emptyShell || facesToSew.size() > 1)
{
BRepBuilderAPI_Sewing sew;
sew.Add(workShell);
FaceVectorType::iterator sewIt;
for(sewIt = facesToSew.begin(); sewIt != facesToSew.end(); ++sewIt)
sew.Add(*sewIt);
sew.Perform();
try {
workShell = TopoDS::Shell(sew.SewedShape());
} catch (Standard_Failure) {
return false;
}
// update the list of modifications
for (std::vector<ShapePairType>::iterator it = modifiedShapes.begin(); it != modifiedShapes.end(); ++it)
{
if (sew.IsModified(it->second))
{
it->second = sew.Modified(it->second);
break;
}
}
}
else
{
// workShell has no more faces and we add exactly one face
BRep_Builder builder;
builder.MakeShell(workShell);
FaceVectorType::iterator sewIt;
for(sewIt = facesToSew.begin(); sewIt != facesToSew.end(); ++sewIt)
builder.Add(workShell, *sewIt);
}
BRepLib_FuseEdges edgeFuse(workShell);
// TODO: change this version after occ fix. Freecad Mantis 1450
#if OCC_VERSION_HEX <= 0x7fffff
TopTools_IndexedMapOfShape map;
collectConicEdges(workShell, map);
edgeFuse.AvoidEdges(map);
#endif
TopTools_DataMapOfShapeShape affectedFaces;
edgeFuse.Faces(affectedFaces);
TopTools_DataMapIteratorOfDataMapOfShapeShape mapIt;
for (mapIt.Initialize(affectedFaces); mapIt.More(); mapIt.Next())
{
ShapeFix_Face faceFixer(TopoDS::Face(mapIt.Value()));
faceFixer.Perform();
}
workShell = TopoDS::Shell(edgeFuse.Shape());
// update the list of modifications
TopTools_DataMapOfShapeShape faceMap;
edgeFuse.Faces(faceMap);
for (mapIt.Initialize(faceMap); mapIt.More(); mapIt.Next())
{
bool isModifiedFace = false;
for (std::vector<ShapePairType>::iterator it = modifiedShapes.begin(); it != modifiedShapes.end(); ++it)
{
if (mapIt.Key().IsSame(it->second)) {
// Note: IsEqual() for some reason does not work
it->second = mapIt.Value();
isModifiedFace = true;
}
}
if (!isModifiedFace)
{
// Catch faces that were not united but whose boundary was changed (probably because
// several adjacent faces were united)
// See https://sourceforge.net/apps/mantisbt/free-cad/view.php?id=873
modifiedShapes.push_back(std::make_pair(mapIt.Key(), mapIt.Value()));
}
}
// Handle edges that were fused. See https://sourceforge.net/apps/mantisbt/free-cad/view.php?id=873
TopTools_DataMapOfIntegerListOfShape oldEdges;
TopTools_DataMapOfIntegerShape newEdges;
edgeFuse.Edges(oldEdges);
edgeFuse.ResultEdges(newEdges);
TopTools_DataMapIteratorOfDataMapOfIntegerListOfShape edgeMapIt;
for (edgeMapIt.Initialize(oldEdges); edgeMapIt.More(); edgeMapIt.Next())
{
const TopTools_ListOfShape& edges = edgeMapIt.Value();
int idx = edgeMapIt.Key();
TopTools_ListIteratorOfListOfShape edgeIt;
for (edgeIt.Initialize(edges); edgeIt.More(); edgeIt.Next())
{
if (newEdges.IsBound(idx))
modifiedShapes.push_back(std::make_pair(edgeIt.Value(), newEdges(idx)));
}
// TODO: Handle vertices that have disappeared in the fusion of the edges
}
}
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//BRepBuilderAPI_RefineModel implement a way to log all modifications on the faces
Part::BRepBuilderAPI_RefineModel::BRepBuilderAPI_RefineModel(const TopoDS_Shape& shape)
{
myShape = shape;
Build();
}
void Part::BRepBuilderAPI_RefineModel::Build()
{
if (myShape.IsNull())
Standard_Failure::Raise("Cannot remove splitter from empty shape");
if (myShape.ShapeType() == TopAbs_SOLID) {
const TopoDS_Solid &solid = TopoDS::Solid(myShape);
BRepBuilderAPI_MakeSolid mkSolid;
TopExp_Explorer it;
for (it.Init(solid, TopAbs_SHELL); it.More(); it.Next()) {
const TopoDS_Shell &currentShell = TopoDS::Shell(it.Current());
ModelRefine::FaceUniter uniter(currentShell);
if (uniter.process()) {
if (uniter.isModified()) {
const TopoDS_Shell &newShell = uniter.getShell();
mkSolid.Add(newShell);
LogModifications(uniter);
}
else {
mkSolid.Add(currentShell);
}
}
else {
Standard_Failure::Raise("Removing splitter failed");
}
}
myShape = mkSolid.Solid();
}
else if (myShape.ShapeType() == TopAbs_SHELL) {
const TopoDS_Shell& shell = TopoDS::Shell(myShape);
ModelRefine::FaceUniter uniter(shell);
if (uniter.process()) {
// TODO: Why not check for uniter.isModified()?
myShape = uniter.getShell();
LogModifications(uniter);
}
else {
Standard_Failure::Raise("Removing splitter failed");
}
}
else if (myShape.ShapeType() == TopAbs_COMPOUND) {
BRep_Builder builder;
TopoDS_Compound comp;
builder.MakeCompound(comp);
TopExp_Explorer xp;
// solids
for (xp.Init(myShape, TopAbs_SOLID); xp.More(); xp.Next()) {
const TopoDS_Solid &solid = TopoDS::Solid(xp.Current());
BRepTools_ReShape reshape;
TopExp_Explorer it;
for (it.Init(solid, TopAbs_SHELL); it.More(); it.Next()) {
const TopoDS_Shell &currentShell = TopoDS::Shell(it.Current());
ModelRefine::FaceUniter uniter(currentShell);
if (uniter.process()) {
if (uniter.isModified()) {
const TopoDS_Shell &newShell = uniter.getShell();
reshape.Replace(currentShell, newShell);
LogModifications(uniter);
}
}
}
builder.Add(comp, reshape.Apply(solid));
}
// free shells
for (xp.Init(myShape, TopAbs_SHELL, TopAbs_SOLID); xp.More(); xp.Next()) {
const TopoDS_Shell& shell = TopoDS::Shell(xp.Current());
ModelRefine::FaceUniter uniter(shell);
if (uniter.process()) {
builder.Add(comp, uniter.getShell());
LogModifications(uniter);
}
}
// the rest
for (xp.Init(myShape, TopAbs_FACE, TopAbs_SHELL); xp.More(); xp.Next()) {
if (!xp.Current().IsNull())
builder.Add(comp, xp.Current());
}
for (xp.Init(myShape, TopAbs_WIRE, TopAbs_FACE); xp.More(); xp.Next()) {
if (!xp.Current().IsNull())
builder.Add(comp, xp.Current());
}
for (xp.Init(myShape, TopAbs_EDGE, TopAbs_WIRE); xp.More(); xp.Next()) {
if (!xp.Current().IsNull())
builder.Add(comp, xp.Current());
}
for (xp.Init(myShape, TopAbs_VERTEX, TopAbs_EDGE); xp.More(); xp.Next()) {
if (!xp.Current().IsNull())
builder.Add(comp, xp.Current());
}
myShape = comp;
}
Done();
}
void Part::BRepBuilderAPI_RefineModel::LogModifications(const ModelRefine::FaceUniter& uniter)
{
const std::vector<ShapePairType>& modShapes = uniter.getModifiedShapes();
for (std::vector<ShapePairType>::const_iterator it = modShapes.begin(); it != modShapes.end(); ++it) {
TopTools_ListOfShape list;
list.Append(it->second);
myModified.Bind(it->first, list);
}
const ShapeVectorType& delShapes = uniter.getDeletedShapes();
for (ShapeVectorType::const_iterator it = delShapes.begin(); it != delShapes.end(); ++it) {
myDeleted.Append(*it);
}
}
const TopTools_ListOfShape& Part::BRepBuilderAPI_RefineModel::Modified(const TopoDS_Shape& S)
{
if (myModified.IsBound(S))
return myModified.Find(S);
else
return myEmptyList;
}
Standard_Boolean Part::BRepBuilderAPI_RefineModel::IsDeleted(const TopoDS_Shape& S)
{
TopTools_ListIteratorOfListOfShape it;
for (it.Initialize(myDeleted); it.More(); it.Next())
{
if (it.Value().IsSame(S))
return Standard_True;
}
return Standard_False;
}