FreeCAD / src /Mod /Surface /App /FeatureGeomFillSurface.cpp
AbdulElahGwaith's picture
Upload folder using huggingface_hub
985c397 verified
// SPDX-License-Identifier: LGPL-2.1-or-later
/***************************************************************************
* Copyright (c) 2014-2015 Nathan Miller <Nathan.A.Mill[at]gmail.com> *
* Balázs Bámer *
* *
* 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 <BRepBuilderAPI_Copy.hxx>
#include <BRepBuilderAPI_MakeFace.hxx>
#include <BRep_Tool.hxx>
#include <GeomConvert.hxx>
#include <GeomFill.hxx>
#include <GeomFill_BSplineCurves.hxx>
#include <GeomFill_BezierCurves.hxx>
#include <Geom_BSplineCurve.hxx>
#include <Geom_BSplineSurface.hxx>
#include <Geom_BezierCurve.hxx>
#include <Geom_BezierSurface.hxx>
#include <Geom_TrimmedCurve.hxx>
#include <ShapeConstruct_Curve.hxx>
#include <ShapeExtend_WireData.hxx>
#include <ShapeFix_Wire.hxx>
#include <Standard_ConstructionError.hxx>
#include <StdFail_NotDone.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <TopoDS_Wire.hxx>
#include <gp_Trsf.hxx>
#include "FeatureGeomFillSurface.h"
using namespace Surface;
ShapeValidator::ShapeValidator()
{
initValidator();
}
void ShapeValidator::initValidator()
{
willBezier = true;
edgeCount = 0;
}
// shows error message if the shape is not an edge
void ShapeValidator::checkEdge(const TopoDS_Shape& shape)
{
if (shape.IsNull() || shape.ShapeType() != TopAbs_EDGE) {
Standard_Failure::Raise("Shape is not an edge.\n");
}
TopoDS_Edge etmp = TopoDS::Edge(shape); // Curve TopoDS_Edge
TopLoc_Location heloc; // this will be output
Standard_Real u0; // contains output
Standard_Real u1; // contains output
Handle(Geom_Curve) c_geom = BRep_Tool::Curve(etmp, heloc, u0, u1); // The geometric curve
Handle(Geom_BezierCurve) bez_geom = Handle(Geom_BezierCurve)::DownCast(c_geom); // Try to get
// Bezier curve
// if not a Bezier then try to create a B-spline surface from the edges
if (bez_geom.IsNull()) {
willBezier = false;
}
edgeCount++;
}
void ShapeValidator::checkAndAdd(const TopoDS_Shape& shape, Handle(ShapeExtend_WireData) * aWD)
{
checkEdge(shape);
if (aWD) {
BRepBuilderAPI_Copy copier(shape);
// make a copy of the shape and the underlying geometry to avoid to affect the input shapes
(*aWD)->Add(TopoDS::Edge(copier.Shape()));
}
}
void ShapeValidator::checkAndAdd(
const Part::TopoShape& ts,
const char* subName,
Handle(ShapeExtend_WireData) * aWD
)
{
try {
if (subName && *subName != '\0') {
// we want only the subshape which is linked
checkAndAdd(ts.getSubShape(subName), aWD);
}
else if (!ts.getShape().IsNull() && ts.getShape().ShapeType() == TopAbs_WIRE) {
TopoDS_Wire wire = TopoDS::Wire(ts.getShape());
for (TopExp_Explorer xp(wire, TopAbs_EDGE); xp.More(); xp.Next()) {
checkAndAdd(xp.Current(), aWD);
}
}
else {
checkAndAdd(ts.getShape(), aWD);
}
}
catch (Standard_Failure&) { // any OCC exception means an inappropriate shape in the selection
Standard_Failure::Raise("Wrong shape type.\n");
}
}
PROPERTY_SOURCE(Surface::GeomFillSurface, Part::Spline)
const char* GeomFillSurface::FillTypeEnums[] = {"Stretched", "Coons", "Curved", nullptr};
GeomFillSurface::GeomFillSurface()
: Spline()
{
ADD_PROPERTY(FillType, ((long)0));
ADD_PROPERTY(BoundaryList, (nullptr, "Dummy"));
ADD_PROPERTY(ReversedList, (false));
FillType.setEnums(FillTypeEnums);
BoundaryList.setScope(App::LinkScope::Global);
}
// Check if any components of the surface have been modified
short GeomFillSurface::mustExecute() const
{
if (BoundaryList.isTouched() || ReversedList.isTouched() || FillType.isTouched()) {
return 1;
}
return Spline::mustExecute();
}
void GeomFillSurface::onChanged(const App::Property* prop)
{
if (isRestoring()) {
if (prop == &BoundaryList) {
// auto-adjusting size of this list
if (BoundaryList.getSize() != ReversedList.getSize()) {
ReversedList.setSize(BoundaryList.getSize());
}
}
}
Part::Spline::onChanged(prop);
}
App::DocumentObjectExecReturn* GeomFillSurface::execute()
{
try {
TopoDS_Wire aWire;
// Gets the healed wire
if (getWire(aWire)) {
createBezierSurface(aWire);
}
else {
createBSplineSurface(aWire);
}
return App::DocumentObject::StdReturn;
}
catch (Standard_ConstructionError&) {
// message is in a Latin language, show a normal one
return new App::DocumentObjectExecReturn("Curves are disjoint.");
}
catch (StdFail_NotDone&) {
return new App::DocumentObjectExecReturn(
"A curve was not a B-spline and could not be converted into one."
);
}
catch (Standard_Failure& e) {
return new App::DocumentObjectExecReturn(e.GetMessageString());
}
}
GeomFill_FillingStyle GeomFillSurface::getFillingStyle()
{
// Identify filling style
switch (FillType.getValue()) {
case GeomFill_StretchStyle:
case GeomFill_CoonsStyle:
case GeomFill_CurvedStyle:
return static_cast<GeomFill_FillingStyle>(FillType.getValue());
default:
Standard_Failure::Raise("Filling style must be 0 (Stretch), 1 (Coons), or 2 (Curved).\n");
return GeomFill_StretchStyle; // this is to shut up the compiler
}
}
bool GeomFillSurface::getWire(TopoDS_Wire& aWire)
{
Handle(ShapeFix_Wire) aShFW = new ShapeFix_Wire;
Handle(ShapeExtend_WireData) aWD = new ShapeExtend_WireData;
std::vector<App::PropertyLinkSubList::SubSet> boundary = BoundaryList.getSubListValues();
if (boundary.size() > 4) { // if too many not even try
Standard_Failure::Raise("Only 2-4 curves are allowed\n");
}
ShapeValidator validator;
for (const auto& set : boundary) {
if (set.first->isDerivedFrom<Part::Feature>()) {
for (const auto& jt : set.second) {
const Part::TopoShape& ts = static_cast<Part::Feature*>(set.first)->Shape.getShape();
validator.checkAndAdd(ts, jt.c_str(), &aWD);
}
}
else {
Standard_Failure::Raise("Curve not from Part::Feature\n");
}
}
if (validator.numEdges() < 2 || validator.numEdges() > 4) {
Standard_Failure::Raise("Only 2-4 curves are allowed\n");
}
// Reorder the curves and fix the wire if required
aShFW->Load(aWD); // Load in the wire
aShFW->FixReorder(); // Fix the order of the edges if required
aShFW->ClosedWireMode() = Standard_True; // Enables closed wire mode
aShFW->FixConnected(); // Fix connection between wires
aShFW->FixSelfIntersection(); // Fix Self Intersection
aShFW->Perform(); // Perform the fixes
aWire = aShFW->Wire(); // Healed Wire
if (aWire.IsNull()) {
Standard_Failure::Raise("Wire unable to be constructed\n");
}
return validator.isBezier();
}
void GeomFillSurface::createFace(const Handle(Geom_BoundedSurface) & aSurface)
{
BRepBuilderAPI_MakeFace aFaceBuilder;
Standard_Real u1, u2, v1, v2;
// transfer surface bounds to face
aSurface->Bounds(u1, u2, v1, v2);
aFaceBuilder.Init(aSurface, u1, u2, v1, v2, Precision::Confusion());
TopoDS_Face aFace = aFaceBuilder.Face();
if (!aFaceBuilder.IsDone()) {
Standard_Failure::Raise("Face unable to be constructed\n");
}
if (aFace.IsNull()) {
Standard_Failure::Raise("Resulting Face is null\n");
}
this->Shape.setValue(aFace);
}
void GeomFillSurface::createBezierSurface(TopoDS_Wire& aWire)
{
std::vector<Handle(Geom_BezierCurve)> curves;
curves.reserve(4);
Standard_Real u1, u2; // contains output
TopExp_Explorer anExp(aWire, TopAbs_EDGE);
for (; anExp.More(); anExp.Next()) {
const TopoDS_Edge hedge = TopoDS::Edge(anExp.Current());
TopLoc_Location heloc; // this will be output
Handle(Geom_Curve) c_geom = BRep_Tool::Curve(hedge, heloc, u1, u2); // The geometric curve
Handle(Geom_BezierCurve) bezier = Handle(Geom_BezierCurve)::DownCast(c_geom); // Try to get
// Bezier curve
if (!bezier.IsNull()) {
bezier->Segment(u1, u2); // DownCast(c_geom) will not trim bezier, so DIY
gp_Trsf transf = heloc.Transformation();
bezier->Transform(transf); // apply original transformation to control points
// Store Underlying Geometry
curves.push_back(bezier);
}
else {
Standard_Failure::Raise("Curve not a Bezier Curve");
}
}
GeomFill_FillingStyle fstyle = getFillingStyle();
GeomFill_BezierCurves aSurfBuilder; // Create Surface Builder
std::size_t edgeCount = curves.size();
const boost::dynamic_bitset<>& booleans = ReversedList.getValues();
if (edgeCount == booleans.size()) {
for (std::size_t i = 0; i < edgeCount; i++) {
if (booleans[i]) {
curves[i]->Reverse();
}
}
}
if (edgeCount == 2) {
aSurfBuilder.Init(curves[0], curves[1], fstyle);
}
else if (edgeCount == 3) {
aSurfBuilder.Init(curves[0], curves[1], curves[2], fstyle);
}
else if (edgeCount == 4) {
aSurfBuilder.Init(curves[0], curves[1], curves[2], curves[3], fstyle);
}
createFace(aSurfBuilder.Surface());
}
void GeomFillSurface::createBSplineSurface(TopoDS_Wire& aWire)
{
std::vector<Handle(Geom_BSplineCurve)> curves;
curves.reserve(4);
Standard_Real u1, u2; // contains output
TopExp_Explorer anExp(aWire, TopAbs_EDGE);
for (; anExp.More(); anExp.Next()) {
const TopoDS_Edge& edge = TopoDS::Edge(anExp.Current());
TopLoc_Location heloc; // this will be output
Handle(Geom_Curve) c_geom = BRep_Tool::Curve(edge, heloc, u1, u2); // The geometric curve
Handle(Geom_BSplineCurve) bspline = Handle(Geom_BSplineCurve)::DownCast(
c_geom
); // Try to get BSpline curve
gp_Trsf transf = heloc.Transformation();
if (!bspline.IsNull()) {
bspline->Segment(u1, u2); // DownCast(c_geom) will not trim spline, so DIY
bspline->Transform(transf); // apply original transformation to control points
// Store Underlying Geometry
curves.push_back(bspline);
}
else {
// try to convert it into a B-spline
Handle(Geom_TrimmedCurve) trim = new Geom_TrimmedCurve(c_geom, u1, u2);
// Approximate the curve to non-rational polynomial BSpline
// to avoid C0 continuity in output surface
GeomConvert conv;
Convert_ParameterisationType paratype = Convert_Polynomial;
Handle(Geom_BSplineCurve) bspline2 = conv.CurveToBSplineCurve(trim, paratype);
if (!bspline2.IsNull()) {
bspline2->Transform(transf); // apply original transformation to control points
curves.push_back(bspline2);
}
else {
// GeomConvert failed, try ShapeConstruct_Curve now
ShapeConstruct_Curve scc;
Handle(Geom_BSplineCurve) spline
= scc.ConvertToBSpline(c_geom, u1, u2, Precision::Confusion());
if (spline.IsNull()) {
Standard_Failure::Raise(
"A curve was not a B-spline and could not be converted into one."
);
}
spline->Transform(transf); // apply original transformation to control points
curves.push_back(spline);
}
}
}
GeomFill_FillingStyle fstyle = getFillingStyle();
GeomFill_BSplineCurves aSurfBuilder; // Create Surface Builder
std::size_t edgeCount = curves.size();
const boost::dynamic_bitset<>& booleans = ReversedList.getValues();
if (edgeCount == booleans.size()) {
for (std::size_t i = 0; i < edgeCount; i++) {
if (booleans[i]) {
curves[i]->Reverse();
}
}
}
if (edgeCount == 2) {
aSurfBuilder.Init(curves[0], curves[1], fstyle);
}
else if (edgeCount == 3) {
aSurfBuilder.Init(curves[0], curves[1], curves[2], fstyle);
}
else if (edgeCount == 4) {
aSurfBuilder.Init(curves[0], curves[1], curves[2], curves[3], fstyle);
}
createFace(aSurfBuilder.Surface());
}