FreeCAD / src /Mod /Surface /App /FeatureFilling.cpp
AbdulElahGwaith's picture
Upload folder using huggingface_hub
985c397 verified
// SPDX-License-Identifier: LGPL-2.1-or-later
/***************************************************************************
* Copyright (c) 2014 Nathan Miller <Nathan.A.Mill[at]gmail.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 <string>
#include <BRepBuilderAPI_MakeWire.hxx>
#include <BRepFill_Filling.hxx>
#include <BRep_Tool.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Face.hxx>
#include <gp_Pnt.hxx>
#include "FeatureFilling.h"
using namespace Surface;
PROPERTY_SOURCE(Surface::Filling, Part::Spline)
// Initial values
Filling::Filling()
{
// clang-format off
ADD_PROPERTY_TYPE(BoundaryEdges,(nullptr,""), "Filling", App::Prop_None,
"Boundary Edges (C0 is required for edges without a corresponding face)");
ADD_PROPERTY_TYPE(BoundaryFaces,(""), "Filling", App::Prop_None, "Boundary Faces");
ADD_PROPERTY_TYPE(BoundaryOrder,(-1), "Filling", App::Prop_None,
"Order of constraint on boundary faces (C0, G1 and G2 are possible)");
ADD_PROPERTY_TYPE(UnboundEdges,(nullptr,""), "Filling", App::Prop_None,
"Unbound constraint edges (C0 is required for edges without a corresponding face)");
ADD_PROPERTY_TYPE(UnboundFaces,(""), "Filling", App::Prop_None,
"Unbound constraint faces");
ADD_PROPERTY_TYPE(UnboundOrder,(-1), "Filling", App::Prop_None,
"Order of constraint on curve faces (C0, G1 and G2 are possible)");
ADD_PROPERTY_TYPE(FreeFaces,(nullptr,""), "Filling", App::Prop_None, "Free constraint on a face");
ADD_PROPERTY_TYPE(FreeOrder,(0), "Filling", App::Prop_None, "Order of constraint on free faces");
ADD_PROPERTY_TYPE(Points,(nullptr,""), "Filling", App::Prop_None, "Constraint Points (on Surface)");
ADD_PROPERTY_TYPE(InitialFace,(nullptr), "Filling", App::Prop_None, "Initial surface to use");
ADD_PROPERTY_TYPE(Degree,(3), "Filling", App::Prop_None, "Starting degree");
ADD_PROPERTY_TYPE(PointsOnCurve,(15), "Filling", App::Prop_None,
"Number of points on an edge for constraint");
ADD_PROPERTY_TYPE(Iterations,(2), "Filling", App::Prop_None, "Number of iterations");
ADD_PROPERTY_TYPE(Anisotropy,(false), "Filling", App::Prop_None, "Anisotropy");
ADD_PROPERTY_TYPE(Tolerance2d,(0.00001), "Filling", App::Prop_None, "2D Tolerance");
ADD_PROPERTY_TYPE(Tolerance3d,(0.0001), "Filling", App::Prop_None, "3D Tolerance");
ADD_PROPERTY_TYPE(TolAngular,(0.01), "Filling", App::Prop_None, "G1 tolerance");
ADD_PROPERTY_TYPE(TolCurvature,(0.1), "Filling", App::Prop_None, "G2 tolerance");
ADD_PROPERTY_TYPE(MaximumDegree,(8), "Filling", App::Prop_None, "Maximum curve degree");
ADD_PROPERTY_TYPE(MaximumSegments,(9), "Filling", App::Prop_None, "Maximum number of segments");
// clang-format on
BoundaryEdges.setScope(App::LinkScope::Global);
UnboundEdges.setScope(App::LinkScope::Global);
FreeFaces.setScope(App::LinkScope::Global);
Points.setScope(App::LinkScope::Global);
InitialFace.setScope(App::LinkScope::Global);
BoundaryEdges.setSize(0);
BoundaryFaces.setSize(0);
BoundaryOrder.setSize(0);
UnboundEdges.setSize(0);
UnboundFaces.setSize(0);
UnboundOrder.setSize(0);
FreeFaces.setSize(0);
FreeOrder.setSize(0);
Points.setSize(0);
}
short Filling::mustExecute() const
{
if (BoundaryEdges.isTouched() || BoundaryFaces.isTouched() || BoundaryOrder.isTouched()
|| UnboundEdges.isTouched() || UnboundFaces.isTouched() || UnboundOrder.isTouched()
|| FreeFaces.isTouched() || FreeOrder.isTouched() || Points.isTouched()
|| InitialFace.isTouched() || Degree.isTouched() || PointsOnCurve.isTouched()
|| Iterations.isTouched() || Anisotropy.isTouched() || Tolerance2d.isTouched()
|| Tolerance3d.isTouched() || TolAngular.isTouched() || TolCurvature.isTouched()
|| MaximumDegree.isTouched() || MaximumSegments.isTouched()) {
return 1;
}
return 0;
}
void Filling::addConstraints(
BRepFill_Filling& builder,
const App::PropertyLinkSubList& edges,
const App::PropertyStringList& faces,
const App::PropertyIntegerList& orders,
Standard_Boolean bnd
)
{
auto edge_obj = edges.getValues();
auto edge_sub = edges.getSubValues();
auto face_sub = faces.getValues();
auto contvals = orders.getValues();
// if the number of continuities doesn't match then fall back to C0
if (edge_sub.size() != contvals.size()) {
contvals.resize(edge_sub.size());
std::fill(contvals.begin(), contvals.end(), static_cast<long>(GeomAbs_C0));
}
// if the number of faces doesn't match then fall back to empty strings
// an empty face string indicates that there is no face associated to an edge
if (face_sub.size() != edge_sub.size()) {
face_sub.resize(edge_obj.size());
std::fill(face_sub.begin(), face_sub.end(), std::string());
}
if (edge_obj.size() == edge_sub.size()) {
// BRepFill_Filling crashes if the boundary edges are not added in a consecutive order.
// these edges are first added to a test wire to check that they can be securely added to
// the Filling algo
BRepBuilderAPI_MakeWire testWire;
for (std::size_t index = 0; index < edge_obj.size(); index++) {
// get the part object
App::DocumentObject* obj = edge_obj[index];
const std::string& sub = edge_sub[index];
if (obj && obj->isDerivedFrom<Part::Feature>()) {
// get the sub-edge of the part's shape
const Part::TopoShape& shape = static_cast<Part::Feature*>(obj)->Shape.getShape();
TopoDS_Shape edge = shape.getSubShape(sub.c_str());
if (!edge.IsNull() && edge.ShapeType() == TopAbs_EDGE) {
GeomAbs_Shape cont = static_cast<GeomAbs_Shape>(contvals[index]);
// check for an adjacent face of the edge
std::string subFace = face_sub[index];
// edge doesn't have set an adjacent face
if (subFace.empty()) {
if (!bnd) {
// not a boundary edge: safe to add it directly
builder.Add(TopoDS::Edge(edge), cont, bnd);
}
else {
// boundary edge: try to add it to the test wire first
testWire.Add(TopoDS::Edge(edge));
if (testWire.IsDone()) {
builder.Add(TopoDS::Edge(edge), cont, bnd);
}
else {
Standard_Failure::Raise(
"Boundary edges must be added in a consecutive order"
);
}
}
}
else {
TopoDS_Shape face = shape.getSubShape(subFace.c_str());
if (!face.IsNull() && face.ShapeType() == TopAbs_FACE) {
if (!bnd) {
// not a boundary edge: safe to add it directly
builder.Add(TopoDS::Edge(edge), TopoDS::Face(face), cont, bnd);
}
else {
// boundary edge: try to add it to the test wire first
testWire.Add(TopoDS::Edge(edge));
if (testWire.IsDone()) {
builder.Add(TopoDS::Edge(edge), TopoDS::Face(face), cont, bnd);
}
else {
Standard_Failure::Raise(
"Boundary edges must be added in a consecutive order"
);
}
}
}
else {
Standard_Failure::Raise("Sub-shape is not a face");
}
}
}
else {
Standard_Failure::Raise("Sub-shape is not an edge");
}
}
}
}
else {
Standard_Failure::Raise("Number of links does not match with the number of orders");
}
}
// Add free support faces with their continuities
void Filling::addConstraints(
BRepFill_Filling& builder,
const App::PropertyLinkSubList& faces,
const App::PropertyIntegerList& orders
)
{
auto face_obj = faces.getValues();
auto face_sub = faces.getSubValues();
auto contvals = orders.getValues();
if (face_obj.size() == face_sub.size() && face_obj.size() == contvals.size()) {
for (std::size_t index = 0; index < face_obj.size(); index++) {
App::DocumentObject* obj = face_obj[index];
const std::string& sub = face_sub[index];
if (obj && obj->isDerivedFrom<Part::Feature>()) {
const Part::TopoShape& shape = static_cast<Part::Feature*>(obj)->Shape.getShape();
TopoDS_Shape face = shape.getSubShape(sub.c_str());
if (!face.IsNull() && face.ShapeType() == TopAbs_FACE) {
GeomAbs_Shape cont = static_cast<GeomAbs_Shape>(contvals[index]);
builder.Add(TopoDS::Face(face), cont);
}
else {
Standard_Failure::Raise("Sub-shape is not a face");
}
}
}
}
else {
Standard_Failure::Raise("Number of links does not match with the number of orders");
}
}
void Filling::addConstraints(BRepFill_Filling& builder, const App::PropertyLinkSubList& pointsList)
{
auto points = pointsList.getSubListValues();
for (const auto& it : points) {
App::DocumentObject* obj = it.first;
std::vector<std::string> sub = it.second;
if (obj && obj->isDerivedFrom<Part::Feature>()) {
const Part::TopoShape& shape = static_cast<Part::Feature*>(obj)->Shape.getShape();
for (const auto& jt : sub) {
TopoDS_Shape subShape = shape.getSubShape(jt.c_str());
if (!subShape.IsNull() && subShape.ShapeType() == TopAbs_VERTEX) {
gp_Pnt pnt = BRep_Tool::Pnt(TopoDS::Vertex(subShape));
builder.Add(pnt);
}
}
}
}
}
App::DocumentObjectExecReturn* Filling::execute()
{
// Assign Variables
unsigned int degree = Degree.getValue();
unsigned int ptsoncurve = PointsOnCurve.getValue();
unsigned int numIter = Iterations.getValue();
bool anisotropy = Anisotropy.getValue();
double tol2d = Tolerance2d.getValue();
double tol3d = Tolerance3d.getValue();
double tolG1 = TolAngular.getValue();
double tolG2 = TolCurvature.getValue();
unsigned int maxdeg = MaximumDegree.getValue();
unsigned int maxseg = MaximumSegments.getValue();
try {
BRepFill_Filling
builder(degree, ptsoncurve, numIter, anisotropy, tol2d, tol3d, tolG1, tolG2, maxdeg, maxseg);
if ((BoundaryEdges.getSize()) < 1) {
return new App::DocumentObjectExecReturn("Border must have at least one curve defined.");
}
// Load the initial surface if set
App::DocumentObject* initFace = InitialFace.getValue();
if (initFace && initFace->isDerivedFrom<Part::Feature>()) {
const Part::TopoShape& shape = static_cast<Part::Feature*>(initFace)->Shape.getShape();
std::vector<std::string> subNames = InitialFace.getSubValues();
for (const auto& it : subNames) {
TopoDS_Shape subShape = shape.getSubShape(it.c_str());
if (!subShape.IsNull() && subShape.ShapeType() == TopAbs_FACE) {
builder.LoadInitSurface(TopoDS::Face(subShape));
break;
}
}
}
// Add the constraints of border curves/faces (bound)
int numBoundaries = BoundaryEdges.getSize();
addConstraints(builder, BoundaryEdges, BoundaryFaces, BoundaryOrder, Standard_True);
// Add additional edge constraints if available (unbound)
if (UnboundEdges.getSize() > 0) {
addConstraints(builder, UnboundEdges, UnboundFaces, UnboundOrder, Standard_False);
}
// Add additional constraint on free faces
if (FreeFaces.getSize() > 0) {
addConstraints(builder, FreeFaces, FreeOrder);
}
// App point constraints
if (Points.getSize() > 0) {
addConstraints(builder, Points);
}
// Build the face
if (numBoundaries > 1) {
builder.Build();
}
if (!builder.IsDone()) {
Standard_Failure::Raise("Failed to create a face from constraints");
}
// Return the face
TopoDS_Face aFace = builder.Face();
this->Shape.setValue(aFace);
return App::DocumentObject::StdReturn;
}
catch (Standard_Failure& e) {
return new App::DocumentObjectExecReturn(e.GetMessageString());
}
}